am 46cf2623: Merge "Align 64-bit registers on even dalvik registers"

* commit '46cf26234d9d0a3f9b95e79479c3447e9a5c7e14':
  Align 64-bit registers on even dalvik registers
diff --git a/Android.mk b/Android.mk
index 5401832..140509c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,11 +16,14 @@
 
 subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
 		libdex \
+		vm \
 		dexgen \
 		dexlist \
+		dexopt \
 		dexdump \
 		dx \
 		tools \
+		unit-tests \
 	))
 
 include $(subdirs)
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..2969180
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,60 @@
+This directory contains the Dalvik virtual machine and core class library,
+as well as related tools, libraries, and tests.
+
+A note about the licenses and header comments
+---------------------------------------------
+
+Much of the code under this directory originally came from the Apache
+Harmony project, and as such contains the standard Apache header
+comment. Some of the code was written originally for the Android
+project, and as such contains the standard Android header comment.
+Some files contain code from both projects. In these cases, the header
+comment is a combination of the other two, and the portions of the
+code from Harmony are identified as indicated in the comment.
+
+Here is the combined header comment:
+
+/*
+ * Copyright (C) <year> 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.
+ *
+ * ----------
+ *
+ * Portions of the code surrounded by "// BEGIN Harmony code" and
+ * "// END Harmony code" are copyrighted and licensed separately, as
+ * follows:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+Native SH call bridge
+---------------------
+
+Native SH call bridge is written by
+Shin-ichiro KAWASAKI <shinichiro.kawasaki.mg@hitachi.com>
+and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp.
diff --git a/dexopt/Android.mk b/dexopt/Android.mk
new file mode 100644
index 0000000..1dca215
--- /dev/null
+++ b/dexopt/Android.mk
@@ -0,0 +1,59 @@
+# Copyright (C) 2008 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.
+
+#
+# dexopt, the DEX file optimizer.  This is fully integrated with the VM,
+# so it must be linked against the full VM shared library.
+#
+LOCAL_PATH:= $(call my-dir)
+
+local_src_files := \
+		OptMain.cpp
+
+local_c_includes := \
+		dalvik \
+		dalvik/libdex \
+		dalvik/vm
+
+include $(CLEAR_VARS)
+ifeq ($(TARGET_CPU_SMP),true)
+    LOCAL_CFLAGS += -DANDROID_SMP=1
+else
+    LOCAL_CFLAGS += -DANDROID_SMP=0
+endif
+
+LOCAL_SRC_FILES := $(local_src_files)
+LOCAL_C_INCLUDES := $(local_c_includes)
+LOCAL_SHARED_LIBRARIES := libssl libdvm libcrypto libicuuc libicui18n libcutils libexpat liblog libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dexopt
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(WITH_HOST_DALVIK),true)
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(local_src_files)
+    LOCAL_C_INCLUDES := $(local_c_includes)
+    LOCAL_SHARED_LIBRARIES := libssl-host libdvm libcrypto-host libicuuc-host libicui18n-host
+    LOCAL_STATIC_LIBRARIES :=  libcutils libexpat liblog libz
+    LOCAL_LDLIBS += -ldl -lpthread
+    LOCAL_CFLAGS += -DANDROID_SMP=1
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := dexopt
+    include $(BUILD_HOST_EXECUTABLE)
+endif
diff --git a/dexopt/OptMain.cpp b/dexopt/OptMain.cpp
new file mode 100644
index 0000000..f948ff4
--- /dev/null
+++ b/dexopt/OptMain.cpp
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Command-line DEX optimization and verification entry point.
+ *
+ * There are three ways to launch this:
+ * (1) From the VM.  This takes a dozen args, one of which is a file
+ *     descriptor that acts as both input and output.  This allows us to
+ *     remain ignorant of where the DEX data originally came from.
+ * (2) From installd or another native application.  Pass in a file
+ *     descriptor for a zip file, a file descriptor for the output, and
+ *     a filename for debug messages.  Many assumptions are made about
+ *     what's going on (verification + optimization are enabled, boot
+ *     class path is in BOOTCLASSPATH, etc).
+ * (3) On the host during a build for preoptimization. This behaves
+ *     almost the same as (2), except it takes file names instead of
+ *     file descriptors.
+ *
+ * There are some fragile aspects around bootclasspath entries, owing
+ * largely to the VM's history of working on whenever it thought it needed
+ * instead of strictly doing what it was told.  If optimizing bootclasspath
+ * entries, always do them in the order in which they appear in the path.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include "cutils/log.h"
+#include "cutils/process_name.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static const char* kClassesDex = "classes.dex";
+
+
+/*
+ * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
+ * up front for the DEX optimization header.
+ */
+static int extractAndProcessZip(int zipFd, int cacheFd,
+    const char* debugFileName, bool isBootstrap, const char* bootClassPath,
+    const char* dexoptFlagStr)
+{
+    ZipArchiveHandle zippy;
+    ZipEntry zipEntry;
+    off_t dexOffset;
+    int err;
+    int result = -1;
+    int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
+    DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
+    DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
+
+    /* make sure we're still at the start of an empty file */
+    if (lseek(cacheFd, 0, SEEK_END) != 0) {
+        ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName);
+        goto bail;
+    }
+
+    /*
+     * Write a skeletal DEX optimization header.  We want the classes.dex
+     * to come just after it.
+     */
+    err = dexOptCreateEmptyHeader(cacheFd);
+    if (err != 0)
+        goto bail;
+
+    /* record the file position so we can get back here later */
+    dexOffset = lseek(cacheFd, 0, SEEK_CUR);
+    if (dexOffset < 0)
+        goto bail;
+
+    /*
+     * Open the zip archive, find the DEX entry.
+     */
+    if (dexZipOpenArchiveFd(zipFd, debugFileName, &zippy) != 0) {
+        ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName);
+        goto bail;
+    }
+
+    if (dexZipFindEntry(zippy, kClassesDex, &zipEntry) != 0) {
+        ALOGW("DexOptZ: zip archive '%s' does not include %s",
+            debugFileName, kClassesDex);
+        goto bail;
+    }
+
+    /*
+     * Extract the DEX data into the cache file at the current offset.
+     */
+    if (dexZipExtractEntryToFile(zippy, &zipEntry, cacheFd) != 0) {
+        ALOGW("DexOptZ: extraction of %s from %s failed",
+            kClassesDex, debugFileName);
+        goto bail;
+    }
+
+    /* Parse the options. */
+    if (dexoptFlagStr[0] != '\0') {
+        const char* opc;
+        const char* val;
+
+        opc = strstr(dexoptFlagStr, "v=");      /* verification */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'n':   verifyMode = VERIFY_MODE_NONE;          break;
+            case 'r':   verifyMode = VERIFY_MODE_REMOTE;        break;
+            case 'a':   verifyMode = VERIFY_MODE_ALL;           break;
+            default:                                            break;
+            }
+        }
+
+        opc = strstr(dexoptFlagStr, "o=");      /* optimization */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'n':   dexOptMode = OPTIMIZE_MODE_NONE;        break;
+            case 'v':   dexOptMode = OPTIMIZE_MODE_VERIFIED;    break;
+            case 'a':   dexOptMode = OPTIMIZE_MODE_ALL;         break;
+            case 'f':   dexOptMode = OPTIMIZE_MODE_FULL;        break;
+            default:                                            break;
+            }
+        }
+
+        opc = strstr(dexoptFlagStr, "m=y");     /* register map */
+        if (opc != NULL) {
+            dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
+        }
+
+        opc = strstr(dexoptFlagStr, "u=");      /* uniprocessor target */
+        if (opc != NULL) {
+            switch (*(opc+2)) {
+            case 'y':   dexoptFlags |= DEXOPT_UNIPROCESSOR;     break;
+            case 'n':   dexoptFlags |= DEXOPT_SMP;              break;
+            default:                                            break;
+            }
+        }
+    }
+
+    /*
+     * Prep the VM and perform the optimization.
+     */
+
+    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
+            dexoptFlags) != 0)
+    {
+        ALOGE("DexOptZ: VM init failed");
+        goto bail;
+    }
+
+    //vmStarted = 1;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(cacheFd, dexOffset,
+            zipEntry.uncompressed_length, debugFileName,
+            zipEntry.mod_time, zipEntry.crc32, isBootstrap))
+    {
+        ALOGE("Optimization failed");
+        goto bail;
+    }
+
+    /* we don't shut the VM down -- process is about to exit */
+
+    result = 0;
+
+bail:
+    dexZipCloseArchive(zippy);
+    return result;
+}
+
+/*
+ * Common functionality for normal device-side processing as well as
+ * preoptimization.
+ */
+static int processZipFile(int zipFd, int cacheFd, const char* zipName,
+        const char *dexoptFlags)
+{
+    char* bcpCopy = NULL;
+
+    /*
+     * Check to see if this is a bootstrap class entry. If so, truncate
+     * the path.
+     */
+    const char* bcp = getenv("BOOTCLASSPATH");
+    if (bcp == NULL) {
+        ALOGE("DexOptZ: BOOTCLASSPATH not set");
+        return -1;
+    }
+
+    bool isBootstrap = false;
+    const char* match = strstr(bcp, zipName);
+    if (match != NULL) {
+        /*
+         * TODO: we have a partial string match, but that doesn't mean
+         * we've matched an entire path component. We should make sure
+         * that we're matching on the full zipName, and if not we
+         * should re-do the strstr starting at (match+1).
+         *
+         * The scenario would be a bootclasspath with something like
+         * "/system/framework/core.jar" while we're trying to optimize
+         * "/framework/core.jar". Not very likely since all paths are
+         * absolute and end with ".jar", but not impossible.
+         */
+        int matchOffset = match - bcp;
+        if (matchOffset > 0 && bcp[matchOffset-1] == ':')
+            matchOffset--;
+        ALOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d",
+            zipName, matchOffset);
+        bcpCopy = strdup(bcp);
+        bcpCopy[matchOffset] = '\0';
+
+        bcp = bcpCopy;
+        ALOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'", bcp);
+        isBootstrap = true;
+    }
+
+    int result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
+            bcp, dexoptFlags);
+
+    free(bcpCopy);
+    return result;
+}
+
+/* advance to the next arg and extract it */
+#define GET_ARG(_var, _func, _msg)                                          \
+    {                                                                       \
+        char* endp;                                                         \
+        (_var) = _func(*++argv, &endp, 0);                                  \
+        if (*endp != '\0') {                                                \
+            ALOGE("%s '%s'", _msg, *argv);                                   \
+            goto bail;                                                      \
+        }                                                                   \
+        --argc;                                                             \
+    }
+
+/*
+ * Parse arguments.  We want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--zip"
+ *   2. zip fd (input, read-only)
+ *   3. cache fd (output, read-write, locked with flock)
+ *   4. filename of zipfile being optimized (used for debug messages and
+ *      for comparing against BOOTCLASSPATH; does not need to be
+ *      accessible or even exist)
+ *   5. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path.  If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int fromZip(int argc, char* const argv[])
+{
+    int result = -1;
+    int zipFd, cacheFd;
+    const char* zipName;
+    char* bcpCopy = NULL;
+    const char* dexoptFlags;
+
+    if (argc != 6) {
+        ALOGE("Wrong number of args for --zip (found %d)", argc);
+        goto bail;
+    }
+
+    /* skip "--zip" */
+    argc--;
+    argv++;
+
+    GET_ARG(zipFd, strtol, "bad zip fd");
+    GET_ARG(cacheFd, strtol, "bad cache fd");
+    zipName = *++argv;
+    --argc;
+    dexoptFlags = *++argv;
+    --argc;
+
+    result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
+
+bail:
+    return result;
+}
+
+/*
+ * Parse arguments for a preoptimization run. This is when dalvikvm is run
+ * on a host to optimize dex files for eventual running on a (different)
+ * device. We want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--preopt"
+ *   2. zipfile name
+ *   3. output file name
+ *   4. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path.  If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int preopt(int argc, char* const argv[])
+{
+    int zipFd = -1;
+    int outFd = -1;
+    int result = -1;
+
+    if (argc != 5) {
+        /*
+         * Use stderr here, since this variant is meant to be called on
+         * the host side.
+         */
+        fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
+                argc);
+        return -1;
+    }
+
+    const char* zipName = argv[2];
+    const char* outName = argv[3];
+    const char* dexoptFlags = argv[4];
+
+    if (strstr(dexoptFlags, "u=y") == NULL &&
+        strstr(dexoptFlags, "u=n") == NULL)
+    {
+        fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
+        return -1;
+    }
+
+    zipFd = open(zipName, O_RDONLY);
+    if (zipFd < 0) {
+        perror(argv[0]);
+        return -1;
+    }
+
+    outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
+    if (outFd < 0) {
+        perror(argv[0]);
+        goto bail;
+    }
+
+    result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
+
+bail:
+    if (zipFd >= 0) {
+        close(zipFd);
+    }
+
+    if (outFd >= 0) {
+        close(outFd);
+    }
+
+    return result;
+}
+
+/*
+ * Parse arguments for an "old-style" invocation directly from the VM.
+ *
+ * Here's what we want:
+ *   0. (name of dexopt command -- ignored)
+ *   1. "--dex"
+ *   2. DALVIK_VM_BUILD value, as a sanity check
+ *   3. file descriptor, locked with flock, for DEX file being optimized
+ *   4. DEX offset within file
+ *   5. DEX length
+ *   6. filename of file being optimized (for debug messages only)
+ *   7. modification date of source (goes into dependency section)
+ *   8. CRC of source (goes into dependency section)
+ *   9. flags (optimization level, isBootstrap)
+ *  10. bootclasspath entry #1
+ *  11. bootclasspath entry #2
+ *   ...
+ *
+ * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
+ * argument list and calls this executable.
+ *
+ * The bootclasspath entries become the dependencies for this DEX file.
+ *
+ * The open file descriptor MUST NOT be for one of the bootclasspath files.
+ * The parent has the descriptor locked, and we'll try to lock it again as
+ * part of processing the bootclasspath.  (We can catch this and return
+ * an error by comparing filenames or by opening the bootclasspath files
+ * and stat()ing them for inode numbers).
+ */
+static int fromDex(int argc, char* const argv[])
+{
+    int result = -1;
+    bool vmStarted = false;
+    char* bootClassPath = NULL;
+    int fd, flags, vmBuildVersion;
+    long offset, length;
+    const char* debugFileName;
+    u4 crc, modWhen;
+    char* endp;
+    bool onlyOptVerifiedDex = false;
+    DexClassVerifyMode verifyMode;
+    DexOptimizerMode dexOptMode;
+
+    if (argc < 10) {
+        /* don't have all mandatory args */
+        ALOGE("Not enough arguments for --dex (found %d)", argc);
+        goto bail;
+    }
+
+    /* skip "--dex" */
+    argc--;
+    argv++;
+
+    /*
+     * Extract the args.
+     */
+    GET_ARG(vmBuildVersion, strtol, "bad vm build");
+    if (vmBuildVersion != DALVIK_VM_BUILD) {
+        ALOGE("DexOpt: build rev does not match VM: %d vs %d",
+            vmBuildVersion, DALVIK_VM_BUILD);
+        goto bail;
+    }
+    GET_ARG(fd, strtol, "bad fd");
+    GET_ARG(offset, strtol, "bad offset");
+    GET_ARG(length, strtol, "bad length");
+    debugFileName = *++argv;
+    --argc;
+    GET_ARG(modWhen, strtoul, "bad modWhen");
+    GET_ARG(crc, strtoul, "bad crc");
+    GET_ARG(flags, strtol, "bad flags");
+
+    ALOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=%#x crc=%#x flg=%d (argc=%d)",
+        fd, offset, length, debugFileName, modWhen, crc, flags, argc);
+    assert(argc > 0);
+
+    if (--argc == 0) {
+        bootClassPath = strdup("");
+    } else {
+        int i, bcpLen;
+        char* const* argp;
+        char* cp;
+
+        bcpLen = 0;
+        for (i = 0, argp = argv; i < argc; i++) {
+            ++argp;
+            ALOGV("DEP: '%s'", *argp);
+            bcpLen += strlen(*argp) + 1;
+        }
+
+        cp = bootClassPath = (char*) malloc(bcpLen +1);
+        for (i = 0, argp = argv; i < argc; i++) {
+            int strLen;
+
+            ++argp;
+            strLen = strlen(*argp);
+            if (i != 0)
+                *cp++ = ':';
+            memcpy(cp, *argp, strLen);
+            cp += strLen;
+        }
+        *cp = '\0';
+
+        assert((int) strlen(bootClassPath) == bcpLen-1);
+    }
+    ALOGV("  bootclasspath is '%s'", bootClassPath);
+
+    /* start the VM partway */
+
+    /* ugh -- upgrade these to a bit field if they get any more complex */
+    if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
+        if ((flags & DEXOPT_VERIFY_ALL) != 0)
+            verifyMode = VERIFY_MODE_ALL;
+        else
+            verifyMode = VERIFY_MODE_REMOTE;
+    } else {
+        verifyMode = VERIFY_MODE_NONE;
+    }
+    if ((flags & DEXOPT_OPT_ENABLED) != 0) {
+        if ((flags & DEXOPT_OPT_ALL) != 0)
+            dexOptMode = OPTIMIZE_MODE_ALL;
+        else
+            dexOptMode = OPTIMIZE_MODE_VERIFIED;
+    } else {
+        dexOptMode = OPTIMIZE_MODE_NONE;
+    }
+
+    if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
+        ALOGE("VM init failed");
+        goto bail;
+    }
+
+    vmStarted = true;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
+            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
+    {
+        ALOGE("Optimization failed");
+        goto bail;
+    }
+
+    result = 0;
+
+bail:
+    /*
+     * In theory we should gracefully shut the VM down at this point.  In
+     * practice that only matters if we're checking for memory leaks with
+     * valgrind -- simply exiting is much faster.
+     *
+     * As it turns out, the DEX optimizer plays a little fast and loose
+     * with class loading.  We load all of the classes from a partially-
+     * formed DEX file, which is unmapped when we're done.  If we want to
+     * do clean shutdown here, perhaps for testing with valgrind, we need
+     * to skip the munmap call there.
+     */
+#if 0
+    if (vmStarted) {
+        ALOGI("DexOpt shutting down, result=%d", result);
+        dvmShutdown();
+    }
+#endif
+
+    free(bootClassPath);
+    ALOGV("DexOpt command complete (result=%d)", result);
+    return result;
+}
+
+/*
+ * Main entry point.  Decide where to go.
+ */
+int main(int argc, char* const argv[])
+{
+    set_process_name("dexopt");
+
+    setvbuf(stdout, NULL, _IONBF, 0);
+
+    if (argc > 1) {
+        if (strcmp(argv[1], "--zip") == 0)
+            return fromZip(argc, argv);
+        else if (strcmp(argv[1], "--dex") == 0)
+            return fromDex(argc, argv);
+        else if (strcmp(argv[1], "--preopt") == 0)
+            return preopt(argc, argv);
+    }
+
+    fprintf(stderr,
+        "Usage:\n\n"
+        "Short version: Don't use this.\n\n"
+        "Slightly longer version: This system-internal tool is used to\n"
+        "produce optimized dex files. See the source code for details.\n");
+
+    return 1;
+}
diff --git a/dx/Android.mk b/dx/Android.mk
index 37568d7..3723c44 100644
--- a/dx/Android.mk
+++ b/dx/Android.mk
@@ -7,7 +7,7 @@
 # .jar files they wrap.
 
 # This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
+ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
 # the dx script
 # ============================================================
@@ -87,7 +87,7 @@
 $(LOCAL_INSTALLED_MODULE): | $(installed_shrinkedAndroid) $(installed_mainDexClasses.rules)
 INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
 
-endif # TARGET_BUILD_APPS
+endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
 
 # the dexmerger script
 # ============================================================
diff --git a/dx/src/Android.mk b/dx/src/Android.mk
index bc9fb2e..e53144d 100644
--- a/dx/src/Android.mk
+++ b/dx/src/Android.mk
@@ -3,7 +3,7 @@
 LOCAL_PATH := $(call my-dir)
 
 # This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
+ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
 # dx java library
 # ============================================================
@@ -23,7 +23,7 @@
 
 INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
 
-endif # TARGET_BUILD_APPS
+endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
 
 # the documentation
 # ============================================================
diff --git a/tools/deadcode.py b/tools/deadcode.py
new file mode 100755
index 0000000..2ef8c70
--- /dev/null
+++ b/tools/deadcode.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+
+def SplitSections(buffer):
+    """Spin through the input buffer looking for section header lines.
+    When found, the name of the section is extracted.  The entire contents
+    of that section is added to a result hashmap with the section name
+    as the key"""
+
+    # Match lines like
+    #              |section_name:
+    # capturing section_name
+    headerPattern = re.compile(r'^\s+\|([a-z _]+)\:$', re.MULTILINE)
+
+    sections = {}
+    start = 0
+    anchor = -1
+    sectionName = ''
+
+    while True:
+        # Look for a section header
+        result = headerPattern.search(buffer, start)
+
+        # If there are no more, add a section from the last header to EOF
+        if result is None:
+            if anchor is not -1:
+                sections[sectionName] = buffer[anchor]
+            return sections
+
+        # Add the lines from the last header, to this one to the sections
+        # map indexed by the section name
+        if anchor is not -1:
+            sections[sectionName] = buffer[anchor:result.start()]
+
+        sectionName = result.group(1)
+        start = result.end()
+        anchor = start
+
+    return sections
+
+def FindMethods(section):
+    """Spin through the 'method code index' section and extract all
+    method signatures.  When found, they are added to a result list."""
+
+    # Match lines like:
+    #             |[abcd] com/example/app/Class.method:(args)return
+    # capturing the method signature
+    methodPattern = re.compile(r'^\s+\|\[\w{4}\] (.*)$', re.MULTILINE)
+
+    start = 0
+    methods = []
+
+    while True:
+        # Look for a method name
+        result = methodPattern.search(section, start)
+
+        if result is None:
+            return methods
+
+        # Add the captured signature to the method list
+        methods.append(result.group(1))
+        start = result.end()
+
+def CallsMethod(codes, method):
+    """Spin through all the input method signatures.  For each one, return
+    whether or not there is method invokation line in the codes section that
+    lists the method as the target."""
+
+    start = 0
+
+    while True:
+        # Find the next reference to the method signature
+        match = codes.find(method, start)
+
+        if match is -1:
+            break;
+
+        # Find the beginning of the line the method reference is on
+        startOfLine = codes.rfind("\n", 0, match) + 1
+
+        # If the word 'invoke' comes between the beginning of the line
+        # and the method reference, then it is a call to that method rather
+        # than the beginning of the code section for that method.
+        if codes.find("invoke", startOfLine, match) is not -1:
+            return True
+
+        start = match + len(method)
+
+    return False
+
+
+
+def main():
+    if len(sys.argv) is not 2 or not sys.argv[1].endswith(".jar"):
+        print "Usage:", sys.argv[0], "<filename.jar>"
+        sys.exit()
+
+    command = 'dx --dex --dump-width=1000 --dump-to=-"" "%s"' % sys.argv[1]
+
+    pipe = os.popen(command)
+
+    # Read the whole dump file into memory
+    data = pipe.read()
+    sections = SplitSections(data)
+
+    pipe.close()
+    del(data)
+
+    methods = FindMethods(sections['method code index'])
+    codes = sections['codes']
+    del(sections)
+
+    print "Dead Methods:"
+    count = 0
+
+    for method in methods:
+        if not CallsMethod(codes, method):
+            print "\t", method
+            count += 1
+
+    if count is 0:
+        print "\tNone"
+
+if __name__ == '__main__':
+    main()
diff --git a/tools/dex-preopt b/tools/dex-preopt
new file mode 100755
index 0000000..a9b75a4
--- /dev/null
+++ b/tools/dex-preopt
@@ -0,0 +1,320 @@
+#!/bin/bash
+#
+# 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.
+
+#
+# Usage: dex-preopt [options] path/to/input.jar path/to/output.odex
+#
+# This tool runs a host build of dalvikvm in order to preoptimize dex
+# files that will be run on a device.
+#
+# The input may be any sort of jar file (including .apk files), as long
+# as it contains a classes.dex file. Note that optimized versions of
+# bootstrap classes must be created before this can be run on other files;
+# use the "--bootstrap" option to do this.
+#
+# The "output.odex" file must not already exist.
+#
+# This is expected to be running in a user build environment, where
+# "dexopt" is available on the host.
+#
+# Options:
+#   --build-dir=path/to/out -- Specify where the base of the build tree is.
+#     This is typically a directory named "out". If not specified, it is
+#     assumed to be the current directory. The specified input and output
+#     paths are taken to be relative to this directory.
+#   --dexopt=path/to/dexopt -- Specify the path to the dexopt executable.
+#     If unspecified, there must be a unique subdirectory of the build-dir
+#     that looks like host/ARCH/bin which must contain dexopt.
+#   --product-dir=path/to/product -- Specify the path, relative to the build
+#     directory, where the product tree to be used is. This directory should
+#     contain the boot classpath jar files. If not specified, then there
+#     must be a unique directory in the build named "target/product/NAME",
+#     and this is the directory that will be used.
+#   --boot-dir=path/to/bootclasspath -- Specify the path, relative to the
+#     product directory, of the directory where the boot classpath files
+#     reside. If not specified, this defaults to "system/framework"
+#   --boot-jars=list:of:jar:base:names -- Specify the list of base names
+#     of bootstrap classpath elements, colon-separated. Order is significant
+#     and must match the BOOTCLASSPATH that is eventually specified at
+#     runtime on the device. This defaults to "core". However, this really
+#     needs to match the target product's BOOTCLASSPATH, which, as of this
+#     writing, doesn't have a super-strict way of being defined within the
+#     build. You can find variations of it in different init.rc files under
+#     system/core/rootdir or under product-specific directories.
+#   --bootstrap -- Process the bootstrap classes. If this is specified,
+#     then, instead of processing a specified input file, no other arguments
+#     are taken, and what is processed is the entirety of the boot jar
+#     list, in order.
+#   --verify={none,remote,all} -- Specify what level of verification to
+#     do. Defaults to "all".
+#   --optimize={none,verified,all} -- Specify which classes to optimize.
+#     Defaults to "verified".
+#   --no-register-maps -- Indicate that the output should not contain
+#     register maps. By default, register maps are created and included.
+#   --uniprocessor -- Indicate that the output should target a uniprocessor.
+#     By default, optimizations will be made that specifically target
+#     SMP processors (which will merely be superfluous on uniprocessors).
+#
+
+# Defaults.
+dexopt=''
+buildDir='.'
+productDir=''
+bootDir='system/framework'
+bootstrap='no'
+doVerify='all'
+doOptimize='verified'
+doRegisterMaps='yes'
+doUniprocessor='no'
+bootJars='core'
+
+optimizeFlags='' # built up from the more human-friendly options
+bogus='no' # indicates if there was an error during processing arguments
+
+# Iterate over the arguments looking for options.
+while true; do
+    origOption="$1"
+
+    if [ "x${origOption}" = "x--" ]; then
+        # A raw "--" signals the end of option processing.
+        shift
+        break
+    fi
+
+    # Parse the option into components.
+    optionBeforeValue=`expr -- "${origOption}" : '--\([^=]*\)='`
+
+    if [ "$?" = '0' ]; then
+        # Option has the form "--option=value".
+        option="${optionBeforeValue}"
+        value=`expr -- "${origOption}" : '--[^=]*=\(.*\)'`
+        hasValue='yes'
+    else
+        option=`expr -- "${origOption}" : '--\(.*\)'`
+        if [ "$?" = '1' ]; then
+            # Not an option.
+            break
+        fi
+        # Option has the form "--option".
+        value=""
+        hasValue='no'
+    fi
+    shift
+
+    # Interpret the option
+    if [ "${option}" = 'build-dir' -a "${hasValue}" = 'yes' ]; then
+        buildDir="${value}"
+    elif [ "${option}" = 'dexopt' -a "${hasValue}" = 'yes' ]; then
+        dexopt="${value}"
+    elif [ "${option}" = 'boot-dir' -a "${hasValue}" = 'yes' ]; then
+        bootDir="${value}"
+    elif [ "${option}" = 'product-dir' -a "${hasValue}" = 'yes' ]; then
+        productDir="${value}"
+    elif [ "${option}" = 'boot-jars' -a "${hasValue}" = 'yes' ]; then
+        bootJars="${value}"
+    elif [ "${option}" = 'bootstrap' -a "${hasValue}" = 'no' ]; then
+        bootstrap='yes'
+    elif [ "${option}" = 'verify' -a "${hasValue}" = 'yes' ]; then
+        doVerify="${value}"
+    elif [ "${option}" = 'optimize' -a "${hasValue}" = 'yes' ]; then
+        doOptimize="${value}"
+    elif [ "${option}" = 'no-register-maps' -a "${hasValue}" = 'no' ]; then
+        doRegisterMaps='no'
+    elif [ "${option}" = 'uniprocessor' -a "${hasValue}" = 'no' ]; then
+        doUniprocessor='yes'
+    else
+        echo "unknown option: ${origOption}" 1>&2
+        bogus='yes'
+    fi
+done
+
+# Check and set up the input and output files. In the case of bootstrap
+# processing, verify that no files are specified.
+inputFile=$1
+outputFile=$2
+if [ "${bootstrap}" = 'yes' ]; then
+    if [ "$#" != '0' ]; then
+        echo "unexpected arguments in --bootstrap mode" 1>&2
+        bogus=yes
+    fi
+elif [ "$#" != '2' ]; then
+    echo "must specify input and output files (and no more arguments)" 1>&2
+    bogus=yes
+fi
+
+# Sanity-check the specified build directory.
+if [ "x${buildDir}" = 'x' ]; then
+    echo "must specify build directory" 1>&2
+    bogus=yes
+elif [ ! '(' -d "${buildDir}" -a -w "${buildDir}" ')' ]; then
+    echo "build-dir is not a writable directory: ${buildDir}" 1>&2
+    bogus=yes
+fi
+
+# Sanity-check the specified boot classpath directory.
+if [ "x${bootDir}" = 'x' ]; then
+    echo "must specify boot classpath directory" 1>&2
+    bogus=yes
+fi
+
+# Sanity-check the specified boot jar list.
+if [ "x${bootJars}" = 'x' ]; then
+    echo "must specify non-empty boot-jars list" 1>&2
+    bogus=yes
+fi
+
+# Sanity-check and expand the verify option.
+if [ "x${doVerify}" = 'xnone' ]; then
+    optimizeFlags="${optimizeFlags},v=n"
+elif [ "x${doVerify}" = 'xremote' ]; then
+    optimizeFlags="${optimizeFlags},v=r"
+elif [ "x${doVerify}" = 'xall' ]; then
+    optimizeFlags="${optimizeFlags},v=a"
+else
+    echo "bad value for --verify: ${doVerify}" 1>&2
+    bogus=yes
+fi
+
+# Sanity-check and expand the optimize option.
+if [ "x${doOptimize}" = 'xnone' ]; then
+    optimizeFlags="${optimizeFlags},o=n"
+elif [ "x${doOptimize}" = 'xverified' ]; then
+    optimizeFlags="${optimizeFlags},o=v"
+elif [ "x${doOptimize}" = 'xall' ]; then
+    optimizeFlags="${optimizeFlags},o=a"
+else
+    echo "bad value for --optimize: ${doOptimize}" 1>&2
+    bogus=yes
+fi
+
+# Expand the register maps selection, if necessary.
+if [ "${doRegisterMaps}" = 'yes' ]; then
+    optimizeFlags="${optimizeFlags},m=y"
+fi
+
+# Expand the uniprocessor directive, if necessary.
+if [ "${doUniprocessor}" = 'yes' ]; then
+    optimizeFlags="${optimizeFlags},u=y"
+else
+    optimizeFlags="${optimizeFlags},u=n"
+fi
+
+# Kill off the spare comma in optimizeFlags.
+optimizeFlags=`echo ${optimizeFlags} | sed 's/^,//'`
+
+# Error out if there was trouble.
+if [ "${bogus}" = 'yes' ]; then
+    # There was an error during option processing.
+    echo "usage: $0" 1>&2
+    echo '  [--build-dir=path/to/out] [--dexopt=path/to/dexopt]' 1>&2
+    echo '  [--product-dir=path/to/product] [--boot-dir=name]' 1>&2
+    echo '  [--boot-jars=list:of:names] [--bootstrap]' 1>&2
+    echo '  [--verify=type] [--optimize=type] [--no-register-maps]' 1>&2
+    echo '  [--uniprocessor] path/to/input.jar path/to/output.odex' 1>&2
+    exit 1
+fi
+
+# Cd to the build directory, un-symlinkifying it for clarity.
+cd "${buildDir}"
+cd "`/bin/pwd`"
+
+# If needed, find the default product directory.
+if [ "x${productDir}" = 'x' ]; then
+    productDir="`ls target/product`"
+    if [ "$?" != '0' ]; then
+        echo "can't find product directory" 1>&2
+        exit 1
+    elif [ `expr -- "${productDir}" : ".* "` != '0' ]; then
+        echo "ambiguous product directory" 1>&2
+        exit 1
+    fi
+    productDir="target/product/${productDir}"
+fi
+
+# Verify the product directory.
+if [ ! '(' -d "${productDir}" -a -w "${productDir}" ')' ]; then
+    echo "product-dir is not a writable directory: ${productDir}" 1>&2
+    exit 1
+fi
+
+# Expand and verify the boot classpath directory. We add "/./" here to
+# separate the build system part of the path from the target system
+# suffix part of the path. The dexopt executable (deep inside the vm
+# really) uses this to know how to generate the names of the
+# dependencies (since we don't want the device files to contain bits
+# of pathname from the host build system).
+productBootDir="${productDir}/./${bootDir}"
+if [ ! '(' -d "${productBootDir}" -a -w "${productBootDir}" ')' ]; then
+    echo "boot-dir is not a writable directory: ${productBootDir}" 1>&2
+    exit 1
+fi
+
+# Find the dexopt binary if necesasry, and verify it.
+if [ "x${dexopt}" = 'x' ]; then
+    dexopt="`ls host/*/bin/dexopt`"
+    if [ "$?" != '0' ]; then
+        echo "can't find dexopt binary" 1>&2
+        exit 1
+    elif [ `expr -- "${dexopt}" : ".* "` != '0' ]; then
+        echo "ambiguous host directory" 1>&2
+        exit 1
+    fi
+fi
+if [ ! -x "${dexopt}" ]; then
+    echo "dexopt binary is not executable: ${dexopt}" 1>&2
+    exit 1
+fi
+
+# Expand the bootJars into paths that are relative from the build
+# directory, maintaining the colon separators.
+BOOTCLASSPATH=`echo ":${bootJars}" | \
+    sed "s!:\([^:]*\)!:${productBootDir}/\1.jar!g" | \
+    sed 's/^://'`
+export BOOTCLASSPATH
+
+if [ "${bootstrap}" = 'yes' ]; then
+    # Split the boot classpath into separate elements and iterate over them,
+    # processing each, in order.
+    elements=`echo "${BOOTCLASSPATH}" | sed 's/:/ /g'`
+
+    for inputFile in $elements; do
+        echo "Processing ${inputFile}" 1>&2
+        outputFile="`dirname ${inputFile}`/`basename ${inputFile} .jar`.odex"
+        "${dexopt}" --preopt "${inputFile}" "${outputFile}" "${optimizeFlags}"
+        status="$?"
+        if [ "${status}" != '0' ]; then
+            exit "${status}"
+        fi
+    done
+else
+    echo "Processing ${inputFile}" 1>&2
+
+    bootJarFile=`expr -- "${inputFile}" : "${productDir}/${bootDir}/\(.*\)"`
+    if [ "x${bootJarFile}" != 'x' ]; then
+        # The input file is in the boot classpath directory, so it needs
+        # to have "/./" inserted into it (see longer description above).
+        inputFile="${productBootDir}/${bootJarFile}"
+    fi
+
+    "${dexopt}" --preopt "${inputFile}" "${outputFile}" "${optimizeFlags}"
+
+    status="$?"
+    if [ "${status}" != '0' ]; then
+        exit "${status}"
+    fi
+fi
+
+echo "Done!" 1>&2
diff --git a/tools/dexcheck b/tools/dexcheck
new file mode 100755
index 0000000..2ec8b29
--- /dev/null
+++ b/tools/dexcheck
@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 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.
+
+#
+# This tool checks the integrity of the optimized dex files on a single
+# Android device connected to your computer.
+#
+# Brief HOW-TO:
+#
+# 1. Disconnect all but one device from USB.
+# 2. Set up a standard shell environment (envsetup.sh, lunch, etc.).
+# 3. Run "adb root" if necessary to ensure read permission on
+#    /data/dalvik-cache. If in doubt, run the command. Power users may
+#    also use "su" followed by "chmod 777 /data/dalvik-cache".
+# 4. Run this script, e.g. from the build root, "dalvik/tools/dexcheck".
+#
+# If all of the dex files are okay, you will just see a series of
+# lines written to your shell window naming each of the files. If
+# there is a problem, though, you will see something like this:
+#
+#     system@app@Maps.apk@classes.dex
+#     Failure in system@app@Maps.apk@classes.dex: ERROR: DEX parse failed
+#
+# When this happens, the log ("adb logcat") will generally have at
+# least a little more information about the dex level of the problem.
+# However, any error at all usually indicates some form of lower level
+# filesystem or filesystem cache corruption.
+#
+
+# Get the list of files.  Use "sed" to drop the trailing carriage return.
+files=`adb shell "cd /data/dalvik-cache; echo *" | sed -e s/.$//`
+if [ "$files" = "*" ]; then
+    echo 'ERROR: commands must run as root on device (try "adb root" first?)'
+    exit 1
+fi
+
+failure=0
+
+# Check each file in turn.  This is much faster with "dexdump -c", but that
+# flag was not available in 1.6 and earlier.
+#
+# The dexdump found in older builds does not stop on checksum failures and
+# will likely crash.
+for file in $files; do
+    echo $file
+    errout=`adb shell "dexdump /data/dalvik-cache/$file > dev/null"`
+    errcount=`echo $errout | wc -w` > /dev/null
+    if [ $errcount != "0" ]; then
+        echo "  Failure in $file: $errout"
+        failure=1
+    fi
+done
+
+exit $failure
diff --git a/tools/gdbjithelper/Android.mk b/tools/gdbjithelper/Android.mk
new file mode 100644
index 0000000..cf19907
--- /dev/null
+++ b/tools/gdbjithelper/Android.mk
@@ -0,0 +1,23 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := gdbjithelper.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_MODULE := gdbjithelper
+LOCAL_MODULE_TAGS := eng
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_EXECUTABLE)
diff --git a/tools/gdbjithelper/README.txt b/tools/gdbjithelper/README.txt
new file mode 100644
index 0000000..aab1a00
--- /dev/null
+++ b/tools/gdbjithelper/README.txt
@@ -0,0 +1,65 @@
+Step 1
+
+If you see a native crash in the bugreport and the PC/LR are pointing to the
+code cache address range*, copy them into codePC and codeLR in gdbjithelper.c,
+respectively.
+
+*Caveats: debuggerd doesn't know the range of code cache. So apply this tool if
+the crashing address is not contained by any shared library.
+
+       #00  pc 463ba204
+       #01  lr 463ba1c9  <unknown>
+
+code around pc:
+463ba1e4 4300e119 4284aa7a f927f7b7 40112268
+463ba1f4 419da7f8 00002000 01000100 00080000
+463ba204 4191debc 01010000 4284aa74 68b00054
+463ba214 045cf205 cc016468 0718f2a5 d0102800
+463ba224 4c13c701 a20aa108 efb0f775 e008e010
+
+code around lr:
+463ba1a8 42e19e58 f2050050 cc01045c 0718f2a5
+463ba1b8 d00f2800 4c13c701 a20aa108 efe4f775
+463ba1c8 e007e010 29006bf8 6e77dc01 a10347b8
+463ba1d8 ef60f775 6db1480b 1c2d4788 4300e119
+463ba1e8 4284aa7a f927f7b7 40112268 419da7f8
+
+
+Step 2
+
+Push $OUT/EXECUTABLES/gdbjithelper_intermediates/LINKED/gdbjithelper to
+/system/bin on the device or emulator
+
+
+Step 3
+
+Debug the executable as usual:
+
+adb forward tcp:5039 tcp:5039
+adb shell gdbserver :5039 /system/bin/gdbjithelper &
+arm-eabi-gdb $OUT/symbols/system/bin/gdbjithelper
+(gdb) tar r :5039
+Remote debugging using :5039
+Remote debugging from host 127.0.0.1
+gdb: Unable to get location for thread creation breakpoint: requested event is not supported
+__dl__start () at bionic/linker/arch/arm/begin.S:35
+35      mov r0, sp
+gdb: Unable to get location for thread creation breakpoint: requested event is not supported
+Current language:  auto; currently asm
+(gdb) c
+Continuing.
+[New Thread 596]
+codePC[0]: 0x4300e119
+codePC[1]: 0x4284aa7a
+         :
+
+
+Step 4
+
+Hit ctrl-C
+
+Issue the following command to see code around PC
+x /20i (char *) &codePC+1
+
+Issue the following command to see code around LR
+x /20i (char *) &codeLR+1
diff --git a/tools/gdbjithelper/gdbjithelper.c b/tools/gdbjithelper/gdbjithelper.c
new file mode 100644
index 0000000..817d5a4
--- /dev/null
+++ b/tools/gdbjithelper/gdbjithelper.c
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+
+/* Currently debuggerd dumps 20 words each around PC and LR */
+#define NUM_DUMPED_WORDS 20
+
+volatile int done;
+
+/*
+ * See README.txt for detailed steps.
+ *
+ * If you see a native crash in the bugreport and the PC/LR are
+ * pointing to the code cache address range, copy them into the following
+ * arrays.
+ *
+ *        #00  pc 463ba204
+ *        #01  lr 463ba1c9  <unknown>
+ *
+ * code around pc:
+ * 463ba1e4 4300e119 4284aa7a f927f7b7 40112268
+ * 463ba1f4 419da7f8 00002000 01000100 00080000
+ * 463ba204 4191debc 01010000 4284aa74 68b00054
+ * 463ba214 045cf205 cc016468 0718f2a5 d0102800
+ * 463ba224 4c13c701 a20aa108 efb0f775 e008e010
+ *
+ * code around lr:
+ * 463ba1a8 42e19e58 f2050050 cc01045c 0718f2a5
+ * 463ba1b8 d00f2800 4c13c701 a20aa108 efe4f775
+ * 463ba1c8 e007e010 29006bf8 6e77dc01 a10347b8
+ * 463ba1d8 ef60f775 6db1480b 1c2d4788 4300e119
+ * 463ba1e8 4284aa7a f927f7b7 40112268 419da7f8
+ *
+ */
+
+int codePC[] = {
+    // Sample content
+    0x4300e119, 0x4284aa7a, 0xf927f7b7, 0x40112268,
+    0x419da7f8, 0x00002000, 0x01000100, 0x00080000,
+    0x4191debc, 0x01010000, 0x4284aa74, 0x68b00054,
+    0x045cf205, 0xcc016468, 0x0718f2a5, 0xd0102800,
+    0x4c13c701, 0xa20aa108, 0xefb0f775, 0xe008e010,
+};
+
+int codeLR[] = {
+    // Sample content
+    0x42e19e58, 0xf2050050, 0xcc01045c, 0x0718f2a5,
+    0xd00f2800, 0x4c13c701, 0xa20aa108, 0xefe4f775,
+    0xe007e010, 0x29006bf8, 0x6e77dc01, 0xa10347b8,
+    0xef60f775, 0x6db1480b, 0x1c2d4788, 0x4300e119,
+    0x4284aa7a, 0xf927f7b7, 0x40112268, 0x419da7f8,
+};
+
+/* For example: 463ba1e4 & 0xfff */
+#define START_PC_PAGE_OFFSET 0x1e4
+
+/* For example: 463ba1a8 & 0xfff */
+#define START_LR_PAGE_OFFSET 0x1a8
+
+/* Each points to a two-page buffer */
+char *codePCCache, *codeLRCache;
+
+void dumpCode(int *pc, int *lr)
+{
+    unsigned int i;
+
+    for (i = 0; i < NUM_DUMPED_WORDS; i++) {
+        printf("%p codePC[%d]: %#010x\n", pc + i, i, pc[i]);
+    }
+
+    for (i = 0; i < NUM_DUMPED_WORDS; i++) {
+        printf("%p codeLR[%d]: %#010x\n", lr + i, i, lr[i]);
+    }
+}
+
+int main()
+{
+    codePCCache = memalign(4096, 8192);
+    codeLRCache = memalign(4096, 8192);
+
+    memcpy(codePCCache + START_PC_PAGE_OFFSET, codePC, 4 * NUM_DUMPED_WORDS);
+    memcpy(codeLRCache + START_LR_PAGE_OFFSET, codeLR, 4 * NUM_DUMPED_WORDS);
+
+    dumpCode((int *) (codePCCache + START_PC_PAGE_OFFSET),
+             (int *) (codeLRCache + START_LR_PAGE_OFFSET));
+
+    while (!done) {
+        sleep(1000);
+    }
+    return 0;
+}
diff --git a/unit-tests/Android.mk b/unit-tests/Android.mk
new file mode 100644
index 0000000..c716244
--- /dev/null
+++ b/unit-tests/Android.mk
@@ -0,0 +1,48 @@
+#
+# 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.
+# Copyright The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+test_module = dalvik-vm-unit-tests
+
+test_src_files = \
+    dvmHumanReadableDescriptor_test.cpp \
+    
+test_c_includes = \
+    dalvik \
+    dalvik/vm \
+
+# Build for the device. Run with:
+#   adb shell /data/nativetest/dalvik-vm-unit-tests/dalvik-vm-unit-tests
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DANDROID_SMP=1
+LOCAL_C_INCLUDES += $(test_c_includes)
+LOCAL_MODULE := $(test_module)
+LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_SHARED_LIBRARIES += libcutils libdvm
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_NATIVE_TEST)
+
+# Build for the host.
+# TODO: BUILD_HOST_NATIVE_TEST doesn't work yet; STL-related compile-time and
+# run-time failures, presumably astl/stlport/genuine host STL confusion.
+#include $(CLEAR_VARS)
+#LOCAL_C_INCLUDES += $(test_c_includes)
+#LOCAL_MODULE := $(test_module)
+#LOCAL_SRC_FILES := $(test_src_files)
+#LOCAL_SHARED_LIBRARIES += libdvm libcrypto libssl libicuuc libicui18n
+#LOCAL_WHOLE_STATIC_LIBRARIES += libcutils liblog libdvm
+#include $(BUILD_HOST_NATIVE_TEST)
diff --git a/unit-tests/dvmHumanReadableDescriptor_test.cpp b/unit-tests/dvmHumanReadableDescriptor_test.cpp
new file mode 100644
index 0000000..89ca85c
--- /dev/null
+++ b/unit-tests/dvmHumanReadableDescriptor_test.cpp
@@ -0,0 +1,43 @@
+#include <gtest/gtest.h>
+
+#include "Dalvik.h"
+
+TEST(dvmHumanReadableDescriptor, ArrayReferences) {
+  ASSERT_EQ("java.lang.Class[]", dvmHumanReadableDescriptor("[Ljava/lang/Class;"));
+  ASSERT_EQ("java.lang.Class[][]", dvmHumanReadableDescriptor("[[Ljava/lang/Class;"));
+}
+
+TEST(dvmHumanReadableDescriptor, ScalarReferences) {
+  ASSERT_EQ("java.lang.String", dvmHumanReadableDescriptor("Ljava.lang.String;"));
+  ASSERT_EQ("java.lang.String", dvmHumanReadableDescriptor("Ljava/lang/String;"));
+}
+
+TEST(dvmHumanReadableDescriptor, PrimitiveArrays) {
+  ASSERT_EQ("boolean[]", dvmHumanReadableDescriptor("[Z"));
+  ASSERT_EQ("boolean[][]", dvmHumanReadableDescriptor("[[Z"));
+  ASSERT_EQ("byte[]", dvmHumanReadableDescriptor("[B"));
+  ASSERT_EQ("byte[][]", dvmHumanReadableDescriptor("[[B"));
+  ASSERT_EQ("char[]", dvmHumanReadableDescriptor("[C"));
+  ASSERT_EQ("char[][]", dvmHumanReadableDescriptor("[[C"));
+  ASSERT_EQ("double[]", dvmHumanReadableDescriptor("[D"));
+  ASSERT_EQ("double[][]", dvmHumanReadableDescriptor("[[D"));
+  ASSERT_EQ("float[]", dvmHumanReadableDescriptor("[F"));
+  ASSERT_EQ("float[][]", dvmHumanReadableDescriptor("[[F"));
+  ASSERT_EQ("int[]", dvmHumanReadableDescriptor("[I"));
+  ASSERT_EQ("int[][]", dvmHumanReadableDescriptor("[[I"));
+  ASSERT_EQ("long[]", dvmHumanReadableDescriptor("[J"));
+  ASSERT_EQ("long[][]", dvmHumanReadableDescriptor("[[J"));
+  ASSERT_EQ("short[]", dvmHumanReadableDescriptor("[S"));
+  ASSERT_EQ("short[][]", dvmHumanReadableDescriptor("[[S"));
+}
+
+TEST(dvmHumanReadableDescriptor, PrimitiveScalars) {
+  ASSERT_EQ("boolean", dvmHumanReadableDescriptor("Z"));
+  ASSERT_EQ("byte", dvmHumanReadableDescriptor("B"));
+  ASSERT_EQ("char", dvmHumanReadableDescriptor("C"));
+  ASSERT_EQ("double", dvmHumanReadableDescriptor("D"));
+  ASSERT_EQ("float", dvmHumanReadableDescriptor("F"));
+  ASSERT_EQ("int", dvmHumanReadableDescriptor("I"));
+  ASSERT_EQ("long", dvmHumanReadableDescriptor("J"));
+  ASSERT_EQ("short", dvmHumanReadableDescriptor("S"));
+}
diff --git a/vm/AllocTracker.cpp b/vm/AllocTracker.cpp
new file mode 100644
index 0000000..8b86c5e
--- /dev/null
+++ b/vm/AllocTracker.cpp
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Allocation tracking and reporting.  We maintain a circular buffer with
+ * the most recent allocations.  The data can be viewed through DDMS.
+ *
+ * There are two basic approaches: manage the buffer with atomic updates
+ * and do a system-wide suspend when DDMS requests it, or protect all
+ * accesses with a mutex.  The former is potentially more efficient, but
+ * the latter is much simpler and more reliable.
+ *
+ * Ideally we'd just use the object heap allocation mutex to guard this
+ * structure, but at the point we grab that (under dvmMalloc()) we're just
+ * allocating a collection of bytes and no longer have the class reference.
+ * Because this is an optional feature it's best to leave the existing
+ * code undisturbed and just use an additional lock.
+ *
+ * We don't currently track allocations of class objects.  We could, but
+ * with the possible exception of Proxy objects they're not that interesting.
+ *
+ * TODO: if we add support for class unloading, we need to add the class
+ * references here to the root set (or just disable class unloading while
+ * this is active).
+ *
+ * TODO: consider making the parameters configurable, so DDMS can decide
+ * how many allocations it wants to see and what the stack depth should be.
+ * Changing the window size is easy, changing the max stack depth is harder
+ * because we go from an array of fixed-size structs to variable-sized data.
+ */
+#include "Dalvik.h"
+
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+static bool isPowerOfTwo(int x) { return (x & (x - 1)) == 0; }
+#endif
+
+#define kMaxAllocRecordStackDepth   16      /* max 255 */
+
+#define kDefaultNumAllocRecords 64*1024 /* MUST be power of 2 */
+
+/*
+ * Record the details of an allocation.
+ */
+struct AllocRecord {
+    ClassObject*    clazz;      /* class allocated in this block */
+    u4              size;       /* total size requested */
+    u2              threadId;   /* simple thread ID; could be recycled */
+
+    /* stack trace elements; unused entries have method==NULL */
+    struct {
+        const Method* method;   /* which method we're executing in */
+        int         pc;         /* current execution offset, in 16-bit units */
+    } stackElem[kMaxAllocRecordStackDepth];
+};
+
+/*
+ * Initialize a few things.  This gets called early, so keep activity to
+ * a minimum.
+ */
+bool dvmAllocTrackerStartup()
+{
+    /* prep locks */
+    dvmInitMutex(&gDvm.allocTrackerLock);
+
+    /* initialized when enabled by DDMS */
+    assert(gDvm.allocRecords == NULL);
+
+    return true;
+}
+
+/*
+ * Release anything we're holding on to.
+ */
+void dvmAllocTrackerShutdown()
+{
+    free(gDvm.allocRecords);
+    dvmDestroyMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ *      Collection
+ * ===========================================================================
+ */
+
+static int getAllocRecordMax() {
+#ifdef HAVE_ANDROID_OS
+    // Check whether there's a system property overriding the number of records.
+    const char* propertyName = "dalvik.vm.allocTrackerMax";
+    char allocRecordMaxString[PROPERTY_VALUE_MAX];
+    if (property_get(propertyName, allocRecordMaxString, "") > 0) {
+        char* end;
+        size_t value = strtoul(allocRecordMaxString, &end, 10);
+        if (*end != '\0') {
+            ALOGE("Ignoring %s '%s' --- invalid", propertyName, allocRecordMaxString);
+            return kDefaultNumAllocRecords;
+        }
+        if (!isPowerOfTwo(value)) {
+            ALOGE("Ignoring %s '%s' --- not power of two", propertyName, allocRecordMaxString);
+            return kDefaultNumAllocRecords;
+        }
+        return value;
+    }
+#endif
+    return kDefaultNumAllocRecords;
+}
+
+/*
+ * Enable allocation tracking.  Does nothing if tracking is already enabled.
+ *
+ * Returns "true" on success.
+ */
+bool dvmEnableAllocTracker()
+{
+    bool result = true;
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    if (gDvm.allocRecords == NULL) {
+        gDvm.allocRecordMax = getAllocRecordMax();
+
+        ALOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)",
+              gDvm.allocRecordMax, kMaxAllocRecordStackDepth,
+              sizeof(AllocRecord) * gDvm.allocRecordMax);
+        gDvm.allocRecordHead = gDvm.allocRecordCount = 0;
+        gDvm.allocRecords = (AllocRecord*) malloc(sizeof(AllocRecord) * gDvm.allocRecordMax);
+
+        if (gDvm.allocRecords == NULL)
+            result = false;
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    return result;
+}
+
+/*
+ * Disable allocation tracking.  Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker()
+{
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    if (gDvm.allocRecords != NULL) {
+        free(gDvm.allocRecords);
+        gDvm.allocRecords = NULL;
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+}
+
+/*
+ * Get the last few stack frames.
+ */
+static void getStackFrames(Thread* self, AllocRecord* pRec)
+{
+    int stackDepth = 0;
+    void* fp;
+
+    fp = self->interpSave.curFrame;
+
+    while ((fp != NULL) && (stackDepth < kMaxAllocRecordStackDepth)) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*) fp)) {
+            pRec->stackElem[stackDepth].method = method;
+            if (dvmIsNativeMethod(method)) {
+                pRec->stackElem[stackDepth].pc = 0;
+            } else {
+                assert(saveArea->xtra.currentPc >= method->insns &&
+                        saveArea->xtra.currentPc <
+                        method->insns + dvmGetMethodInsnsSize(method));
+                pRec->stackElem[stackDepth].pc =
+                    (int) (saveArea->xtra.currentPc - method->insns);
+            }
+            stackDepth++;
+        }
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+
+    /* clear out the rest (normally there won't be any) */
+    while (stackDepth < kMaxAllocRecordStackDepth) {
+        pRec->stackElem[stackDepth].method = NULL;
+        pRec->stackElem[stackDepth].pc = 0;
+        stackDepth++;
+    }
+}
+
+/*
+ * Add a new allocation to the set.
+ */
+void dvmDoTrackAllocation(ClassObject* clazz, size_t size)
+{
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        ALOGW("alloc tracker: no thread");
+        return;
+    }
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+    if (gDvm.allocRecords == NULL) {
+        dvmUnlockMutex(&gDvm.allocTrackerLock);
+        return;
+    }
+
+    /* advance and clip */
+    if (++gDvm.allocRecordHead == gDvm.allocRecordMax)
+        gDvm.allocRecordHead = 0;
+
+    AllocRecord* pRec = &gDvm.allocRecords[gDvm.allocRecordHead];
+
+    pRec->clazz = clazz;
+    pRec->size = size;
+    pRec->threadId = self->threadId;
+    getStackFrames(self, pRec);
+
+    if (gDvm.allocRecordCount < gDvm.allocRecordMax)
+        gDvm.allocRecordCount++;
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ *      Reporting
+ * ===========================================================================
+ */
+
+/*
+The data we send to DDMS contains everything we have recorded.
+
+Message header (all values big-endian):
+  (1b) message header len (to allow future expansion); includes itself
+  (1b) entry header len
+  (1b) stack frame len
+  (2b) number of entries
+  (4b) offset to string table from start of message
+  (2b) number of class name strings
+  (2b) number of method name strings
+  (2b) number of source file name strings
+  For each entry:
+    (4b) total allocation size
+    (2b) threadId
+    (2b) allocated object's class name index
+    (1b) stack depth
+    For each stack frame:
+      (2b) method's class name
+      (2b) method name
+      (2b) method source file
+      (2b) line number, clipped to 32767; -2 if native; -1 if no source
+  (xb) class name strings
+  (xb) method name strings
+  (xb) source file strings
+
+  As with other DDM traffic, strings are sent as a 4-byte length
+  followed by UTF-16 data.
+
+We send up 16-bit unsigned indexes into string tables.  In theory there
+can be (kMaxAllocRecordStackDepth * gDvm.allocRecordMax) unique strings in
+each table, but in practice there should be far fewer.
+
+The chief reason for using a string table here is to keep the size of
+the DDMS message to a minimum.  This is partly to make the protocol
+efficient, but also because we have to form the whole thing up all at
+once in a memory buffer.
+
+We use separate string tables for class names, method names, and source
+files to keep the indexes small.  There will generally be no overlap
+between the contents of these tables.
+*/
+const int kMessageHeaderLen = 15;
+const int kEntryHeaderLen = 9;
+const int kStackFrameLen = 8;
+
+/*
+ * Return the index of the head element.
+ *
+ * We point at the most-recently-written record, so if allocRecordCount is 1
+ * we want to use the current element.  Take "head+1" and subtract count
+ * from it.
+ *
+ * We need to handle underflow in our circular buffer, so we add
+ * gDvm.allocRecordMax and then mask it back down.
+ */
+inline static int headIndex()
+{
+    return (gDvm.allocRecordHead+1 + gDvm.allocRecordMax - gDvm.allocRecordCount)
+            & (gDvm.allocRecordMax-1);
+}
+
+/*
+ * Dump the contents of a PointerSet full of character pointers.
+ */
+static void dumpStringTable(PointerSet* strings)
+{
+    int count = dvmPointerSetGetCount(strings);
+    int i;
+
+    for (i = 0; i < count; i++)
+        printf("  %s\n", (const char*) dvmPointerSetGetEntry(strings, i));
+}
+
+/*
+ * Get the method's source file.  If we don't know it, return "" instead
+ * of a NULL pointer.
+ */
+static const char* getMethodSourceFile(const Method* method)
+{
+    const char* fileName = dvmGetMethodSourceFile(method);
+    if (fileName == NULL)
+        fileName = "";
+    return fileName;
+}
+
+/*
+ * Generate string tables.
+ *
+ * Our source material is UTF-8 string constants from DEX files.  If we
+ * want to be thorough we can generate a hash value for each string and
+ * use the VM hash table implementation, or we can do a quick & dirty job
+ * by just maintaining a list of unique pointers.  If the same string
+ * constant appears in multiple DEX files we'll end up with duplicates,
+ * but in practice this shouldn't matter (and if it does, we can uniq-sort
+ * the result in a second pass).
+ */
+static bool populateStringTables(PointerSet* classNames,
+    PointerSet* methodNames, PointerSet* fileNames)
+{
+    int count = gDvm.allocRecordCount;
+    int idx = headIndex();
+    int classCount, methodCount, fileCount;         /* debug stats */
+
+    classCount = methodCount = fileCount = 0;
+
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+
+        dvmPointerSetAddEntry(classNames, pRec->clazz->descriptor);
+        classCount++;
+
+        int i;
+        for (i = 0; i < kMaxAllocRecordStackDepth; i++) {
+            if (pRec->stackElem[i].method == NULL)
+                break;
+
+            const Method* method = pRec->stackElem[i].method;
+            dvmPointerSetAddEntry(classNames, method->clazz->descriptor);
+            classCount++;
+            dvmPointerSetAddEntry(methodNames, method->name);
+            methodCount++;
+            dvmPointerSetAddEntry(fileNames, getMethodSourceFile(method));
+            fileCount++;
+        }
+
+        idx = (idx + 1) & (gDvm.allocRecordMax-1);
+    }
+
+    ALOGI("class %d/%d, method %d/%d, file %d/%d",
+        dvmPointerSetGetCount(classNames), classCount,
+        dvmPointerSetGetCount(methodNames), methodCount,
+        dvmPointerSetGetCount(fileNames), fileCount);
+
+    return true;
+}
+
+/*
+ * Generate the base info (i.e. everything but the string tables).
+ *
+ * This should be called twice.  On the first call, "ptr" is NULL and
+ * "baseLen" is zero.  The return value is used to allocate a buffer.
+ * On the second call, "ptr" points to a data buffer, and "baseLen"
+ * holds the value from the result of the first call.
+ *
+ * The size of the output data is returned.
+ */
+static size_t generateBaseOutput(u1* ptr, size_t baseLen,
+    const PointerSet* classNames, const PointerSet* methodNames,
+    const PointerSet* fileNames)
+{
+    u1* origPtr = ptr;
+    int count = gDvm.allocRecordCount;
+    int idx = headIndex();
+
+    if (origPtr != NULL) {
+        set1(&ptr[0], kMessageHeaderLen);
+        set1(&ptr[1], kEntryHeaderLen);
+        set1(&ptr[2], kStackFrameLen);
+        set2BE(&ptr[3], count);
+        set4BE(&ptr[5], baseLen);
+        set2BE(&ptr[9], dvmPointerSetGetCount(classNames));
+        set2BE(&ptr[11], dvmPointerSetGetCount(methodNames));
+        set2BE(&ptr[13], dvmPointerSetGetCount(fileNames));
+    }
+    ptr += kMessageHeaderLen;
+
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+
+        /* compute depth */
+        int  depth;
+        for (depth = 0; depth < kMaxAllocRecordStackDepth; depth++) {
+            if (pRec->stackElem[depth].method == NULL)
+                break;
+        }
+
+        /* output header */
+        if (origPtr != NULL) {
+            set4BE(&ptr[0], pRec->size);
+            set2BE(&ptr[4], pRec->threadId);
+            set2BE(&ptr[6],
+                dvmPointerSetFind(classNames, pRec->clazz->descriptor));
+            set1(&ptr[8], depth);
+        }
+        ptr += kEntryHeaderLen;
+
+        /* convert stack frames */
+        int i;
+        for (i = 0; i < depth; i++) {
+            if (origPtr != NULL) {
+                const Method* method = pRec->stackElem[i].method;
+                int lineNum;
+
+                lineNum = dvmLineNumFromPC(method, pRec->stackElem[i].pc);
+                if (lineNum > 32767)
+                    lineNum = 32767;
+
+                set2BE(&ptr[0], dvmPointerSetFind(classNames,
+                        method->clazz->descriptor));
+                set2BE(&ptr[2], dvmPointerSetFind(methodNames,
+                        method->name));
+                set2BE(&ptr[4], dvmPointerSetFind(fileNames,
+                        getMethodSourceFile(method)));
+                set2BE(&ptr[6], (u2)lineNum);
+            }
+            ptr += kStackFrameLen;
+        }
+
+        idx = (idx + 1) & (gDvm.allocRecordMax-1);
+    }
+
+    return ptr - origPtr;
+}
+
+/*
+ * Compute the size required to store a string table.  Includes the length
+ * word and conversion to UTF-16.
+ */
+static size_t computeStringTableSize(PointerSet* strings)
+{
+    int count = dvmPointerSetGetCount(strings);
+    size_t size = 0;
+    int i;
+
+    for (i = 0; i < count; i++) {
+        const char* str = (const char*) dvmPointerSetGetEntry(strings, i);
+
+        size += 4 + dvmUtf8Len(str) * 2;
+    }
+
+    return size;
+}
+
+/*
+ * Convert a UTF-8 string to UTF-16.  We also need to byte-swap the values
+ * to big-endian, and we can't assume even alignment on the target.
+ *
+ * Returns the string's length, in characters.
+ */
+static int convertUtf8ToUtf16BEUA(u1* utf16Str, const char* utf8Str)
+{
+    u1* origUtf16Str = utf16Str;
+
+    while (*utf8Str != '\0') {
+        u2 utf16 = dexGetUtf16FromUtf8(&utf8Str);       /* advances utf8Str */
+        set2BE(utf16Str, utf16);
+        utf16Str += 2;
+    }
+
+    return (utf16Str - origUtf16Str) / 2;
+}
+
+/*
+ * Output a string table serially.
+ */
+static size_t outputStringTable(PointerSet* strings, u1* ptr)
+{
+    int count = dvmPointerSetGetCount(strings);
+    u1* origPtr = ptr;
+    int i;
+
+    for (i = 0; i < count; i++) {
+        const char* str = (const char*) dvmPointerSetGetEntry(strings, i);
+        int charLen;
+
+        /* copy UTF-8 string to big-endian unaligned UTF-16 */
+        charLen = convertUtf8ToUtf16BEUA(&ptr[4], str);
+        set4BE(&ptr[0], charLen);
+
+        ptr += 4 + charLen * 2;
+    }
+
+    return ptr - origPtr;
+}
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen)
+{
+    bool result = false;
+    u1* buffer = NULL;
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    /*
+     * Part 1: generate string tables.
+     */
+    PointerSet* classNames = NULL;
+    PointerSet* methodNames = NULL;
+    PointerSet* fileNames = NULL;
+
+    /*
+     * Allocate storage.  Usually there's 60-120 of each thing (sampled
+     * when max=512), but it varies widely and isn't closely bound to
+     * the number of allocations we've captured.  The sets expand quickly
+     * if needed.
+     */
+    classNames = dvmPointerSetAlloc(128);
+    methodNames = dvmPointerSetAlloc(128);
+    fileNames = dvmPointerSetAlloc(128);
+    if (classNames == NULL || methodNames == NULL || fileNames == NULL) {
+        ALOGE("Failed allocating pointer sets");
+        goto bail;
+    }
+
+    if (!populateStringTables(classNames, methodNames, fileNames))
+        goto bail;
+
+    if (false) {
+        printf("Classes:\n");
+        dumpStringTable(classNames);
+        printf("Methods:\n");
+        dumpStringTable(methodNames);
+        printf("Files:\n");
+        dumpStringTable(fileNames);
+    }
+
+    /*
+     * Part 2: compute the size of the output.
+     *
+     * (Could also just write to an expanding buffer.)
+     */
+    size_t baseSize, totalSize;
+    baseSize = generateBaseOutput(NULL, 0, classNames, methodNames, fileNames);
+    assert(baseSize > 0);
+    totalSize = baseSize;
+    totalSize += computeStringTableSize(classNames);
+    totalSize += computeStringTableSize(methodNames);
+    totalSize += computeStringTableSize(fileNames);
+    ALOGI("Generated AT, size is %zd/%zd", baseSize, totalSize);
+
+    /*
+     * Part 3: allocate a buffer and generate the output.
+     */
+    u1* strPtr;
+
+    buffer = (u1*) malloc(totalSize);
+    strPtr = buffer + baseSize;
+    generateBaseOutput(buffer, baseSize, classNames, methodNames, fileNames);
+    strPtr += outputStringTable(classNames, strPtr);
+    strPtr += outputStringTable(methodNames, strPtr);
+    strPtr += outputStringTable(fileNames, strPtr);
+    if (strPtr - buffer != (int)totalSize) {
+        ALOGE("size mismatch (%d vs %zd)", strPtr - buffer, totalSize);
+        dvmAbort();
+    }
+    //dvmPrintHexDump(buffer, totalSize);
+
+    *pData = buffer;
+    *pDataLen = totalSize;
+    buffer = NULL;          // don't free -- caller will own
+    result = true;
+
+bail:
+    dvmPointerSetFree(classNames);
+    dvmPointerSetFree(methodNames);
+    dvmPointerSetFree(fileNames);
+    free(buffer);
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    //dvmDumpTrackedAllocations(false);
+    return result;
+}
+
+/*
+ * Dump the tracked allocations to the log file.
+ *
+ * If "enable" is set, we try to enable the feature if it's not already
+ * active.
+ */
+void dvmDumpTrackedAllocations(bool enable)
+{
+    if (enable)
+        dvmEnableAllocTracker();
+
+    dvmLockMutex(&gDvm.allocTrackerLock);
+    if (gDvm.allocRecords == NULL) {
+        dvmUnlockMutex(&gDvm.allocTrackerLock);
+        return;
+    }
+
+    /*
+     * "idx" is the head of the list.  We want to start at the end of the
+     * list and move forward to the tail.
+     */
+    int idx = headIndex();
+    int count = gDvm.allocRecordCount;
+
+    ALOGI("Tracked allocations, (head=%d count=%d)",
+        gDvm.allocRecordHead, count);
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+        ALOGI(" T=%-2d %6d %s",
+            pRec->threadId, pRec->size, pRec->clazz->descriptor);
+
+        if (true) {
+            for (int i = 0; i < kMaxAllocRecordStackDepth; i++) {
+                if (pRec->stackElem[i].method == NULL)
+                    break;
+
+                const Method* method = pRec->stackElem[i].method;
+                if (dvmIsNativeMethod(method)) {
+                    ALOGI("    %s.%s (Native)",
+                        method->clazz->descriptor, method->name);
+                } else {
+                    ALOGI("    %s.%s +%d",
+                        method->clazz->descriptor, method->name,
+                        pRec->stackElem[i].pc);
+                }
+            }
+        }
+
+        /* pause periodically to help logcat catch up */
+        if ((count % 5) == 0)
+            usleep(40000);
+
+        idx = (idx + 1) & (gDvm.allocRecordMax-1);
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    if (false) {
+        u1* data;
+        size_t dataLen;
+        if (dvmGenerateTrackedAllocationReport(&data, &dataLen))
+            free(data);
+    }
+}
diff --git a/vm/AllocTracker.h b/vm/AllocTracker.h
new file mode 100644
index 0000000..dede397
--- /dev/null
+++ b/vm/AllocTracker.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Allocation tracking and reporting.
+ */
+#ifndef DALVIK_ALLOCTRACKER_H_
+#define DALVIK_ALLOCTRACKER_H_
+
+/* initialization */
+bool dvmAllocTrackerStartup(void);
+void dvmAllocTrackerShutdown(void);
+
+struct AllocRecord;
+
+/*
+ * Enable allocation tracking.  Does nothing if tracking is already enabled.
+ */
+bool dvmEnableAllocTracker(void);
+
+/*
+ * Disable allocation tracking.  Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker(void);
+
+/*
+ * If allocation tracking is enabled, add a new entry to the set.
+ */
+#define dvmTrackAllocation(_clazz, _size)                                   \
+    {                                                                       \
+        if (gDvm.allocRecords != NULL)                                      \
+            dvmDoTrackAllocation(_clazz, _size);                            \
+    }
+void dvmDoTrackAllocation(ClassObject* clazz, size_t size);
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set.  "*pData"
+ * refers to newly-allocated storage that must be freed by the caller.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen);
+
+/*
+ * Dump the tracked allocations to the log file.  If "enable" is set, this
+ * will enable tracking if it's not already on.
+ */
+void dvmDumpTrackedAllocations(bool enable);
+
+#endif  // DALVIK_ALLOCTRACKER_H_
diff --git a/vm/Android.mk b/vm/Android.mk
new file mode 100644
index 0000000..3c2dd31
--- /dev/null
+++ b/vm/Android.mk
@@ -0,0 +1,165 @@
+# Copyright (C) 2008 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.
+
+#
+# Android.mk for Dalvik VM.
+#
+# This makefile builds both for host and target, and so the very large
+# swath of common definitions are factored out into a separate file to
+# minimize duplication.
+#
+# If you enable or disable optional features here (or in Dvm.mk),
+# rebuild the VM with:
+#
+#  make clean-libdvm clean-libdvm_assert clean-libdvm_sv clean-libdvm_interp
+#  make -j4 libdvm
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Build for the target (device).
+#
+
+ifeq ($(TARGET_CPU_SMP),true)
+    target_smp_flag := -DANDROID_SMP=1
+else
+    target_smp_flag := -DANDROID_SMP=0
+endif
+host_smp_flag := -DANDROID_SMP=1
+
+# Build the installed version (libdvm.so) first
+WITH_JIT := true
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+# Overwrite default settings
+LOCAL_MODULE := libdvm
+LOCAL_CFLAGS += $(target_smp_flag)
+LOCAL_LDFLAGS_x86 += -Wl,--no-fatal-warnings
+
+# Define WITH_ADDRESS_SANITIZER to build an ASan-instrumented version of the
+# library in /system/lib/asan/libdvm.so.
+ifneq ($(strip $(WITH_ADDRESS_SANITIZER)),)
+    LOCAL_MODULE_RELATIVE_PATH := asan
+    LOCAL_ADDRESS_SANITIZER := true
+    LOCAL_CFLAGS := $(filter-out $(CLANG_CONFIG_UNKNOWN_CFLAGS),$(LOCAL_CFLAGS))
+endif
+
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_SHARED_LIBRARY)
+
+# Derivation #1
+# Enable assertions and JIT tuning
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                -DWITH_JIT_TUNING $(target_smp_flag)
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+LOCAL_MODULE := libdvm_assert
+LOCAL_LDFLAGS_x86 += -Wl,--no-fatal-warnings
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_SHARED_LIBRARY)
+
+# Derivation #2
+# Enable assertions and JIT self-verification
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                -DWITH_SELF_VERIFICATION $(target_smp_flag)
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+LOCAL_MODULE := libdvm_sv
+LOCAL_LDFLAGS_x86 += -Wl,--no-fatal-warnings
+# MIPS support for self-verification is incomplete
+LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH := mips
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_SHARED_LIBRARY)
+
+# Derivation #3
+# Compile out the JIT
+WITH_JIT := false
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+LOCAL_CFLAGS += $(target_smp_flag)
+# TODO: split out the asflags.
+LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+LOCAL_MODULE := libdvm_interp
+LOCAL_LDFLAGS_x86 += -Wl,--no-fatal-warnings
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_SHARED_LIBRARY)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+    include $(CLEAR_VARS)
+
+    # Variables used in the included Dvm.mk.
+    dvm_os := $(HOST_OS)
+    dvm_arch := x86
+    # Note: HOST_ARCH_VARIANT isn't defined.
+    dvm_arch_variant := x86
+    WITH_JIT := true
+    include $(LOCAL_PATH)/Dvm.mk
+
+    LOCAL_SHARED_LIBRARIES += libnativehelper libcrypto-host libssl-host libicuuc-host libicui18n-host
+
+    LOCAL_LDLIBS := -lpthread -ldl
+    ifeq ($(HOST_OS),linux)
+      # need this for clock_gettime() in profiling
+      LOCAL_LDLIBS += -lrt
+    endif
+
+    # Build as a WHOLE static library so dependencies are available at link
+    # time. When building this target as a regular static library, certain
+    # dependencies like expat are not found by the linker.
+    LOCAL_WHOLE_STATIC_LIBRARIES += libexpat libcutils libdex liblog libz
+    LOCAL_STATIC_LIBRARIES += libutils
+
+    # The libffi from the source tree should never be used by host builds.
+    # The recommendation is that host builds should always either
+    # have sufficient custom code so that libffi isn't needed at all,
+    # or they should use the platform's provided libffi. So, if the common
+    # build rules decided to include it, axe it back out here.
+    ifneq (,$(findstring libffi,$(LOCAL_SHARED_LIBRARIES)))
+        LOCAL_SHARED_LIBRARIES := \
+            $(patsubst libffi, ,$(LOCAL_SHARED_LIBRARIES))
+    endif
+
+    LOCAL_CFLAGS += $(host_smp_flag)
+    # TODO: split out the asflags.
+    LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libdvm
+    LOCAL_32_BIT_ONLY := true
+
+    include $(BUILD_HOST_SHARED_LIBRARY)
+
+    # Copy the dalvik shell script to the host's bin directory
+    include $(CLEAR_VARS)
+    LOCAL_IS_HOST_MODULE := true
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE_CLASS := EXECUTABLES
+    LOCAL_MODULE := dalvik
+    include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dalvik | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+endif
diff --git a/vm/Atomic.cpp b/vm/Atomic.cpp
new file mode 100644
index 0000000..ccbf64a
--- /dev/null
+++ b/vm/Atomic.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#include "Dalvik.h"
+
+#include <cutils/atomic.h>
+
+#if defined(__arm__)
+#include <machine/cpu-features.h>
+#endif
+
+/*****************************************************************************/
+
+#if defined(HAVE_MACOSX_IPC)
+#define NEED_MAC_QUASI_ATOMICS 1
+
+#elif defined(__i386__) || defined(__x86_64__)
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+
+#elif defined(__mips__)
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+
+#elif defined(__arm__)
+
+// TODO: Clang can not process our inline assembly at the moment.
+#if defined(__ARM_HAVE_LDREXD) && !defined(__clang__)
+#define NEED_ARM_LDREXD_QUASI_ATOMICS 1
+#else
+#define NEED_PTHREADS_QUASI_ATOMICS 1
+#endif
+
+#else
+#error "Unsupported atomic operations for this platform"
+#endif
+
+/*****************************************************************************/
+
+#if NEED_ARM_LDREXD_QUASI_ATOMICS
+
+static inline int64_t dvmQuasiAtomicSwap64Body(int64_t newvalue,
+                                               volatile int64_t* addr)
+{
+    int64_t prev;
+    int status;
+    do {
+        __asm__ __volatile__ ("@ dvmQuasiAtomicSwap64\n"
+            "ldrexd     %0, %H0, [%3]\n"
+            "strexd     %1, %4, %H4, [%3]"
+            : "=&r" (prev), "=&r" (status), "+m"(*addr)
+            : "r" (addr), "r" (newvalue)
+            : "cc");
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t newvalue, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64Body(newvalue, addr);
+}
+
+int64_t dvmQuasiAtomicSwap64Sync(int64_t newvalue, volatile int64_t* addr)
+{
+    int64_t prev;
+    ANDROID_MEMBAR_STORE();
+    prev = dvmQuasiAtomicSwap64Body(newvalue, addr);
+    ANDROID_MEMBAR_FULL();
+    return prev;
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int64_t prev;
+    int status;
+    do {
+        __asm__ __volatile__ ("@ dvmQuasiAtomicCas64\n"
+            "ldrexd     %0, %H0, [%3]\n"
+            "mov        %1, #0\n"
+            "teq        %0, %4\n"
+            "teqeq      %H0, %H4\n"
+            "strexdeq   %1, %5, %H5, [%3]"
+            : "=&r" (prev), "=&r" (status), "+m"(*addr)
+            : "r" (addr), "Ir" (oldvalue), "r" (newvalue)
+            : "cc");
+    } while (__builtin_expect(status != 0, 0));
+    return prev != oldvalue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    int64_t value;
+    __asm__ __volatile__ ("@ dvmQuasiAtomicRead64\n"
+        "ldrexd     %0, %H0, [%1]"
+        : "=&r" (value)
+        : "r" (addr));
+    return value;
+}
+#endif
+
+/*****************************************************************************/
+
+#if NEED_MAC_QUASI_ATOMICS
+
+#include <libkern/OSAtomic.h>
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+            (int64_t*)addr) == 0;
+}
+
+
+static inline int64_t dvmQuasiAtomicSwap64Body(int64_t value,
+                                               volatile int64_t* addr)
+{
+    int64_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (dvmQuasiAtomicCas64(oldValue, value, addr));
+    return oldValue;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64Body(value, addr);
+}
+
+int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    ANDROID_MEMBAR_STORE();
+    oldValue = dvmQuasiAtomicSwap64Body(value, addr);
+    /* TUNING: barriers can be avoided on some architectures */
+    ANDROID_MEMBAR_FULL();
+    return oldValue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    return OSAtomicAdd64Barrier(0, addr);
+}
+#endif
+
+/*****************************************************************************/
+
+#if NEED_PTHREADS_QUASI_ATOMICS
+
+// In the absence of a better implementation, we implement the 64-bit atomic
+// operations through mutex locking.
+
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+
+#include <pthread.h>
+
+static const size_t kSwapLockCount = 32;
+static pthread_mutex_t* gSwapLocks[kSwapLockCount];
+
+void dvmQuasiAtomicsStartup() {
+    for (size_t i = 0; i < kSwapLockCount; ++i) {
+        pthread_mutex_t* m = new pthread_mutex_t;
+        dvmInitMutex(m);
+        gSwapLocks[i] = m;
+    }
+}
+
+void dvmQuasiAtomicsShutdown() {
+    for (size_t i = 0; i < kSwapLockCount; ++i) {
+        pthread_mutex_t* m = gSwapLocks[i];
+        gSwapLocks[i] = NULL;
+        if (m != NULL) {
+            dvmDestroyMutex(m);
+        }
+        delete m;
+    }
+}
+
+static inline pthread_mutex_t* GetSwapLock(const volatile int64_t* addr) {
+    return gSwapLocks[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+
+    oldValue = *addr;
+    *addr    = value;
+
+    pthread_mutex_unlock(lock);
+    return oldValue;
+}
+
+/* Same as dvmQuasiAtomicSwap64 - mutex handles barrier */
+int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
+{
+    return dvmQuasiAtomicSwap64(value, addr);
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int result;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+
+    if (*addr == oldvalue) {
+        *addr  = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    int64_t result;
+    pthread_mutex_t* lock = GetSwapLock(addr);
+
+    pthread_mutex_lock(lock);
+    result = *addr;
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+#else
+
+// The other implementations don't need any special setup.
+void dvmQuasiAtomicsStartup() {}
+void dvmQuasiAtomicsShutdown() {}
+
+#endif /*NEED_PTHREADS_QUASI_ATOMICS*/
diff --git a/vm/Atomic.h b/vm/Atomic.h
new file mode 100644
index 0000000..becbeeb
--- /dev/null
+++ b/vm/Atomic.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Atomic operations
+ */
+#ifndef DALVIK_ATOMIC_H_
+#define DALVIK_ATOMIC_H_
+
+#include <cutils/atomic.h>          /* use common Android atomic ops */
+#include <cutils/atomic-inline.h>   /* and some uncommon ones */
+
+void dvmQuasiAtomicsStartup();
+void dvmQuasiAtomicsShutdown();
+
+/*
+ * NOTE: Two "quasiatomic" operations on the exact same memory address
+ * are guaranteed to operate atomically with respect to each other,
+ * but no guarantees are made about quasiatomic operations mixed with
+ * non-quasiatomic operations on the same address, nor about
+ * quasiatomic operations that are performed on partially-overlapping
+ * memory.
+ *
+ * Only the "Sync" versions of these provide a memory barrier.
+ */
+
+/*
+ * Swap the 64-bit value at "addr" with "value".  Returns the previous
+ * value.
+ */
+extern "C" int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr);
+
+/*
+ * Swap the 64-bit value at "addr" with "value".  Returns the previous
+ * value.  Provides memory barriers.
+ */
+extern "C" int64_t dvmQuasiAtomicSwap64Sync(int64_t value,
+                                            volatile int64_t* addr);
+
+/*
+ * Read the 64-bit value at "addr".
+ */
+extern "C" int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr);
+
+/*
+ * If the value at "addr" is equal to "oldvalue", replace it with "newvalue"
+ * and return 0.  Otherwise, don't swap, and return nonzero.
+ */
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr);
+
+#endif  // DALVIK_ATOMIC_H_
diff --git a/vm/AtomicCache.cpp b/vm/AtomicCache.cpp
new file mode 100644
index 0000000..6fbbc12
--- /dev/null
+++ b/vm/AtomicCache.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Mutex-free cache.  Each entry has two 32-bit keys, one 32-bit value,
+ * and a 32-bit version.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1.  If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x)  (x)
+//#define BOOL_TO_INT(x)  ((x) ? 1 : 0)
+
+#define CPU_CACHE_WIDTH         32
+#define CPU_CACHE_WIDTH_1       (CPU_CACHE_WIDTH-1)
+
+#define ATOMIC_LOCK_FLAG        (1 << 31)
+
+/*
+ * Allocate cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries)
+{
+    AtomicCache* newCache;
+
+    newCache = (AtomicCache*) calloc(1, sizeof(AtomicCache));
+    if (newCache == NULL)
+        return NULL;
+
+    newCache->numEntries = numEntries;
+
+    newCache->entryAlloc = calloc(1,
+        sizeof(AtomicCacheEntry) * numEntries + CPU_CACHE_WIDTH);
+    if (newCache->entryAlloc == NULL) {
+        free(newCache);
+        return NULL;
+    }
+
+    /*
+     * Adjust storage to align on a 32-byte boundary.  Each entry is 16 bytes
+     * wide.  This ensures that each cache entry sits on a single CPU cache
+     * line.
+     */
+    assert(sizeof(AtomicCacheEntry) == 16);
+    newCache->entries = (AtomicCacheEntry*)
+        (((int) newCache->entryAlloc + CPU_CACHE_WIDTH_1) & ~CPU_CACHE_WIDTH_1);
+
+    return newCache;
+}
+
+/*
+ * Free cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache)
+{
+    if (cache != NULL) {
+        free(cache->entryAlloc);
+        free(cache);
+    }
+}
+
+
+/*
+ * Update a cache entry.
+ *
+ * In the event of a collision with another thread, the update may be skipped.
+ *
+ * We only need "pCache" for stats.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+    u4 firstVersion
+#if CALC_CACHE_STATS > 0
+    , AtomicCache* pCache
+#endif
+    )
+{
+    /*
+     * The fields don't match, so we want to update them.  There is a risk
+     * that another thread is also trying to update them, so we grab an
+     * ownership flag to lock out other threads.
+     *
+     * If the lock flag was already set in "firstVersion", somebody else
+     * was in mid-update, and we don't want to continue here.  (This means
+     * that using "firstVersion" as the "before" argument to the CAS would
+     * succeed when it shouldn't and vice-versa -- we could also just pass
+     * in (firstVersion & ~ATOMIC_LOCK_FLAG) as the first argument.)
+     *
+     * NOTE: we don't deal with the situation where we overflow the version
+     * counter and trample the ATOMIC_LOCK_FLAG (at 2^31).  Probably not
+     * a real concern.
+     */
+    if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
+        android_atomic_release_cas(
+                firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
+                (volatile s4*) &pEntry->version) != 0)
+    {
+        /*
+         * We couldn't get the write lock.  Return without updating the table.
+         */
+#if CALC_CACHE_STATS > 0
+        pCache->fail++;
+#endif
+        return;
+    }
+
+    /* must be even-valued on entry */
+    assert((firstVersion & 0x01) == 0);
+
+#if CALC_CACHE_STATS > 0
+    /* for stats, assume a key value of zero indicates an empty entry */
+    if (pEntry->key1 == 0)
+        pCache->fills++;
+    else
+        pCache->misses++;
+#endif
+
+    /*
+     * We have the write lock, but somebody could be reading this entry
+     * while we work.  We use memory barriers to ensure that the state
+     * is always consistent when the version number is even.
+     */
+    u4 newVersion = (firstVersion | ATOMIC_LOCK_FLAG) + 1;
+    assert((newVersion & 0x01) == 1);
+
+    pEntry->version = newVersion;
+
+    android_atomic_release_store(key1, (int32_t*) &pEntry->key1);
+    pEntry->key2 = key2;
+    pEntry->value = value;
+
+    newVersion++;
+    android_atomic_release_store(newVersion, (int32_t*) &pEntry->version);
+
+    /*
+     * Clear the lock flag.  Nobody else should have been able to modify
+     * pEntry->version, so if this fails the world is broken.
+     */
+    assert(newVersion == ((firstVersion + 2) | ATOMIC_LOCK_FLAG));
+    if (android_atomic_release_cas(
+            newVersion, newVersion & ~ATOMIC_LOCK_FLAG,
+            (volatile s4*) &pEntry->version) != 0)
+    {
+        //ALOGE("unable to reset the instanceof cache ownership");
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Dump the "instanceof" cache stats.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache)
+{
+    if (pCache == NULL)
+        return;
+    dvmFprintf(stdout,
+        "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n",
+        pCache->trivial, pCache->fail, pCache->hits,
+        pCache->misses, pCache->fills,
+        (pCache->hits == 0) ? 0 :
+            pCache->hits * 100 /
+                (pCache->fail + pCache->hits + pCache->misses + pCache->fills),
+        pCache->numEntries);
+}
diff --git a/vm/AtomicCache.h b/vm/AtomicCache.h
new file mode 100644
index 0000000..42ba6be
--- /dev/null
+++ b/vm/AtomicCache.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Mutex-free cache for key1+key2=value.
+ */
+#ifndef DALVIK_ATOMICCACHE_H_
+#define DALVIK_ATOMICCACHE_H_
+
+/*
+ * If set to "1", gather some stats on our caching success rate.
+ */
+#define CALC_CACHE_STATS 0
+
+
+/*
+ * One entry in the cache.  We store two keys (e.g. the classes that are
+ * arguments to "instanceof") and one result (e.g. a boolean value).
+ *
+ * Must be exactly 16 bytes.
+ */
+struct AtomicCacheEntry {
+    u4          key1;
+    u4          key2;
+    u4          value;
+    volatile u4 version;    /* version and lock flag */
+};
+
+/*
+ * One cache.
+ *
+ * Thought: we might be able to save a few cycles by storing the cache
+ * struct and "entries" separately, avoiding an indirection.  (We already
+ * handle "numEntries" separately in ATOMIC_CACHE_LOOKUP.)
+ */
+struct AtomicCache {
+    AtomicCacheEntry*   entries;        /* array of entries */
+    int         numEntries;             /* #of entries, must be power of 2 */
+
+    void*       entryAlloc;             /* memory allocated for entries */
+
+    /* cache stats; note we don't guarantee atomic increments for these */
+    int         trivial;                /* cache access not required */
+    int         fail;                   /* contention failure */
+    int         hits;                   /* found entry in cache */
+    int         misses;                 /* entry was for other keys */
+    int         fills;                  /* entry was empty */
+};
+
+/*
+ * Do a cache lookup.  We need to be able to read and write entries
+ * atomically.  There are a couple of ways to do this:
+ *  (1) Have a global lock.  A mutex is too heavy, so instead we would use
+ *      an atomic flag.  If the flag is set, we could sit and spin,
+ *      but if we're a high-priority thread that may cause a lockup.
+ *      Better to just ignore the cache and do the full computation.
+ *  (2) Have a "version" that gets incremented atomically when a write
+ *      begins and again when it completes.  Compare the version before
+ *      and after doing reads.  So long as we have memory barriers in the
+ *      right place the compiler and CPU will do the right thing, allowing
+ *      us to skip atomic ops in the common read case.  The table updates
+ *      are expensive, requiring two writes with barriers.  We also need
+ *      some sort of lock to ensure that nobody else tries to start an
+ *      update while we're in the middle of one.
+ *
+ * We expect a 95+% hit rate for the things we use this for, so #2 is
+ * much better than #1.
+ *
+ * _cache is an AtomicCache*
+ * _cacheSize is _cache->cacheSize (can save a cycle avoiding the lookup)
+ * _key1, _key2 are the keys
+ *
+ * Define a function ATOMIC_CACHE_CALC that returns a 32-bit value.  This
+ * will be invoked when we need to compute the value.
+ *
+ * Returns the value.
+ */
+#if CALC_CACHE_STATS > 0
+# define CACHE_XARG(_value) ,_value
+#else
+# define CACHE_XARG(_value)
+#endif
+#define ATOMIC_CACHE_LOOKUP(_cache, _cacheSize, _key1, _key2) ({            \
+    AtomicCacheEntry* pEntry;                                               \
+    int hash;                                                               \
+    u4 firstVersion, secondVersion;                                         \
+    u4 value;                                                               \
+                                                                            \
+    /* simple hash function */                                              \
+    hash = (((u4)(_key1) >> 2) ^ (u4)(_key2)) & ((_cacheSize)-1);           \
+    pEntry = (_cache)->entries + hash;                                      \
+                                                                            \
+    firstVersion = android_atomic_acquire_load((int32_t*)&pEntry->version); \
+                                                                            \
+    if (pEntry->key1 == (u4)(_key1) && pEntry->key2 == (u4)(_key2)) {       \
+        /*                                                                  \
+         * The fields match.  Get the value, then read the version a        \
+         * second time to verify that we didn't catch a partial update.     \
+         * We're also hosed if "firstVersion" was odd, indicating that      \
+         * an update was in progress before we got here (unlikely).         \
+         */                                                                 \
+        value = android_atomic_acquire_load((int32_t*) &pEntry->value);     \
+        secondVersion = pEntry->version;                                    \
+                                                                            \
+        if ((firstVersion & 0x01) != 0 || firstVersion != secondVersion) {  \
+            /*                                                              \
+             * We clashed with another thread.  Instead of sitting and      \
+             * spinning, which might not complete if we're a high priority  \
+             * thread, just do the regular computation.                     \
+             */                                                             \
+            if (CALC_CACHE_STATS)                                           \
+                (_cache)->fail++;                                           \
+            value = (u4) ATOMIC_CACHE_CALC;                                 \
+        } else {                                                            \
+            /* all good */                                                  \
+            if (CALC_CACHE_STATS)                                           \
+                (_cache)->hits++;                                           \
+        }                                                                   \
+    } else {                                                                \
+        /*                                                                  \
+         * Compute the result and update the cache.  We really want this    \
+         * to happen in a different method -- it makes the ARM frame        \
+         * setup for this method simpler, which gives us a ~10% speed       \
+         * boost.                                                           \
+         */                                                                 \
+        value = (u4) ATOMIC_CACHE_CALC;                                     \
+        if (value == 0 && ATOMIC_CACHE_NULL_ALLOWED) { \
+            dvmUpdateAtomicCache((u4) (_key1), (u4) (_key2), value, pEntry, \
+                        firstVersion CACHE_XARG(_cache) ); \
+        } \
+    }                                                                       \
+    value;                                                                  \
+})
+
+/*
+ * Allocate a cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries);
+
+/*
+ * Free a cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache);
+
+/*
+ * Update a cache entry.
+ *
+ * Making the last argument optional, instead of merely unused, saves us
+ * a few percent in the ATOMIC_CACHE_LOOKUP time.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+    u4 firstVersion
+#if CALC_CACHE_STATS > 0
+    , AtomicCache* pCache
+#endif
+    );
+
+/*
+ * Debugging.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache);
+
+#endif  // DALVIK_ATOMICCACHE_H_
diff --git a/vm/BitVector.cpp b/vm/BitVector.cpp
new file mode 100644
index 0000000..8b1d141
--- /dev/null
+++ b/vm/BitVector.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Implementation of an expandable bit vector.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <strings.h>
+
+#define kBitVectorGrowth    4   /* increase by 4 u4s when limit hit */
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
+{
+    BitVector* bv;
+    unsigned int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+
+    bv = (BitVector*) malloc(sizeof(BitVector));
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->expandable = expandable;
+    bv->storage = (u4*) calloc(count, sizeof(u4));
+    return bv;
+}
+
+/*
+ * Free a BitVector.
+ */
+void dvmFreeBitVector(BitVector* pBits)
+{
+    if (pBits == NULL)
+        return;
+
+    free(pBits->storage);
+    free(pBits);
+}
+
+/*
+ * "Allocate" the first-available bit in the bitmap.
+ *
+ * This is not synchronized.  The caller is expected to hold some sort of
+ * lock that prevents multiple threads from executing simultaneously in
+ * dvmAllocBit/dvmFreeBit.
+ */
+int dvmAllocBit(BitVector* pBits)
+{
+    unsigned int word, bit;
+
+retry:
+    for (word = 0; word < pBits->storageSize; word++) {
+        if (pBits->storage[word] != 0xffffffff) {
+            /*
+             * There are unallocated bits in this word.  Return the first.
+             */
+            bit = ffs(~(pBits->storage[word])) -1;
+            assert(bit < 32);
+            pBits->storage[word] |= 1 << bit;
+            return (word << 5) | bit;
+        }
+    }
+
+    /*
+     * Ran out of space, allocate more if we're allowed to.
+     */
+    if (!pBits->expandable)
+        return -1;
+
+    pBits->storage = (u4*)realloc(pBits->storage,
+                    (pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
+    memset(&pBits->storage[pBits->storageSize], 0x00,
+        kBitVectorGrowth * sizeof(u4));
+    pBits->storageSize += kBitVectorGrowth;
+    goto retry;
+}
+
+/*
+ * Mark the specified bit as "set".
+ */
+void dvmSetBit(BitVector* pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        if (!pBits->expandable) {
+            ALOGE("Attempt to set bit outside valid range (%d, limit is %d)",
+                num, pBits->storageSize * sizeof(u4) * 8);
+            dvmAbort();
+        }
+
+        /* Round up to word boundaries for "num+1" bits */
+        unsigned int newSize = (num + 1 + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        pBits->storage = (u4*)realloc(pBits->storage, newSize * sizeof(u4));
+        if (pBits->storage == NULL) {
+            ALOGE("BitVector expansion to %d failed", newSize * sizeof(u4));
+            dvmAbort();
+        }
+        memset(&pBits->storage[pBits->storageSize], 0x00,
+            (newSize - pBits->storageSize) * sizeof(u4));
+        pBits->storageSize = newSize;
+    }
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+}
+
+/*
+ * Mark the specified bit as "clear".
+ */
+void dvmClearBit(BitVector* pBits, unsigned int num)
+{
+    assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+    pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+}
+
+/*
+ * Mark all bits bit as "clear".
+ */
+void dvmClearAllBits(BitVector* pBits)
+{
+    unsigned int count = pBits->storageSize;
+    memset(pBits->storage, 0, count * sizeof(u4));
+}
+
+/*
+ * Mark specified number of bits as "set". Cannot set all bits like ClearAll
+ * since there might be unused bits - setting those to one will confuse the
+ * iterator.
+ */
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits)
+{
+    unsigned int idx;
+    assert(((numBits + 31) >> 5) <= pBits->storageSize);
+    for (idx = 0; idx < (numBits >> 5); idx++) {
+        pBits->storage[idx] = -1;
+    }
+    unsigned int remNumBits = numBits & 0x1f;
+    if (remNumBits) {
+        pBits->storage[idx] = (1 << remNumBits) - 1;
+    }
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num)
+{
+    assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+    unsigned int val = pBits->storage[num >> 5] & (1 << (num & 0x1f));
+    return (val != 0);
+}
+
+/*
+ * Count the number of bits that are set.
+ */
+int dvmCountSetBits(const BitVector* pBits)
+{
+    unsigned int word;
+    unsigned int count = 0;
+
+    for (word = 0; word < pBits->storageSize; word++) {
+        u4 val = pBits->storage[word];
+
+        if (val != 0) {
+            if (val == 0xffffffff) {
+                count += 32;
+            } else {
+                /* count the number of '1' bits */
+                while (val != 0) {
+                    val &= val - 1;
+                    count++;
+                }
+            }
+        }
+    }
+
+    return count;
+}
+
+/*
+ * If the vector sizes don't match, log an error and abort.
+ */
+static void checkSizes(const BitVector* bv1, const BitVector* bv2)
+{
+    if (bv1->storageSize != bv2->storageSize) {
+        ALOGE("Mismatched vector sizes (%d, %d)",
+            bv1->storageSize, bv2->storageSize);
+        dvmAbort();
+    }
+}
+
+/*
+ * Copy a whole vector to the other. Only do that when the both vectors have
+ * the same size.
+ */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src)
+{
+    /* if dest is expandable and < src, we could expand dest to match */
+    checkSizes(dest, src);
+
+    memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
+}
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+                            const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable)
+        return false;
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] = src1->storage[idx] & src2->storage[idx];
+    }
+    return true;
+}
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+                        const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable)
+        return false;
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] = src1->storage[idx] | src2->storage[idx];
+    }
+    return true;
+}
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2)
+{
+    if (src1->storageSize != src2->storageSize ||
+        src1->expandable != src2->expandable)
+        return true;
+
+    unsigned int idx;
+    for (idx = 0; idx < src1->storageSize; idx++) {
+        if (src1->storage[idx] != src2->storage[idx]) return true;
+    }
+    return false;
+}
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator)
+{
+    iterator->pBits = pBits;
+    iterator->bitSize = pBits->storageSize * sizeof(u4) * 8;
+    iterator->idx = 0;
+}
+
+/* Return the next position set to 1. -1 means end-of-element reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator)
+{
+    const BitVector* pBits = iterator->pBits;
+    u4 bitIndex = iterator->idx;
+
+    assert(iterator->bitSize == pBits->storageSize * sizeof(u4) * 8);
+    if (bitIndex >= iterator->bitSize) return -1;
+
+    for (; bitIndex < iterator->bitSize; bitIndex++) {
+        unsigned int wordIndex = bitIndex >> 5;
+        unsigned int mask = 1 << (bitIndex & 0x1f);
+        if (pBits->storage[wordIndex] & mask) {
+            iterator->idx = bitIndex+1;
+            return bitIndex;
+        }
+    }
+    /* No more set bits */
+    return -1;
+}
+
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur.  This is a logical OR.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src)
+{
+    bool changed = false;
+
+    checkSizes(dst, src);
+
+    unsigned int idx;
+    for (idx = 0; idx < dst->storageSize; idx++) {
+        u4 merged = src->storage[idx] | dst->storage[idx];
+        if (dst->storage[idx] != merged) {
+            dst->storage[idx] = merged;
+            changed = true;
+        }
+    }
+
+    return changed;
+}
diff --git a/vm/BitVector.h b/vm/BitVector.h
new file mode 100644
index 0000000..2ac0ddf
--- /dev/null
+++ b/vm/BitVector.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef DALVIK_BITVECTOR_H_
+#define DALVIK_BITVECTOR_H_
+
+/*
+ * Expanding bitmap, used for tracking resources.  Bits are numbered starting
+ * from zero.
+ *
+ * All operations on a BitVector are unsynchronized.
+ */
+struct BitVector {
+    bool    expandable;     /* expand bitmap if we run out? */
+    u4      storageSize;    /* current size, in 32-bit words */
+    u4*     storage;
+};
+
+/* Handy iterator to walk through the bit positions set to 1 */
+struct BitVectorIterator {
+    BitVector *pBits;
+    u4 idx;
+    u4 bitSize;
+};
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable);
+void dvmFreeBitVector(BitVector* pBits);
+
+/*
+ * dvmAllocBit always allocates the first possible bit.  If we run out of
+ * space in the bitmap, and it's not marked expandable, dvmAllocBit
+ * returns -1.
+ *
+ * dvmSetBit sets the specified bit, expanding the vector if necessary
+ * (and possible).  Attempting to set a bit past the limit of a non-expandable
+ * bit vector will cause a fatal error.
+ *
+ * dvmSetInitialBits sets all bits in [0..numBits-1]. Won't expand the vector.
+ *
+ * dvmIsBitSet returns "true" if the bit is set.
+ */
+int dvmAllocBit(BitVector* pBits);
+void dvmSetBit(BitVector* pBits, unsigned int num);
+void dvmClearBit(BitVector* pBits, unsigned int num);
+void dvmClearAllBits(BitVector* pBits);
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits);
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num);
+
+/* count the number of bits that have been set */
+int dvmCountSetBits(const BitVector* pBits);
+
+/* copy one vector to another of equal size */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src);
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+                            const BitVector *src2);
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+                        const BitVector *src2);
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src);
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2);
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator);
+
+/* Return the next position set to 1. -1 means end-of-vector reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator);
+
+#endif  // DALVIK_BITVECTOR_H_
diff --git a/vm/Bits.h b/vm/Bits.h
new file mode 100644
index 0000000..04d4fd1
--- /dev/null
+++ b/vm/Bits.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Some handy functions for manipulating bits and bytes.
+ *
+ * These get inlined, so prefer small size over maximum speed.
+ */
+#ifndef DALVIK_BITS_H_
+#define DALVIK_BITS_H_
+
+#include "Common.h"
+#include "Inlines.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Get 1 byte.  (Included to make the code more legible.)
+ */
+INLINE u1 get1(unsigned const char* pSrc)
+{
+    return *pSrc;
+}
+
+/*
+ * Get 2 big-endian bytes.
+ */
+INLINE u2 get2BE(unsigned char const* pSrc)
+{
+    return (pSrc[0] << 8) | pSrc[1];
+}
+
+/*
+ * Get 4 big-endian bytes.
+ */
+INLINE u4 get4BE(unsigned char const* pSrc)
+{
+    return (pSrc[0] << 24) | (pSrc[1] << 16) | (pSrc[2] << 8) | pSrc[3];
+}
+
+/*
+ * Get 8 big-endian bytes.
+ */
+INLINE u8 get8BE(unsigned char const* pSrc)
+{
+    u4 low, high;
+
+    high = pSrc[0];
+    high = (high << 8) | pSrc[1];
+    high = (high << 8) | pSrc[2];
+    high = (high << 8) | pSrc[3];
+    low = pSrc[4];
+    low = (low << 8) | pSrc[5];
+    low = (low << 8) | pSrc[6];
+    low = (low << 8) | pSrc[7];
+
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+INLINE u2 get2LE(unsigned char const* pSrc)
+{
+    return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+INLINE u4 get4LE(unsigned char const* pSrc)
+{
+    u4 result;
+
+    result = pSrc[0];
+    result |= pSrc[1] << 8;
+    result |= pSrc[2] << 16;
+    result |= pSrc[3] << 24;
+
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes.
+ */
+INLINE u8 get8LE(unsigned char const* pSrc)
+{
+    u4 low, high;
+
+    low = pSrc[0];
+    low |= pSrc[1] << 8;
+    low |= pSrc[2] << 16;
+    low |= pSrc[3] << 24;
+    high = pSrc[4];
+    high |= pSrc[5] << 8;
+    high |= pSrc[6] << 16;
+    high |= pSrc[7] << 24;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 1 byte and advance the data pointer.
+ */
+INLINE u1 read1(unsigned const char** ppSrc)
+{
+    return *(*ppSrc)++;
+}
+
+/*
+ * Grab 2 big-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+
+    *ppSrc = pSrc + 2;
+    return pSrc[0] << 8 | pSrc[1];
+}
+
+/*
+ * Grab 4 big-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 result;
+
+    result = pSrc[0] << 24;
+    result |= pSrc[1] << 16;
+    result |= pSrc[2] << 8;
+    result |= pSrc[3];
+
+    *ppSrc = pSrc + 4;
+    return result;
+}
+
+/*
+ * Get 8 big-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8BE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 low, high;
+
+    high = pSrc[0];
+    high = (high << 8) | pSrc[1];
+    high = (high << 8) | pSrc[2];
+    high = (high << 8) | pSrc[3];
+    low = pSrc[4];
+    low = (low << 8) | pSrc[5];
+    low = (low << 8) | pSrc[6];
+    low = (low << 8) | pSrc[7];
+
+    *ppSrc = pSrc + 8;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 2 little-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    *ppSrc = pSrc + 2;
+    return pSrc[0] | pSrc[1] << 8;
+}
+
+/*
+ * Grab 4 little-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 result;
+
+    result = pSrc[0];
+    result |= pSrc[1] << 8;
+    result |= pSrc[2] << 16;
+    result |= pSrc[3] << 24;
+
+    *ppSrc = pSrc + 4;
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8LE(unsigned char const** ppSrc)
+{
+    const unsigned char* pSrc = *ppSrc;
+    u4 low, high;
+
+    low = pSrc[0];
+    low |= pSrc[1] << 8;
+    low |= pSrc[2] << 16;
+    low |= pSrc[3] << 24;
+    high = pSrc[4];
+    high |= pSrc[5] << 8;
+    high |= pSrc[6] << 16;
+    high |= pSrc[7] << 24;
+
+    *ppSrc = pSrc + 8;
+    return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Skip over a UTF-8 string (preceded by a 4-byte length).
+ */
+INLINE void skipUtf8String(unsigned char const** ppSrc)
+{
+    u4 length = read4BE(ppSrc);
+
+    (*ppSrc) += length;
+}
+
+/*
+ * Read a UTF-8 string into a fixed-size buffer, and null-terminate it.
+ *
+ * Returns the length of the original string.
+ */
+INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen)
+{
+    u4 length = read4BE(ppSrc);
+    size_t copyLen = (length < bufLen) ? length : bufLen-1;
+
+    memcpy(buf, *ppSrc, copyLen);
+    buf[copyLen] = '\0';
+
+    (*ppSrc) += length;
+    return length;
+}
+
+/*
+ * Read a UTF-8 string into newly-allocated storage, and null-terminate it.
+ *
+ * Returns the string and its length.  (The latter is probably unnecessary
+ * for the way we're using UTF8.)
+ */
+INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength)
+{
+    u4 length = read4BE(ppSrc);
+    char* buf;
+
+    buf = (char*) malloc(length+1);
+
+    memcpy(buf, *ppSrc, length);
+    buf[length] = '\0';
+
+    (*ppSrc) += length;
+
+    *pLength = length;
+    return buf;
+}
+
+
+/*
+ * Set 1 byte.  (Included to make code more consistent/legible.)
+ */
+INLINE void set1(u1* buf, u1 val)
+{
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 2 big-endian bytes.
+ */
+INLINE void set2BE(u1* buf, u2 val)
+{
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 4 big-endian bytes.
+ */
+INLINE void set4BE(u1* buf, u4 val)
+{
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 8 big-endian bytes.
+ */
+INLINE void set8BE(u1* buf, u8 val)
+{
+    *buf++ = (u1)(val >> 56);
+    *buf++ = (u1)(val >> 48);
+    *buf++ = (u1)(val >> 40);
+    *buf++ = (u1)(val >> 32);
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 8);
+    *buf = (u1)(val);
+}
+
+/*
+ * Set 2 little-endian bytes.
+ */
+INLINE void set2LE(u1* buf, u2 val)
+{
+    *buf++ = (u1)(val);
+    *buf = (u1)(val >> 8);
+}
+
+/*
+ * Set 4 little-endian bytes.
+ */
+INLINE void set4LE(u1* buf, u4 val)
+{
+    *buf++ = (u1)(val);
+    *buf++ = (u1)(val >> 8);
+    *buf++ = (u1)(val >> 16);
+    *buf = (u1)(val >> 24);
+}
+
+/*
+ * Set 8 little-endian bytes.
+ */
+INLINE void set8LE(u1* buf, u8 val)
+{
+    *buf++ = (u1)(val);
+    *buf++ = (u1)(val >> 8);
+    *buf++ = (u1)(val >> 16);
+    *buf++ = (u1)(val >> 24);
+    *buf++ = (u1)(val >> 32);
+    *buf++ = (u1)(val >> 40);
+    *buf++ = (u1)(val >> 48);
+    *buf = (u1)(val >> 56);
+}
+
+/*
+ * Stuff a UTF-8 string into the buffer.
+ */
+INLINE void setUtf8String(u1* buf, const u1* str)
+{
+    u4 strLen = strlen((const char*)str);
+
+    set4BE(buf, strLen);
+    memcpy(buf + sizeof(u4), str, strLen);
+}
+
+#endif  // DALVIK_BITS_H_
diff --git a/vm/CheckJni.cpp b/vm/CheckJni.cpp
new file mode 100644
index 0000000..dac242a
--- /dev/null
+++ b/vm/CheckJni.cpp
@@ -0,0 +1,2364 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Support for -Xcheck:jni (the "careful" version of the JNI interfaces).
+ *
+ * We want to verify types, make sure class and field IDs are valid, and
+ * ensure that JNI's semantic expectations are being met.  JNI seems to
+ * be relatively lax when it comes to requirements for permission checks,
+ * e.g. access to private methods is generally allowed from anywhere.
+ */
+
+#include "Dalvik.h"
+#include "JniInternal.h"
+
+#include <sys/mman.h>
+#include <zlib.h>
+
+/*
+ * Abort if we are configured to bail out on JNI warnings.
+ */
+static void abortMaybe() {
+    if (!gDvmJni.warnOnly) {
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI call bridge wrapper
+ * ===========================================================================
+ */
+
+/*
+ * Check the result of a native method call that returns an object reference.
+ *
+ * The primary goal here is to verify that native code is returning the
+ * correct type of object.  If it's declared to return a String but actually
+ * returns a byte array, things will fail in strange ways later on.
+ *
+ * This can be a fairly expensive operation, since we have to look up the
+ * return type class by name in method->clazz' class loader.  We take a
+ * shortcut here and allow the call to succeed if the descriptor strings
+ * match.  This will allow some false-positives when a class is redefined
+ * by a class loader, but that's rare enough that it doesn't seem worth
+ * testing for.
+ *
+ * At this point, pResult->l has already been converted to an object pointer.
+ */
+static void checkCallResultCommon(const u4* args, const JValue* pResult,
+        const Method* method, Thread* self)
+{
+    assert(pResult->l != NULL);
+    const Object* resultObj = (const Object*) pResult->l;
+
+    if (resultObj == kInvalidIndirectRefObject) {
+        ALOGW("JNI WARNING: invalid reference returned from native code");
+        const Method* method = dvmGetCurrentJNIMethod();
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("             in %s.%s:%s", method->clazz->descriptor, method->name, desc);
+        free(desc);
+        abortMaybe();
+        return;
+    }
+
+    ClassObject* objClazz = resultObj->clazz;
+
+    /*
+     * Make sure that pResult->l is an instance of the type this
+     * method was expected to return.
+     */
+    const char* declType = dexProtoGetReturnType(&method->prototype);
+    const char* objType = objClazz->descriptor;
+    if (strcmp(declType, objType) == 0) {
+        /* names match; ignore class loader issues and allow it */
+        ALOGV("Check %s.%s: %s io %s (FAST-OK)",
+            method->clazz->descriptor, method->name, objType, declType);
+    } else {
+        /*
+         * Names didn't match.  We need to resolve declType in the context
+         * of method->clazz->classLoader, and compare the class objects
+         * for equality.
+         *
+         * Since we're returning an instance of declType, it's safe to
+         * assume that it has been loaded and initialized (or, for the case
+         * of an array, generated).  However, the current class loader may
+         * not be listed as an initiating loader, so we can't just look for
+         * it in the loaded-classes list.
+         */
+        ClassObject* declClazz = dvmFindClassNoInit(declType, method->clazz->classLoader);
+        if (declClazz == NULL) {
+            ALOGW("JNI WARNING: method declared to return '%s' returned '%s'",
+                declType, objType);
+            ALOGW("             failed in %s.%s ('%s' not found)",
+                method->clazz->descriptor, method->name, declType);
+            abortMaybe();
+            return;
+        }
+        if (!dvmInstanceof(objClazz, declClazz)) {
+            ALOGW("JNI WARNING: method declared to return '%s' returned '%s'",
+                declType, objType);
+            ALOGW("             failed in %s.%s",
+                method->clazz->descriptor, method->name);
+            abortMaybe();
+            return;
+        } else {
+            ALOGV("Check %s.%s: %s io %s (SLOW-OK)",
+                method->clazz->descriptor, method->name, objType, declType);
+        }
+    }
+}
+
+/*
+ * Determine if we need to check the return type coming out of the call.
+ *
+ * (We don't simply do this at the top of checkCallResultCommon() because
+ * this is on the critical path for native method calls.)
+ */
+static inline bool callNeedsCheck(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    return (method->shorty[0] == 'L' && !dvmCheckException(self) && pResult->l != NULL);
+}
+
+/*
+ * Check a call into native code.
+ */
+void dvmCheckCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self)) {
+        checkCallResultCommon(args, pResult, method, self);
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI function helpers
+ * ===========================================================================
+ */
+
+static inline const JNINativeInterface* baseEnv(JNIEnv* env) {
+    return ((JNIEnvExt*) env)->baseFuncTable;
+}
+
+static inline const JNIInvokeInterface* baseVm(JavaVM* vm) {
+    return ((JavaVMExt*) vm)->baseFuncTable;
+}
+
+class ScopedCheckJniThreadState {
+public:
+    explicit ScopedCheckJniThreadState(JNIEnv* env) {
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+    }
+
+    ~ScopedCheckJniThreadState() {
+        dvmChangeStatus(NULL, THREAD_NATIVE);
+    }
+
+private:
+    // Disallow copy and assignment.
+    ScopedCheckJniThreadState(const ScopedCheckJniThreadState&);
+    void operator=(const ScopedCheckJniThreadState&);
+};
+
+/*
+ * Flags passed into ScopedCheck.
+ */
+#define kFlag_Default       0x0000
+
+#define kFlag_CritBad       0x0000      /* calling while in critical is bad */
+#define kFlag_CritOkay      0x0001      /* ...okay */
+#define kFlag_CritGet       0x0002      /* this is a critical "get" */
+#define kFlag_CritRelease   0x0003      /* this is a critical "release" */
+#define kFlag_CritMask      0x0003      /* bit mask to get "crit" value */
+
+#define kFlag_ExcepBad      0x0000      /* raised exceptions are bad */
+#define kFlag_ExcepOkay     0x0004      /* ...okay */
+
+#define kFlag_Release       0x0010      /* are we in a non-critical release function? */
+#define kFlag_NullableUtf   0x0020      /* are our UTF parameters nullable? */
+
+#define kFlag_Invocation    0x8000      /* Part of the invocation interface (JavaVM*) */
+
+static const char* indirectRefKindName(IndirectRef iref)
+{
+    return indirectRefKindToString(indirectRefKind(iref));
+}
+
+class ScopedCheck {
+public:
+    // For JNIEnv* functions.
+    explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName) {
+        init(env, flags, functionName, true);
+        checkThread(flags);
+    }
+
+    // For JavaVM* functions.
+    explicit ScopedCheck(bool hasMethod, const char* functionName) {
+        init(NULL, kFlag_Invocation, functionName, hasMethod);
+    }
+
+    /*
+     * In some circumstances the VM will screen class names, but it doesn't
+     * for class lookup.  When things get bounced through a class loader, they
+     * can actually get normalized a couple of times; as a result, passing in
+     * a class name like "java.lang.Thread" instead of "java/lang/Thread" will
+     * work in some circumstances.
+     *
+     * This is incorrect and could cause strange behavior or compatibility
+     * problems, so we want to screen that out here.
+     *
+     * We expect "fully-qualified" class names, like "java/lang/Thread" or
+     * "[Ljava/lang/Object;".
+     */
+    void checkClassName(const char* className) {
+        if (!dexIsValidClassName(className, false)) {
+            ALOGW("JNI WARNING: illegal class name '%s' (%s)", className, mFunctionName);
+            ALOGW("             (should be formed like 'dalvik/system/DexFile')");
+            ALOGW("             or '[Ldalvik/system/DexFile;' or '[[B')");
+            abortMaybe();
+        }
+    }
+
+    void checkFieldTypeForGet(jfieldID fid, const char* expectedSignature, bool isStatic) {
+        if (fid == NULL) {
+            ALOGW("JNI WARNING: null jfieldID (%s)", mFunctionName);
+            showLocation();
+            abortMaybe();
+        }
+
+        bool printWarn = false;
+        Field* field = (Field*) fid;
+        const char* actualSignature = field->signature;
+        if (*expectedSignature == 'L') {
+            // 'actualSignature' has the exact type.
+            // We just know we're expecting some kind of reference.
+            if (*actualSignature != 'L' && *actualSignature != '[') {
+                printWarn = true;
+            }
+        } else if (*actualSignature != *expectedSignature) {
+            printWarn = true;
+        }
+
+        if (!printWarn && isStatic && !dvmIsStaticField(field)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: accessing non-static field %s as static", field->name);
+            } else {
+                ALOGW("JNI WARNING: accessing static field %s as non-static", field->name);
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            ALOGW("JNI WARNING: %s for field '%s' of expected type %s, got %s",
+                    mFunctionName, field->name, expectedSignature, actualSignature);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the field is of the appropriate type.  If the field has an
+     * object type, "jobj" is the object we're trying to assign into it.
+     *
+     * Works for both static and instance fields.
+     */
+    void checkFieldTypeForSet(jobject jobj, jfieldID fieldID, PrimitiveType prim, bool isStatic) {
+        if (fieldID == NULL) {
+            ALOGW("JNI WARNING: null jfieldID (%s)", mFunctionName);
+            showLocation();
+            abortMaybe();
+        }
+
+        bool printWarn = false;
+        Field* field = (Field*) fieldID;
+        if ((field->signature[0] == 'L' || field->signature[0] == '[') && jobj != NULL) {
+            ScopedCheckJniThreadState ts(mEnv);
+            Object* obj = dvmDecodeIndirectRef(self(), jobj);
+            /*
+             * If jobj is a weak global ref whose referent has been cleared,
+             * obj will be NULL.  Otherwise, obj should always be non-NULL
+             * and valid.
+             */
+            if (obj != NULL && !dvmIsHeapAddress(obj)) {
+                ALOGW("JNI WARNING: field operation (%s) on invalid %s reference (%p)",
+                      mFunctionName, indirectRefKindName(jobj), jobj);
+                printWarn = true;
+            } else {
+                ClassObject* fieldClass = dvmFindLoadedClass(field->signature);
+                ClassObject* objClass = obj->clazz;
+
+                assert(fieldClass != NULL);
+                assert(objClass != NULL);
+
+                if (!dvmInstanceof(objClass, fieldClass)) {
+                    ALOGW("JNI WARNING: %s for field '%s' expected type %s, got %s",
+                          mFunctionName, field->name, field->signature, objClass->descriptor);
+                    printWarn = true;
+                }
+            }
+        } else if (dexGetPrimitiveTypeFromDescriptorChar(field->signature[0]) != prim) {
+            ALOGW("JNI WARNING: %s for field '%s' expected type %s, got %s",
+                    mFunctionName, field->name, field->signature, primitiveTypeToName(prim));
+            printWarn = true;
+        } else if (isStatic && !dvmIsStaticField(field)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: %s for non-static field '%s'", mFunctionName, field->name);
+            } else {
+                ALOGW("JNI WARNING: %s for static field '%s'", mFunctionName, field->name);
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that this instance field ID is valid for this object.
+     *
+     * Assumes "jobj" has already been validated.
+     */
+    void checkInstanceFieldID(jobject jobj, jfieldID fieldID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI ERROR: %s on invalid reference (%p)", mFunctionName, jobj);
+            dvmAbort();
+        }
+
+        /*
+         * Check this class and all of its superclasses for a matching field.
+         * Don't need to scan interfaces.
+         */
+        ClassObject* clazz = obj->clazz;
+        while (clazz != NULL) {
+            if ((InstField*) fieldID >= clazz->ifields &&
+                    (InstField*) fieldID < clazz->ifields + clazz->ifieldCount) {
+                return;
+            }
+
+            clazz = clazz->super;
+        }
+
+        ALOGW("JNI WARNING: instance jfieldID %p not valid for class %s (%s)",
+              fieldID, obj->clazz->descriptor, mFunctionName);
+        showLocation();
+        abortMaybe();
+    }
+
+    /*
+     * Verify that the pointer value is non-NULL.
+     */
+    void checkNonNull(const void* ptr) {
+        if (ptr == NULL) {
+            ALOGW("JNI WARNING: invalid null pointer (%s)", mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the method's return type matches the type of call.
+     * 'expectedType' will be "L" for all objects, including arrays.
+     */
+    void checkSig(jmethodID methodID, const char* expectedType, bool isStatic) {
+        const Method* method = (const Method*) methodID;
+        bool printWarn = false;
+
+        if (*expectedType != method->shorty[0]) {
+            ALOGW("JNI WARNING: %s expected return type '%s'", mFunctionName, expectedType);
+            printWarn = true;
+        } else if (isStatic && !dvmIsStaticMethod(method)) {
+            if (isStatic) {
+                ALOGW("JNI WARNING: calling non-static method with static call %s", mFunctionName);
+            } else {
+                ALOGW("JNI WARNING: calling static method with non-static call %s", mFunctionName);
+            }
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGW("             calling %s.%s %s", method->clazz->descriptor, method->name, desc);
+            free(desc);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that this static field ID is valid for this class.
+     *
+     * Assumes "jclazz" has already been validated.
+     */
+    void checkStaticFieldID(jclass jclazz, jfieldID fieldID) {
+        ScopedCheckJniThreadState ts(mEnv);
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(self(), jclazz);
+        StaticField* base = &clazz->sfields[0];
+        int fieldCount = clazz->sfieldCount;
+        if ((StaticField*) fieldID < base || (StaticField*) fieldID >= base + fieldCount) {
+            ALOGW("JNI WARNING: static fieldID %p not valid for class %s (%s)",
+                  fieldID, clazz->descriptor, mFunctionName);
+            ALOGW("             base=%p count=%d", base, fieldCount);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "methodID" is appropriate for "clazz".
+     *
+     * A mismatch isn't dangerous, because the jmethodID defines the class.  In
+     * fact, jclazz is unused in the implementation.  It's best if we don't
+     * allow bad code in the system though.
+     *
+     * Instances of "jclazz" must be instances of the method's declaring class.
+     */
+    void checkStaticMethod(jclass jclazz, jmethodID methodID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(self(), jclazz);
+        const Method* method = (const Method*) methodID;
+
+        if (!dvmInstanceof(clazz, method->clazz)) {
+            ALOGW("JNI WARNING: can't call static %s.%s on class %s (%s)",
+                  method->clazz->descriptor, method->name, clazz->descriptor, mFunctionName);
+            showLocation();
+            // no abort?
+        }
+    }
+
+    /*
+     * Verify that "methodID" is appropriate for "jobj".
+     *
+     * Make sure the object is an instance of the method's declaring class.
+     * (Note the methodID might point to a declaration in an interface; this
+     * will be handled automatically by the instanceof check.)
+     */
+    void checkVirtualMethod(jobject jobj, jmethodID methodID) {
+        ScopedCheckJniThreadState ts(mEnv);
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        const Method* method = (const Method*) methodID;
+
+        if (!dvmInstanceof(obj->clazz, method->clazz)) {
+            ALOGW("JNI WARNING: can't call %s.%s on instance of %s (%s)",
+                  method->clazz->descriptor, method->name, obj->clazz->descriptor, mFunctionName);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /**
+     * The format string is a sequence of the following characters,
+     * and must be followed by arguments of the corresponding types
+     * in the same order.
+     *
+     * Java primitive types:
+     * B - jbyte
+     * C - jchar
+     * D - jdouble
+     * F - jfloat
+     * I - jint
+     * J - jlong
+     * S - jshort
+     * Z - jboolean (shown as true and false)
+     * V - void
+     *
+     * Java reference types:
+     * L - jobject
+     * a - jarray
+     * c - jclass
+     * s - jstring
+     *
+     * JNI types:
+     * b - jboolean (shown as JNI_TRUE and JNI_FALSE)
+     * f - jfieldID
+     * m - jmethodID
+     * p - void*
+     * r - jint (for release mode arguments)
+     * t - thread args (for AttachCurrentThread)
+     * u - const char* (modified UTF-8)
+     * z - jsize (for lengths; use i if negative values are okay)
+     * v - JavaVM*
+     * E - JNIEnv*
+     * . - no argument; just print "..." (used for varargs JNI calls)
+     *
+     * Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
+     */
+    void check(bool entry, const char* fmt0, ...) {
+        va_list ap;
+
+        bool shouldTrace = false;
+        const Method* method = NULL;
+        if ((gDvm.jniTrace || gDvmJni.logThirdPartyJni) && mHasMethod) {
+            // We need to guard some of the invocation interface's calls: a bad caller might
+            // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
+            if ((mFlags & kFlag_Invocation) == 0 || dvmThreadSelf() != NULL) {
+                method = dvmGetCurrentJNIMethod();
+            }
+        }
+        if (method != NULL) {
+            // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+            // when a native method that matches the Xjnitrace argument calls a JNI function
+            // such as NewByteArray.
+            if (gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace) != NULL) {
+                shouldTrace = true;
+            }
+            // If -Xjniopts:logThirdPartyJni is on, we want to log any JNI function calls
+            // made by a third-party native method.
+            if (gDvmJni.logThirdPartyJni) {
+                shouldTrace |= method->shouldTrace;
+            }
+        }
+
+        if (shouldTrace) {
+            va_start(ap, fmt0);
+            std::string msg;
+            for (const char* fmt = fmt0; *fmt;) {
+                char ch = *fmt++;
+                if (ch == 'B') { // jbyte
+                    jbyte b = va_arg(ap, int);
+                    if (b >= 0 && b < 10) {
+                        StringAppendF(&msg, "%d", b);
+                    } else {
+                        StringAppendF(&msg, "%#x (%d)", b, b);
+                    }
+                } else if (ch == 'C') { // jchar
+                    jchar c = va_arg(ap, int);
+                    if (c < 0x7f && c >= ' ') {
+                        StringAppendF(&msg, "U+%x ('%c')", c, c);
+                    } else {
+                        StringAppendF(&msg, "U+%x", c);
+                    }
+                } else if (ch == 'F' || ch == 'D') { // jfloat, jdouble
+                    StringAppendF(&msg, "%g", va_arg(ap, double));
+                } else if (ch == 'I' || ch == 'S') { // jint, jshort
+                    StringAppendF(&msg, "%d", va_arg(ap, int));
+                } else if (ch == 'J') { // jlong
+                    StringAppendF(&msg, "%lld", va_arg(ap, jlong));
+                } else if (ch == 'Z') { // jboolean
+                    StringAppendF(&msg, "%s", va_arg(ap, int) ? "true" : "false");
+                } else if (ch == 'V') { // void
+                    msg += "void";
+                } else if (ch == 'v') { // JavaVM*
+                    JavaVM* vm = va_arg(ap, JavaVM*);
+                    StringAppendF(&msg, "(JavaVM*)%p", vm);
+                } else if (ch == 'E') { // JNIEnv*
+                    JNIEnv* env = va_arg(ap, JNIEnv*);
+                    StringAppendF(&msg, "(JNIEnv*)%p", env);
+                } else if (ch == 'L' || ch == 'a' || ch == 's') { // jobject, jarray, jstring
+                    // For logging purposes, these are identical.
+                    jobject o = va_arg(ap, jobject);
+                    if (o == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "%p", o);
+                    }
+                } else if (ch == 'b') { // jboolean (JNI-style)
+                    jboolean b = va_arg(ap, int);
+                    msg += (b ? "JNI_TRUE" : "JNI_FALSE");
+                } else if (ch == 'c') { // jclass
+                    jclass jc = va_arg(ap, jclass);
+                    Object* c = dvmDecodeIndirectRef(self(), jc);
+                    if (c == NULL) {
+                        msg += "NULL";
+                    } else if (c == kInvalidIndirectRefObject || !dvmIsHeapAddress(c)) {
+                        StringAppendF(&msg, "%p(INVALID)", jc);
+                    } else {
+                        std::string className(dvmHumanReadableType(c));
+                        StringAppendF(&msg, "%s", className.c_str());
+                        if (!entry) {
+                            StringAppendF(&msg, " (%p)", jc);
+                        }
+                    }
+                } else if (ch == 'f') { // jfieldID
+                    jfieldID fid = va_arg(ap, jfieldID);
+                    std::string name(dvmHumanReadableField((Field*) fid));
+                    StringAppendF(&msg, "%s", name.c_str());
+                    if (!entry) {
+                        StringAppendF(&msg, " (%p)", fid);
+                    }
+                } else if (ch == 'z') { // non-negative jsize
+                    // You might expect jsize to be size_t, but it's not; it's the same as jint.
+                    // We only treat this specially so we can do the non-negative check.
+                    // TODO: maybe this wasn't worth it?
+                    jint i = va_arg(ap, jint);
+                    StringAppendF(&msg, "%d", i);
+                } else if (ch == 'm') { // jmethodID
+                    jmethodID mid = va_arg(ap, jmethodID);
+                    std::string name(dvmHumanReadableMethod((Method*) mid, true));
+                    StringAppendF(&msg, "%s", name.c_str());
+                    if (!entry) {
+                        StringAppendF(&msg, " (%p)", mid);
+                    }
+                } else if (ch == 'p' || ch == 't') { // void* ("pointer" or "thread args")
+                    void* p = va_arg(ap, void*);
+                    if (p == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "(void*) %p", p);
+                    }
+                } else if (ch == 'r') { // jint (release mode)
+                    jint releaseMode = va_arg(ap, jint);
+                    if (releaseMode == 0) {
+                        msg += "0";
+                    } else if (releaseMode == JNI_ABORT) {
+                        msg += "JNI_ABORT";
+                    } else if (releaseMode == JNI_COMMIT) {
+                        msg += "JNI_COMMIT";
+                    } else {
+                        StringAppendF(&msg, "invalid release mode %d", releaseMode);
+                    }
+                } else if (ch == 'u') { // const char* (modified UTF-8)
+                    const char* utf = va_arg(ap, const char*);
+                    if (utf == NULL) {
+                        msg += "NULL";
+                    } else {
+                        StringAppendF(&msg, "\"%s\"", utf);
+                    }
+                } else if (ch == '.') {
+                    msg += "...";
+                } else {
+                    ALOGE("unknown trace format specifier %c", ch);
+                    dvmAbort();
+                }
+                if (*fmt) {
+                    StringAppendF(&msg, ", ");
+                }
+            }
+            va_end(ap);
+
+            if (entry) {
+                if (mHasMethod) {
+                    std::string methodName(dvmHumanReadableMethod(method, false));
+                    ALOGI("JNI: %s -> %s(%s)", methodName.c_str(), mFunctionName, msg.c_str());
+                    mIndent = methodName.size() + 1;
+                } else {
+                    ALOGI("JNI: -> %s(%s)", mFunctionName, msg.c_str());
+                    mIndent = 0;
+                }
+            } else {
+                ALOGI("JNI: %*s<- %s returned %s", mIndent, "", mFunctionName, msg.c_str());
+            }
+        }
+
+        // We always do the thorough checks on entry, and never on exit...
+        if (entry) {
+            va_start(ap, fmt0);
+            for (const char* fmt = fmt0; *fmt; ++fmt) {
+                char ch = *fmt;
+                if (ch == 'a') {
+                    checkArray(va_arg(ap, jarray));
+                } else if (ch == 'c') {
+                    checkClass(va_arg(ap, jclass));
+                } else if (ch == 'L') {
+                    checkObject(va_arg(ap, jobject));
+                } else if (ch == 'r') {
+                    checkReleaseMode(va_arg(ap, jint));
+                } else if (ch == 's') {
+                    checkString(va_arg(ap, jstring));
+                } else if (ch == 't') {
+                    checkThreadArgs(va_arg(ap, void*));
+                } else if (ch == 'u') {
+                    if ((mFlags & kFlag_Release) != 0) {
+                        checkNonNull(va_arg(ap, const char*));
+                    } else {
+                        bool nullable = ((mFlags & kFlag_NullableUtf) != 0);
+                        checkUtfString(va_arg(ap, const char*), nullable);
+                    }
+                } else if (ch == 'z') {
+                    checkLengthPositive(va_arg(ap, jsize));
+                } else if (strchr("BCISZbfmpEv", ch) != NULL) {
+                    va_arg(ap, int); // Skip this argument.
+                } else if (ch == 'D' || ch == 'F') {
+                    va_arg(ap, double); // Skip this argument.
+                } else if (ch == 'J') {
+                    va_arg(ap, long); // Skip this argument.
+                } else if (ch == '.') {
+                } else {
+                    ALOGE("unknown check format specifier %c", ch);
+                    dvmAbort();
+                }
+            }
+            va_end(ap);
+        }
+    }
+
+    // Only safe after checkThread returns.
+    Thread* self() {
+        return ((JNIEnvExt*) mEnv)->self;
+    }
+
+private:
+    JNIEnv* mEnv;
+    const char* mFunctionName;
+    int mFlags;
+    bool mHasMethod;
+    size_t mIndent;
+
+    void init(JNIEnv* env, int flags, const char* functionName, bool hasMethod) {
+        mEnv = env;
+        mFlags = flags;
+
+        // Use +6 to drop the leading "Check_"...
+        mFunctionName = functionName + 6;
+
+        // Set "hasMethod" to true if we have a valid thread with a method pointer.
+        // We won't have one before attaching a thread, after detaching a thread, or
+        // after destroying the VM.
+        mHasMethod = hasMethod;
+    }
+
+    /*
+     * Verify that "array" is non-NULL and points to an Array object.
+     *
+     * Since we're dealing with objects, switch to "running" mode.
+     */
+    void checkArray(jarray jarr) {
+        if (jarr == NULL) {
+            ALOGW("JNI WARNING: %s received null array", mFunctionName);
+            showLocation();
+            abortMaybe();
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+        bool printWarn = false;
+
+        Object* obj = dvmDecodeIndirectRef(self(), jarr);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI WARNING: %s: jarray is an invalid %s reference (%p)",
+                  mFunctionName, indirectRefKindName(jarr), jarr);
+            printWarn = true;
+        } else if (obj->clazz->descriptor[0] != '[') {
+            ALOGW("JNI WARNING: %s: jarray arg has wrong type (expected array, got %s)",
+                  mFunctionName, obj->clazz->descriptor);
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    void checkClass(jclass c) {
+        checkInstance(c, gDvm.classJavaLangClass, "jclass");
+    }
+
+    void checkLengthPositive(jsize length) {
+        if (length < 0) {
+            ALOGW("JNI WARNING: negative jsize (%s)", mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "jobj" is a valid object, and that it's an object that JNI
+     * is allowed to know about.  We allow NULL references.
+     *
+     * Switches to "running" mode before performing checks.
+     */
+    void checkObject(jobject jobj) {
+        if (jobj == NULL) {
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+
+        bool printWarn = false;
+        if (dvmGetJNIRefType(self(), jobj) == JNIInvalidRefType) {
+            ALOGW("JNI WARNING: %p is not a valid JNI reference (%s)", jobj, mFunctionName);
+            printWarn = true;
+        } else {
+            Object* obj = dvmDecodeIndirectRef(self(), jobj);
+            if (obj == kInvalidIndirectRefObject) {
+                ALOGW("JNI WARNING: native code passing in invalid reference %p (%s)",
+                      jobj, mFunctionName);
+                printWarn = true;
+            } else if (obj != NULL && !dvmIsHeapAddress(obj)) {
+                // TODO: when we remove workAroundAppJniBugs, this should be impossible.
+                ALOGW("JNI WARNING: native code passing in reference to invalid object %p %p (%s)",
+                        jobj, obj, mFunctionName);
+                printWarn = true;
+            }
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that the "mode" argument passed to a primitive array Release
+     * function is one of the valid values.
+     */
+    void checkReleaseMode(jint mode) {
+        if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+            ALOGW("JNI WARNING: bad value for mode (%d) (%s)", mode, mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    void checkString(jstring s) {
+        checkInstance(s, gDvm.classJavaLangString, "jstring");
+    }
+
+    void checkThreadArgs(void* thread_args) {
+        JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(thread_args);
+        if (args != NULL && args->version < JNI_VERSION_1_2) {
+            ALOGW("JNI WARNING: bad value for JNI version (%d) (%s)", args->version, mFunctionName);
+            abortMaybe();
+        }
+    }
+
+    void checkThread(int flags) {
+        // Get the *correct* JNIEnv by going through our TLS pointer.
+        JNIEnvExt* threadEnv = dvmGetJNIEnvForThread();
+
+        /*
+         * Verify that the current thread is (a) attached and (b) associated with
+         * this particular instance of JNIEnv.
+         */
+        bool printWarn = false;
+        if (threadEnv == NULL) {
+            ALOGE("JNI ERROR: non-VM thread making JNI call (%s)", mFunctionName);
+            // don't set printWarn -- it'll try to call showLocation()
+            dvmAbort();
+        } else if ((JNIEnvExt*) mEnv != threadEnv) {
+            if (dvmThreadSelf()->threadId != threadEnv->envThreadId) {
+                ALOGE("JNI: threadEnv != thread->env? (%s)", mFunctionName);
+                dvmAbort();
+            }
+
+            ALOGW("JNI WARNING: threadid=%d using env from threadid=%d (%s)",
+                  threadEnv->envThreadId, ((JNIEnvExt*) mEnv)->envThreadId, mFunctionName);
+            printWarn = true;
+
+            // If we're keeping broken code limping along, we need to suppress the abort...
+            if (gDvmJni.workAroundAppJniBugs) {
+                printWarn = false;
+            }
+
+            /* this is a bad idea -- need to throw as we exit, or abort func */
+            //dvmThrowRuntimeException("invalid use of JNI env ptr");
+        } else if (((JNIEnvExt*) mEnv)->self != dvmThreadSelf()) {
+            /* correct JNIEnv*; make sure the "self" pointer is correct */
+            ALOGE("JNI ERROR: env->self != thread-self (%p vs. %p) (%s)",
+                  ((JNIEnvExt*) mEnv)->self, dvmThreadSelf(), mFunctionName);
+            dvmAbort();
+        }
+
+        /*
+         * Verify that, if this thread previously made a critical "get" call, we
+         * do the corresponding "release" call before we try anything else.
+         */
+        switch (flags & kFlag_CritMask) {
+        case kFlag_CritOkay:    // okay to call this method
+            break;
+        case kFlag_CritBad:     // not okay to call
+            if (threadEnv->critical) {
+                ALOGW("JNI WARNING: threadid=%d using JNI after critical get (%s)",
+                      threadEnv->envThreadId, mFunctionName);
+                printWarn = true;
+            }
+            break;
+        case kFlag_CritGet:     // this is a "get" call
+            /* don't check here; we allow nested gets */
+            threadEnv->critical++;
+            break;
+        case kFlag_CritRelease: // this is a "release" call
+            threadEnv->critical--;
+            if (threadEnv->critical < 0) {
+                ALOGW("JNI WARNING: threadid=%d called too many critical releases (%s)",
+                      threadEnv->envThreadId, mFunctionName);
+                printWarn = true;
+            }
+            break;
+        default:
+            assert(false);
+        }
+
+        /*
+         * Verify that, if an exception has been raised, the native code doesn't
+         * make any JNI calls other than the Exception* methods.
+         */
+        bool printException = false;
+        if ((flags & kFlag_ExcepOkay) == 0 && dvmCheckException(dvmThreadSelf())) {
+            ALOGW("JNI WARNING: JNI function %s called with exception pending", mFunctionName);
+            printWarn = true;
+            printException = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+        }
+        if (printException) {
+            ALOGW("Pending exception is:");
+            dvmLogExceptionStackTrace();
+        }
+        if (printWarn) {
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "bytes" points to valid "modified UTF-8" data.
+     */
+    void checkUtfString(const char* bytes, bool nullable) {
+        if (bytes == NULL) {
+            if (!nullable) {
+                ALOGW("JNI WARNING: non-nullable const char* was NULL (%s)", mFunctionName);
+                showLocation();
+                abortMaybe();
+            }
+            return;
+        }
+
+        const char* errorKind = NULL;
+        u1 utf8 = checkUtfBytes(bytes, &errorKind);
+        if (errorKind != NULL) {
+            ALOGW("JNI WARNING: %s input is not valid Modified UTF-8: illegal %s byte %#x",
+                  mFunctionName, errorKind, utf8);
+            ALOGW("             string: '%s'", bytes);
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    /*
+     * Verify that "jobj" is a valid non-NULL object reference, and points to
+     * an instance of expectedClass.
+     *
+     * Because we're looking at an object on the GC heap, we have to switch
+     * to "running" mode before doing the checks.
+     */
+    void checkInstance(jobject jobj, ClassObject* expectedClass, const char* argName) {
+        if (jobj == NULL) {
+            ALOGW("JNI WARNING: received null %s (%s)", argName, mFunctionName);
+            showLocation();
+            abortMaybe();
+            return;
+        }
+
+        ScopedCheckJniThreadState ts(mEnv);
+        bool printWarn = false;
+
+        Object* obj = dvmDecodeIndirectRef(self(), jobj);
+        if (!dvmIsHeapAddress(obj)) {
+            ALOGW("JNI WARNING: %s is an invalid %s reference (%p) (%s)",
+                  argName, indirectRefKindName(jobj), jobj, mFunctionName);
+            printWarn = true;
+        } else if (obj->clazz != expectedClass) {
+            ALOGW("JNI WARNING: %s arg has wrong type (expected %s, got %s) (%s)",
+                  argName, expectedClass->descriptor, obj->clazz->descriptor, mFunctionName);
+            printWarn = true;
+        }
+
+        if (printWarn) {
+            showLocation();
+            abortMaybe();
+        }
+    }
+
+    static u1 checkUtfBytes(const char* bytes, const char** errorKind) {
+        while (*bytes != '\0') {
+            u1 utf8 = *(bytes++);
+            // Switch on the high four bits.
+            switch (utf8 >> 4) {
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+                // Bit pattern 0xxx. No need for any extra bytes.
+                break;
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0f:
+                /*
+                 * Bit pattern 10xx or 1111, which are illegal start bytes.
+                 * Note: 1111 is valid for normal UTF-8, but not the
+                 * modified UTF-8 used here.
+                 */
+                *errorKind = "start";
+                return utf8;
+            case 0x0e:
+                // Bit pattern 1110, so there are two additional bytes.
+                utf8 = *(bytes++);
+                if ((utf8 & 0xc0) != 0x80) {
+                    *errorKind = "continuation";
+                    return utf8;
+                }
+                // Fall through to take care of the final byte.
+            case 0x0c:
+            case 0x0d:
+                // Bit pattern 110x, so there is one additional byte.
+                utf8 = *(bytes++);
+                if ((utf8 & 0xc0) != 0x80) {
+                    *errorKind = "continuation";
+                    return utf8;
+                }
+                break;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Returns a human-readable name for the given primitive type.
+     */
+    static const char* primitiveTypeToName(PrimitiveType primType) {
+        switch (primType) {
+        case PRIM_VOID:    return "void";
+        case PRIM_BOOLEAN: return "boolean";
+        case PRIM_BYTE:    return "byte";
+        case PRIM_SHORT:   return "short";
+        case PRIM_CHAR:    return "char";
+        case PRIM_INT:     return "int";
+        case PRIM_LONG:    return "long";
+        case PRIM_FLOAT:   return "float";
+        case PRIM_DOUBLE:  return "double";
+        case PRIM_NOT:     return "Object/array";
+        default:           return "???";
+        }
+    }
+
+    void showLocation() {
+        const Method* method = dvmGetCurrentJNIMethod();
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("             in %s.%s:%s (%s)", method->clazz->descriptor, method->name, desc, mFunctionName);
+        free(desc);
+    }
+
+    // Disallow copy and assignment.
+    ScopedCheck(const ScopedCheck&);
+    void operator=(const ScopedCheck&);
+};
+
+/*
+ * ===========================================================================
+ *      Guarded arrays
+ * ===========================================================================
+ */
+
+#define kGuardLen       512         /* must be multiple of 2 */
+#define kGuardPattern   0xd5e3      /* uncommon values; d5e3d5e3 invalid addr */
+#define kGuardMagic     0xffd5aa96
+
+/* this gets tucked in at the start of the buffer; struct size must be even */
+struct GuardedCopy {
+    u4          magic;
+    uLong       adler;
+    size_t      originalLen;
+    const void* originalPtr;
+
+    /* find the GuardedCopy given the pointer into the "live" data */
+    static inline const GuardedCopy* fromData(const void* dataBuf) {
+        return reinterpret_cast<const GuardedCopy*>(actualBuffer(dataBuf));
+    }
+
+    /*
+     * Create an over-sized buffer to hold the contents of "buf".  Copy it in,
+     * filling in the area around it with guard data.
+     *
+     * We use a 16-bit pattern to make a rogue memset less likely to elude us.
+     */
+    static void* create(const void* buf, size_t len, bool modOkay) {
+        size_t newLen = actualLength(len);
+        u1* newBuf = debugAlloc(newLen);
+
+        /* fill it in with a pattern */
+        u2* pat = (u2*) newBuf;
+        for (size_t i = 0; i < newLen / 2; i++) {
+            *pat++ = kGuardPattern;
+        }
+
+        /* copy the data in; note "len" could be zero */
+        memcpy(newBuf + kGuardLen / 2, buf, len);
+
+        /* if modification is not expected, grab a checksum */
+        uLong adler = 0;
+        if (!modOkay) {
+            adler = adler32(0L, Z_NULL, 0);
+            adler = adler32(adler, (const Bytef*)buf, len);
+            *(uLong*)newBuf = adler;
+        }
+
+        GuardedCopy* pExtra = reinterpret_cast<GuardedCopy*>(newBuf);
+        pExtra->magic = kGuardMagic;
+        pExtra->adler = adler;
+        pExtra->originalPtr = buf;
+        pExtra->originalLen = len;
+
+        return newBuf + kGuardLen / 2;
+    }
+
+    /*
+     * Free up the guard buffer, scrub it, and return the original pointer.
+     */
+    static void* destroy(void* dataBuf) {
+        const GuardedCopy* pExtra = GuardedCopy::fromData(dataBuf);
+        void* originalPtr = (void*) pExtra->originalPtr;
+        size_t len = pExtra->originalLen;
+        debugFree(dataBuf, len);
+        return originalPtr;
+    }
+
+    /*
+     * Verify the guard area and, if "modOkay" is false, that the data itself
+     * has not been altered.
+     *
+     * The caller has already checked that "dataBuf" is non-NULL.
+     */
+    static bool check(const void* dataBuf, bool modOkay) {
+        static const u4 kMagicCmp = kGuardMagic;
+        const u1* fullBuf = actualBuffer(dataBuf);
+        const GuardedCopy* pExtra = GuardedCopy::fromData(dataBuf);
+
+        /*
+         * Before we do anything with "pExtra", check the magic number.  We
+         * do the check with memcmp rather than "==" in case the pointer is
+         * unaligned.  If it points to completely bogus memory we're going
+         * to crash, but there's no easy way around that.
+         */
+        if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
+            u1 buf[4];
+            memcpy(buf, &pExtra->magic, 4);
+            ALOGE("JNI: guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
+                    buf[3], buf[2], buf[1], buf[0], dataBuf); /* assume little endian */
+            return false;
+        }
+
+        size_t len = pExtra->originalLen;
+
+        /* check bottom half of guard; skip over optional checksum storage */
+        const u2* pat = (u2*) fullBuf;
+        for (size_t i = sizeof(GuardedCopy) / 2; i < (kGuardLen / 2 - sizeof(GuardedCopy)) / 2; i++) {
+            if (pat[i] != kGuardPattern) {
+                ALOGE("JNI: guard pattern(1) disturbed at %p + %d", fullBuf, i*2);
+                return false;
+            }
+        }
+
+        int offset = kGuardLen / 2 + len;
+        if (offset & 0x01) {
+            /* odd byte; expected value depends on endian-ness of host */
+            const u2 patSample = kGuardPattern;
+            if (fullBuf[offset] != ((const u1*) &patSample)[1]) {
+                ALOGE("JNI: guard pattern disturbed in odd byte after %p (+%d) 0x%02x 0x%02x",
+                        fullBuf, offset, fullBuf[offset], ((const u1*) &patSample)[1]);
+                return false;
+            }
+            offset++;
+        }
+
+        /* check top half of guard */
+        pat = (u2*) (fullBuf + offset);
+        for (size_t i = 0; i < kGuardLen / 4; i++) {
+            if (pat[i] != kGuardPattern) {
+                ALOGE("JNI: guard pattern(2) disturbed at %p + %d", fullBuf, offset + i*2);
+                return false;
+            }
+        }
+
+        /*
+         * If modification is not expected, verify checksum.  Strictly speaking
+         * this is wrong: if we told the client that we made a copy, there's no
+         * reason they can't alter the buffer.
+         */
+        if (!modOkay) {
+            uLong adler = adler32(0L, Z_NULL, 0);
+            adler = adler32(adler, (const Bytef*)dataBuf, len);
+            if (pExtra->adler != adler) {
+                ALOGE("JNI: buffer modified (0x%08lx vs 0x%08lx) at addr %p",
+                        pExtra->adler, adler, dataBuf);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+private:
+    static u1* debugAlloc(size_t len) {
+        void* result = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+        if (result == MAP_FAILED) {
+            ALOGE("GuardedCopy::create mmap(%d) failed: %s", len, strerror(errno));
+            dvmAbort();
+        }
+        return reinterpret_cast<u1*>(result);
+    }
+
+    static void debugFree(void* dataBuf, size_t len) {
+        u1* fullBuf = actualBuffer(dataBuf);
+        size_t totalByteCount = actualLength(len);
+        // TODO: we could mprotect instead, and keep the allocation around for a while.
+        // This would be even more expensive, but it might catch more errors.
+        // if (mprotect(fullBuf, totalByteCount, PROT_NONE) != 0) {
+        //     ALOGW("mprotect(PROT_NONE) failed: %s", strerror(errno));
+        // }
+        if (munmap(fullBuf, totalByteCount) != 0) {
+            ALOGW("munmap failed: %s", strerror(errno));
+            dvmAbort();
+        }
+    }
+
+    static const u1* actualBuffer(const void* dataBuf) {
+        return reinterpret_cast<const u1*>(dataBuf) - kGuardLen / 2;
+    }
+
+    static u1* actualBuffer(void* dataBuf) {
+        return reinterpret_cast<u1*>(dataBuf) - kGuardLen / 2;
+    }
+
+    // Underlying length of a user allocation of 'length' bytes.
+    static size_t actualLength(size_t length) {
+        return (length + kGuardLen + 1) & ~0x01;
+    }
+};
+
+/*
+ * Return the width, in bytes, of a primitive type.
+ */
+static int dvmPrimitiveTypeWidth(PrimitiveType primType) {
+    switch (primType) {
+        case PRIM_BOOLEAN: return 1;
+        case PRIM_BYTE:    return 1;
+        case PRIM_SHORT:   return 2;
+        case PRIM_CHAR:    return 2;
+        case PRIM_INT:     return 4;
+        case PRIM_LONG:    return 8;
+        case PRIM_FLOAT:   return 4;
+        case PRIM_DOUBLE:  return 8;
+        case PRIM_VOID:
+        default: {
+            assert(false);
+            return -1;
+        }
+    }
+}
+
+/*
+ * Create a guarded copy of a primitive array.  Modifications to the copied
+ * data are allowed.  Returns a pointer to the copied data.
+ */
+static void* createGuardedPACopy(JNIEnv* env, const jarray jarr, jboolean* isCopy) {
+    ScopedCheckJniThreadState ts(env);
+
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(dvmThreadSelf(), jarr);
+    PrimitiveType primType = arrObj->clazz->elementClass->primitiveType;
+    int len = arrObj->length * dvmPrimitiveTypeWidth(primType);
+    void* result = GuardedCopy::create(arrObj->contents, len, true);
+    if (isCopy != NULL) {
+        *isCopy = JNI_TRUE;
+    }
+    return result;
+}
+
+/*
+ * Perform the array "release" operation, which may or may not copy data
+ * back into the VM, and may or may not release the underlying storage.
+ */
+static void* releaseGuardedPACopy(JNIEnv* env, jarray jarr, void* dataBuf, int mode) {
+    ScopedCheckJniThreadState ts(env);
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(dvmThreadSelf(), jarr);
+
+    if (!GuardedCopy::check(dataBuf, true)) {
+        ALOGE("JNI: failed guarded copy check in releaseGuardedPACopy");
+        abortMaybe();
+        return NULL;
+    }
+
+    if (mode != JNI_ABORT) {
+        size_t len = GuardedCopy::fromData(dataBuf)->originalLen;
+        memcpy(arrObj->contents, dataBuf, len);
+    }
+
+    u1* result = NULL;
+    if (mode != JNI_COMMIT) {
+        result = (u1*) GuardedCopy::destroy(dataBuf);
+    } else {
+        result = (u1*) (void*) GuardedCopy::fromData(dataBuf)->originalPtr;
+    }
+
+    /* pointer is to the array contents; back up to the array object */
+    result -= OFFSETOF_MEMBER(ArrayObject, contents);
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI functions
+ * ===========================================================================
+ */
+
+#define CHECK_JNI_ENTRY(flags, types, args...) \
+    ScopedCheck sc(env, flags, __FUNCTION__); \
+    sc.check(true, types, ##args)
+
+#define CHECK_JNI_EXIT(type, exp) ({ \
+    typeof (exp) _rc = (exp); \
+    sc.check(false, type, _rc); \
+    _rc; })
+#define CHECK_JNI_EXIT_VOID() \
+    sc.check(false, "V")
+
+static jint Check_GetVersion(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_Default, "E", env);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetVersion(env));
+}
+
+static jclass Check_DefineClass(JNIEnv* env, const char* name, jobject loader,
+    const jbyte* buf, jsize bufLen)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EuLpz", env, name, loader, buf, bufLen);
+    sc.checkClassName(name);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->DefineClass(env, name, loader, buf, bufLen));
+}
+
+static jclass Check_FindClass(JNIEnv* env, const char* name) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Eu", env, name);
+    sc.checkClassName(name);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->FindClass(env, name));
+}
+
+static jclass Check_GetSuperclass(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->GetSuperclass(env, clazz));
+}
+
+static jboolean Check_IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecc", env, clazz1, clazz2);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsAssignableFrom(env, clazz1, clazz2));
+}
+
+static jmethodID Check_FromReflectedMethod(JNIEnv* env, jobject method) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, method);
+    // TODO: check that 'field' is a java.lang.reflect.Method.
+    return CHECK_JNI_EXIT("m", baseEnv(env)->FromReflectedMethod(env, method));
+}
+
+static jfieldID Check_FromReflectedField(JNIEnv* env, jobject field) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, field);
+    // TODO: check that 'field' is a java.lang.reflect.Field.
+    return CHECK_JNI_EXIT("f", baseEnv(env)->FromReflectedField(env, field));
+}
+
+static jobject Check_ToReflectedMethod(JNIEnv* env, jclass cls,
+        jmethodID methodID, jboolean isStatic)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecmb", env, cls, methodID, isStatic);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedMethod(env, cls, methodID, isStatic));
+}
+
+static jobject Check_ToReflectedField(JNIEnv* env, jclass cls,
+        jfieldID fieldID, jboolean isStatic)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecfb", env, cls, fieldID, isStatic);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedField(env, cls, fieldID, isStatic));
+}
+
+static jint Check_Throw(JNIEnv* env, jthrowable obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    // TODO: check that 'obj' is a java.lang.Throwable.
+    return CHECK_JNI_EXIT("I", baseEnv(env)->Throw(env, obj));
+}
+
+static jint Check_ThrowNew(JNIEnv* env, jclass clazz, const char* message) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Ecu", env, clazz, message);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->ThrowNew(env, clazz, message));
+}
+
+static jthrowable Check_ExceptionOccurred(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->ExceptionOccurred(env));
+}
+
+static void Check_ExceptionDescribe(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    baseEnv(env)->ExceptionDescribe(env);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_ExceptionClear(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
+    baseEnv(env)->ExceptionClear(env);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_FatalError(JNIEnv* env, const char* msg) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, msg);
+    baseEnv(env)->FatalError(env, msg);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jint Check_PushLocalFrame(JNIEnv* env, jint capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EI", env, capacity);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->PushLocalFrame(env, capacity));
+}
+
+static jobject Check_PopLocalFrame(JNIEnv* env, jobject res) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, res);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->PopLocalFrame(env, res));
+}
+
+static jobject Check_NewGlobalRef(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewGlobalRef(env, obj));
+}
+
+static void Check_DeleteGlobalRef(JNIEnv* env, jobject globalRef) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, globalRef);
+    if (globalRef != NULL && dvmGetJNIRefType(sc.self(), globalRef) != JNIGlobalRefType) {
+        ALOGW("JNI WARNING: DeleteGlobalRef on non-global %p (type=%d)",
+             globalRef, dvmGetJNIRefType(sc.self(), globalRef));
+        abortMaybe();
+    } else {
+        baseEnv(env)->DeleteGlobalRef(env, globalRef);
+        CHECK_JNI_EXIT_VOID();
+    }
+}
+
+static jobject Check_NewLocalRef(JNIEnv* env, jobject ref) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, ref);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewLocalRef(env, ref));
+}
+
+static void Check_DeleteLocalRef(JNIEnv* env, jobject localRef) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, localRef);
+    if (localRef != NULL && dvmGetJNIRefType(sc.self(), localRef) != JNILocalRefType) {
+        ALOGW("JNI WARNING: DeleteLocalRef on non-local %p (type=%d)",
+             localRef, dvmGetJNIRefType(sc.self(), localRef));
+        abortMaybe();
+    } else {
+        baseEnv(env)->DeleteLocalRef(env, localRef);
+        CHECK_JNI_EXIT_VOID();
+    }
+}
+
+static jint Check_EnsureLocalCapacity(JNIEnv *env, jint capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EI", env, capacity);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->EnsureLocalCapacity(env, capacity));
+}
+
+static jboolean Check_IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
+    CHECK_JNI_ENTRY(kFlag_Default, "ELL", env, ref1, ref2);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsSameObject(env, ref1, ref2));
+}
+
+static jobject Check_AllocObject(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->AllocObject(env, clazz));
+}
+
+static jobject Check_NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    va_list args;
+    va_start(args, methodID);
+    jobject result = baseEnv(env)->NewObjectV(env, clazz, methodID, args);
+    va_end(args);
+    return CHECK_JNI_EXIT("L", result);
+}
+
+static jobject Check_NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectV(env, clazz, methodID, args));
+}
+
+static jobject Check_NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID, jvalue* args) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectA(env, clazz, methodID, args));
+}
+
+static jclass Check_GetObjectClass(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("c", baseEnv(env)->GetObjectClass(env, obj));
+}
+
+static jboolean Check_IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "ELc", env, obj, clazz);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->IsInstanceOf(env, obj, clazz));
+}
+
+static jmethodID Check_GetMethodID(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("m", baseEnv(env)->GetMethodID(env, clazz, name, sig));
+}
+
+static jfieldID Check_GetFieldID(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("f", baseEnv(env)->GetFieldID(env, clazz, name, sig));
+}
+
+static jmethodID Check_GetStaticMethodID(JNIEnv* env, jclass clazz,
+        const char* name, const char* sig)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("m", baseEnv(env)->GetStaticMethodID(env, clazz, name, sig));
+}
+
+static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz,
+        const char* name, const char* sig)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, clazz, name, sig);
+    return CHECK_JNI_EXIT("f", baseEnv(env)->GetStaticFieldID(env, clazz, name, sig));
+}
+
+#define FIELD_ACCESSORS(_ctype, _jname, _ftype, _type) \
+    static _ctype Check_GetStatic##_jname##Field(JNIEnv* env, jclass clazz, jfieldID fieldID) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecf", env, clazz, fieldID); \
+        sc.checkStaticFieldID(clazz, fieldID); \
+        sc.checkFieldTypeForGet(fieldID, _type, true); \
+        return CHECK_JNI_EXIT(_type, baseEnv(env)->GetStatic##_jname##Field(env, clazz, fieldID)); \
+    } \
+    static _ctype Check_Get##_jname##Field(JNIEnv* env, jobject obj, jfieldID fieldID) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELf", env, obj, fieldID); \
+        sc.checkInstanceFieldID(obj, fieldID); \
+        sc.checkFieldTypeForGet(fieldID, _type, false); \
+        return CHECK_JNI_EXIT(_type, baseEnv(env)->Get##_jname##Field(env, obj, fieldID)); \
+    } \
+    static void Check_SetStatic##_jname##Field(JNIEnv* env, jclass clazz, jfieldID fieldID, _ctype value) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecf" _type, env, clazz, fieldID, value); \
+        sc.checkStaticFieldID(clazz, fieldID); \
+        /* "value" arg only used when type == ref */ \
+        sc.checkFieldTypeForSet((jobject)(u4)value, fieldID, _ftype, true); \
+        baseEnv(env)->SetStatic##_jname##Field(env, clazz, fieldID, value); \
+        CHECK_JNI_EXIT_VOID(); \
+    } \
+    static void Check_Set##_jname##Field(JNIEnv* env, jobject obj, jfieldID fieldID, _ctype value) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELf" _type, env, obj, fieldID, value); \
+        sc.checkInstanceFieldID(obj, fieldID); \
+        /* "value" arg only used when type == ref */ \
+        sc.checkFieldTypeForSet((jobject)(u4) value, fieldID, _ftype, false); \
+        baseEnv(env)->Set##_jname##Field(env, obj, fieldID, value); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+FIELD_ACCESSORS(jobject, Object, PRIM_NOT, "L");
+FIELD_ACCESSORS(jboolean, Boolean, PRIM_BOOLEAN, "Z");
+FIELD_ACCESSORS(jbyte, Byte, PRIM_BYTE, "B");
+FIELD_ACCESSORS(jchar, Char, PRIM_CHAR, "C");
+FIELD_ACCESSORS(jshort, Short, PRIM_SHORT, "S");
+FIELD_ACCESSORS(jint, Int, PRIM_INT, "I");
+FIELD_ACCESSORS(jlong, Long, PRIM_LONG, "J");
+FIELD_ACCESSORS(jfloat, Float, PRIM_FLOAT, "F");
+FIELD_ACCESSORS(jdouble, Double, PRIM_DOUBLE, "D");
+
+#define CALL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
+    /* Virtual... */ \
+    static _ctype Check_Call##_jname##Method(JNIEnv* env, jobject obj, \
+        jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->Call##_jname##MethodV(env, obj, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_Call##_jname##MethodV(JNIEnv* env, jobject obj, \
+        jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->Call##_jname##MethodV(env, obj, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_Call##_jname##MethodA(JNIEnv* env, jobject obj, \
+        jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->Call##_jname##MethodA(env, obj, methodID, args); \
+        _retok; \
+    } \
+    /* Non-virtual... */ \
+    static _ctype Check_CallNonvirtual##_jname##Method(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, clazz, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_CallNonvirtual##_jname##MethodV(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, clazz, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_CallNonvirtual##_jname##MethodA(JNIEnv* env, \
+        jobject obj, jclass clazz, jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, false); \
+        sc.checkVirtualMethod(obj, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallNonvirtual##_jname##MethodA(env, obj, clazz, methodID, args); \
+        _retok; \
+    } \
+    /* Static... */ \
+    static _ctype Check_CallStatic##_jname##Method(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, ...) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        va_list args; \
+        va_start(args, methodID); \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodV(env, clazz, methodID, args); \
+        va_end(args); \
+        _retok; \
+    } \
+    static _ctype Check_CallStatic##_jname##MethodV(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, va_list args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodV(env, clazz, methodID, args); \
+        _retok; \
+    } \
+    static _ctype Check_CallStatic##_jname##MethodA(JNIEnv* env, \
+        jclass clazz, jmethodID methodID, jvalue* args) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, clazz, methodID); /* TODO: args! */ \
+        sc.checkSig(methodID, _retsig, true); \
+        sc.checkStaticMethod(clazz, methodID); \
+        _retdecl; \
+        _retasgn baseEnv(env)->CallStatic##_jname##MethodA(env, clazz, methodID, args); \
+        _retok; \
+    }
+
+#define NON_VOID_RETURN(_retsig, _ctype) return CHECK_JNI_EXIT(_retsig, (_ctype) result)
+#define VOID_RETURN CHECK_JNI_EXIT_VOID()
+
+CALL(jobject, Object, Object* result, result=(Object*), NON_VOID_RETURN("L", jobject), "L");
+CALL(jboolean, Boolean, jboolean result, result=, NON_VOID_RETURN("Z", jboolean), "Z");
+CALL(jbyte, Byte, jbyte result, result=, NON_VOID_RETURN("B", jbyte), "B");
+CALL(jchar, Char, jchar result, result=, NON_VOID_RETURN("C", jchar), "C");
+CALL(jshort, Short, jshort result, result=, NON_VOID_RETURN("S", jshort), "S");
+CALL(jint, Int, jint result, result=, NON_VOID_RETURN("I", jint), "I");
+CALL(jlong, Long, jlong result, result=, NON_VOID_RETURN("J", jlong), "J");
+CALL(jfloat, Float, jfloat result, result=, NON_VOID_RETURN("F", jfloat), "F");
+CALL(jdouble, Double, jdouble result, result=, NON_VOID_RETURN("D", jdouble), "D");
+CALL(void, Void, , , VOID_RETURN, "V");
+
+static jstring Check_NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Epz", env, unicodeChars, len);
+    return CHECK_JNI_EXIT("s", baseEnv(env)->NewString(env, unicodeChars, len));
+}
+
+static jsize Check_GetStringLength(JNIEnv* env, jstring string) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringLength(env, string));
+}
+
+static const jchar* Check_GetStringChars(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
+    const jchar* result = baseEnv(env)->GetStringChars(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        ScopedCheckJniThreadState ts(env);
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(dvmThreadSelf(), string);
+        int byteCount = strObj->length() * 2;
+        result = (const jchar*) GuardedCopy::create(result, byteCount, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Esp", env, string, chars);
+    sc.checkNonNull(chars);
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(chars, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringChars");
+            abortMaybe();
+            return;
+        }
+        chars = (const jchar*) GuardedCopy::destroy((jchar*)chars);
+    }
+    baseEnv(env)->ReleaseStringChars(env, string, chars);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jstring Check_NewStringUTF(JNIEnv* env, const char* bytes) {
+    CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, bytes); // TODO: show pointer and truncate string.
+    return CHECK_JNI_EXIT("s", baseEnv(env)->NewStringUTF(env, bytes));
+}
+
+static jsize Check_GetStringUTFLength(JNIEnv* env, jstring string) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringUTFLength(env, string));
+}
+
+static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
+    const char* result = baseEnv(env)->GetStringUTFChars(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        result = (const char*) GuardedCopy::create(result, strlen(result) + 1, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("u", result); // TODO: show pointer and truncate string.
+}
+
+static void Check_ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
+    CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_Release, "Esu", env, string, utf); // TODO: show pointer and truncate string.
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(utf, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringUTFChars");
+            abortMaybe();
+            return;
+        }
+        utf = (const char*) GuardedCopy::destroy((char*)utf);
+    }
+    baseEnv(env)->ReleaseStringUTFChars(env, string, utf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jsize Check_GetArrayLength(JNIEnv* env, jarray array) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "Ea", env, array);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetArrayLength(env, array));
+}
+
+static jobjectArray Check_NewObjectArray(JNIEnv* env, jsize length,
+        jclass elementClass, jobject initialElement)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EzcL", env, length, elementClass, initialElement);
+    return CHECK_JNI_EXIT("a", baseEnv(env)->NewObjectArray(env, length, elementClass, initialElement));
+}
+
+static jobject Check_GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EaI", env, array, index);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->GetObjectArrayElement(env, array, index));
+}
+
+static void Check_SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EaIL", env, array, index, value);
+    baseEnv(env)->SetObjectArrayElement(env, array, index, value);
+    CHECK_JNI_EXIT_VOID();
+}
+
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
+    static _artype Check_New##_jname##Array(JNIEnv* env, jsize length) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Ez", env, length); \
+        return CHECK_JNI_EXIT("a", baseEnv(env)->New##_jname##Array(env, length)); \
+    }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
+NEW_PRIMITIVE_ARRAY(jcharArray, Char);
+NEW_PRIMITIVE_ARRAY(jshortArray, Short);
+NEW_PRIMITIVE_ARRAY(jintArray, Int);
+NEW_PRIMITIVE_ARRAY(jlongArray, Long);
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
+
+
+/*
+ * Hack to allow forcecopy to work with jniGetNonMovableArrayElements.
+ * The code deliberately uses an invalid sequence of operations, so we
+ * need to pass it through unmodified.  Review that code before making
+ * any changes here.
+ */
+#define kNoCopyMagic    0xd5aab57f
+
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static _ctype* Check_Get##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array array, jboolean* isCopy) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default, "Eap", env, array, isCopy); \
+        u4 noCopy = 0; \
+        if (gDvmJni.forceCopy && isCopy != NULL) { \
+            /* capture this before the base call tramples on it */ \
+            noCopy = *(u4*) isCopy; \
+        } \
+        _ctype* result = baseEnv(env)->Get##_jname##ArrayElements(env, array, isCopy); \
+        if (gDvmJni.forceCopy && result != NULL) { \
+            if (noCopy == kNoCopyMagic) { \
+                ALOGV("FC: not copying %p %x", array, noCopy); \
+            } else { \
+                result = (_ctype*) createGuardedPACopy(env, array, isCopy); \
+            } \
+        } \
+        return CHECK_JNI_EXIT("p", result); \
+    }
+
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static void Check_Release##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array array, _ctype* elems, jint mode) \
+    { \
+        CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Eapr", env, array, elems, mode); \
+        sc.checkNonNull(elems); \
+        if (gDvmJni.forceCopy) { \
+            if ((uintptr_t)elems == kNoCopyMagic) { \
+                ALOGV("FC: not freeing %p", array); \
+                elems = NULL;   /* base JNI call doesn't currently need */ \
+            } else { \
+                elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode); \
+            } \
+        } \
+        baseEnv(env)->Release##_jname##ArrayElements(env, array, elems, mode); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Check_Get##_jname##ArrayRegion(JNIEnv* env, \
+            _ctype##Array array, jsize start, jsize len, _ctype* buf) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
+        baseEnv(env)->Get##_jname##ArrayRegion(env, array, start, len, buf); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Check_Set##_jname##ArrayRegion(JNIEnv* env, \
+            _ctype##Array array, jsize start, jsize len, const _ctype* buf) { \
+        CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
+        baseEnv(env)->Set##_jname##ArrayRegion(env, array, start, len, buf); \
+        CHECK_JNI_EXIT_VOID(); \
+    }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
+    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
+    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+/* TODO: verify primitive array type matches call type */
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
+
+static jint Check_RegisterNatives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods,
+        jint nMethods)
+{
+    CHECK_JNI_ENTRY(kFlag_Default, "EcpI", env, clazz, methods, nMethods);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->RegisterNatives(env, clazz, methods, nMethods));
+}
+
+static jint Check_UnregisterNatives(JNIEnv* env, jclass clazz) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, clazz);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->UnregisterNatives(env, clazz));
+}
+
+static jint Check_MonitorEnter(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorEnter(env, obj));
+}
+
+static jint Check_MonitorExit(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorExit(env, obj));
+}
+
+static jint Check_GetJavaVM(JNIEnv *env, JavaVM **vm) {
+    CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, vm);
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetJavaVM(env, vm));
+}
+
+static void Check_GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
+    baseEnv(env)->GetStringRegion(env, str, start, len, buf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void Check_GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
+    baseEnv(env)->GetStringUTFRegion(env, str, start, len, buf);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static void* Check_GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritGet, "Eap", env, array, isCopy);
+    void* result = baseEnv(env)->GetPrimitiveArrayCritical(env, array, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        result = createGuardedPACopy(env, array, isCopy);
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode)
+{
+    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Eapr", env, array, carray, mode);
+    sc.checkNonNull(carray);
+    if (gDvmJni.forceCopy) {
+        carray = releaseGuardedPACopy(env, array, carray, mode);
+    }
+    baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string, jboolean* isCopy) {
+    CHECK_JNI_ENTRY(kFlag_CritGet, "Esp", env, string, isCopy);
+    const jchar* result = baseEnv(env)->GetStringCritical(env, string, isCopy);
+    if (gDvmJni.forceCopy && result != NULL) {
+        ScopedCheckJniThreadState ts(env);
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(dvmThreadSelf(), string);
+        int byteCount = strObj->length() * 2;
+        result = (const jchar*) GuardedCopy::create(result, byteCount, false);
+        if (isCopy != NULL) {
+            *isCopy = JNI_TRUE;
+        }
+    }
+    return CHECK_JNI_EXIT("p", result);
+}
+
+static void Check_ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* carray) {
+    CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Esp", env, string, carray);
+    sc.checkNonNull(carray);
+    if (gDvmJni.forceCopy) {
+        if (!GuardedCopy::check(carray, false)) {
+            ALOGE("JNI: failed guarded copy check in ReleaseStringCritical");
+            abortMaybe();
+            return;
+        }
+        carray = (const jchar*) GuardedCopy::destroy((jchar*)carray);
+    }
+    baseEnv(env)->ReleaseStringCritical(env, string, carray);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jweak Check_NewWeakGlobalRef(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewWeakGlobalRef(env, obj));
+}
+
+static void Check_DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
+    CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
+    baseEnv(env)->DeleteWeakGlobalRef(env, obj);
+    CHECK_JNI_EXIT_VOID();
+}
+
+static jboolean Check_ExceptionCheck(JNIEnv* env) {
+    CHECK_JNI_ENTRY(kFlag_CritOkay | kFlag_ExcepOkay, "E", env);
+    return CHECK_JNI_EXIT("b", baseEnv(env)->ExceptionCheck(env));
+}
+
+static jobjectRefType Check_GetObjectRefType(JNIEnv* env, jobject obj) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
+    // TODO: proper decoding of jobjectRefType!
+    return CHECK_JNI_EXIT("I", baseEnv(env)->GetObjectRefType(env, obj));
+}
+
+static jobject Check_NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity);
+    return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
+}
+
+static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
+    // TODO: check that 'buf' is a java.nio.Buffer.
+    return CHECK_JNI_EXIT("p", baseEnv(env)->GetDirectBufferAddress(env, buf));
+}
+
+static jlong Check_GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
+    CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
+    // TODO: check that 'buf' is a java.nio.Buffer.
+    return CHECK_JNI_EXIT("J", baseEnv(env)->GetDirectBufferCapacity(env, buf));
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI invocation functions
+ * ===========================================================================
+ */
+
+static jint Check_DestroyJavaVM(JavaVM* vm) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->DestroyJavaVM(vm));
+}
+
+static jint Check_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "vpt", vm, p_env, thr_args);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
+}
+
+static jint Check_AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    ScopedCheck sc(false, __FUNCTION__);
+    sc.check(true, "vpt", vm, p_env, thr_args);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
+}
+
+static jint Check_DetachCurrentThread(JavaVM* vm) {
+    ScopedCheck sc(true, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->DetachCurrentThread(vm));
+}
+
+static jint Check_GetEnv(JavaVM* vm, void** env, jint version) {
+    ScopedCheck sc(true, __FUNCTION__);
+    sc.check(true, "v", vm);
+    return CHECK_JNI_EXIT("I", baseVm(vm)->GetEnv(vm, env, version));
+}
+
+
+/*
+ * ===========================================================================
+ *      Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gCheckNativeInterface = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    Check_GetVersion,
+
+    Check_DefineClass,
+    Check_FindClass,
+
+    Check_FromReflectedMethod,
+    Check_FromReflectedField,
+    Check_ToReflectedMethod,
+
+    Check_GetSuperclass,
+    Check_IsAssignableFrom,
+
+    Check_ToReflectedField,
+
+    Check_Throw,
+    Check_ThrowNew,
+    Check_ExceptionOccurred,
+    Check_ExceptionDescribe,
+    Check_ExceptionClear,
+    Check_FatalError,
+
+    Check_PushLocalFrame,
+    Check_PopLocalFrame,
+
+    Check_NewGlobalRef,
+    Check_DeleteGlobalRef,
+    Check_DeleteLocalRef,
+    Check_IsSameObject,
+    Check_NewLocalRef,
+    Check_EnsureLocalCapacity,
+
+    Check_AllocObject,
+    Check_NewObject,
+    Check_NewObjectV,
+    Check_NewObjectA,
+
+    Check_GetObjectClass,
+    Check_IsInstanceOf,
+
+    Check_GetMethodID,
+
+    Check_CallObjectMethod,
+    Check_CallObjectMethodV,
+    Check_CallObjectMethodA,
+    Check_CallBooleanMethod,
+    Check_CallBooleanMethodV,
+    Check_CallBooleanMethodA,
+    Check_CallByteMethod,
+    Check_CallByteMethodV,
+    Check_CallByteMethodA,
+    Check_CallCharMethod,
+    Check_CallCharMethodV,
+    Check_CallCharMethodA,
+    Check_CallShortMethod,
+    Check_CallShortMethodV,
+    Check_CallShortMethodA,
+    Check_CallIntMethod,
+    Check_CallIntMethodV,
+    Check_CallIntMethodA,
+    Check_CallLongMethod,
+    Check_CallLongMethodV,
+    Check_CallLongMethodA,
+    Check_CallFloatMethod,
+    Check_CallFloatMethodV,
+    Check_CallFloatMethodA,
+    Check_CallDoubleMethod,
+    Check_CallDoubleMethodV,
+    Check_CallDoubleMethodA,
+    Check_CallVoidMethod,
+    Check_CallVoidMethodV,
+    Check_CallVoidMethodA,
+
+    Check_CallNonvirtualObjectMethod,
+    Check_CallNonvirtualObjectMethodV,
+    Check_CallNonvirtualObjectMethodA,
+    Check_CallNonvirtualBooleanMethod,
+    Check_CallNonvirtualBooleanMethodV,
+    Check_CallNonvirtualBooleanMethodA,
+    Check_CallNonvirtualByteMethod,
+    Check_CallNonvirtualByteMethodV,
+    Check_CallNonvirtualByteMethodA,
+    Check_CallNonvirtualCharMethod,
+    Check_CallNonvirtualCharMethodV,
+    Check_CallNonvirtualCharMethodA,
+    Check_CallNonvirtualShortMethod,
+    Check_CallNonvirtualShortMethodV,
+    Check_CallNonvirtualShortMethodA,
+    Check_CallNonvirtualIntMethod,
+    Check_CallNonvirtualIntMethodV,
+    Check_CallNonvirtualIntMethodA,
+    Check_CallNonvirtualLongMethod,
+    Check_CallNonvirtualLongMethodV,
+    Check_CallNonvirtualLongMethodA,
+    Check_CallNonvirtualFloatMethod,
+    Check_CallNonvirtualFloatMethodV,
+    Check_CallNonvirtualFloatMethodA,
+    Check_CallNonvirtualDoubleMethod,
+    Check_CallNonvirtualDoubleMethodV,
+    Check_CallNonvirtualDoubleMethodA,
+    Check_CallNonvirtualVoidMethod,
+    Check_CallNonvirtualVoidMethodV,
+    Check_CallNonvirtualVoidMethodA,
+
+    Check_GetFieldID,
+
+    Check_GetObjectField,
+    Check_GetBooleanField,
+    Check_GetByteField,
+    Check_GetCharField,
+    Check_GetShortField,
+    Check_GetIntField,
+    Check_GetLongField,
+    Check_GetFloatField,
+    Check_GetDoubleField,
+    Check_SetObjectField,
+    Check_SetBooleanField,
+    Check_SetByteField,
+    Check_SetCharField,
+    Check_SetShortField,
+    Check_SetIntField,
+    Check_SetLongField,
+    Check_SetFloatField,
+    Check_SetDoubleField,
+
+    Check_GetStaticMethodID,
+
+    Check_CallStaticObjectMethod,
+    Check_CallStaticObjectMethodV,
+    Check_CallStaticObjectMethodA,
+    Check_CallStaticBooleanMethod,
+    Check_CallStaticBooleanMethodV,
+    Check_CallStaticBooleanMethodA,
+    Check_CallStaticByteMethod,
+    Check_CallStaticByteMethodV,
+    Check_CallStaticByteMethodA,
+    Check_CallStaticCharMethod,
+    Check_CallStaticCharMethodV,
+    Check_CallStaticCharMethodA,
+    Check_CallStaticShortMethod,
+    Check_CallStaticShortMethodV,
+    Check_CallStaticShortMethodA,
+    Check_CallStaticIntMethod,
+    Check_CallStaticIntMethodV,
+    Check_CallStaticIntMethodA,
+    Check_CallStaticLongMethod,
+    Check_CallStaticLongMethodV,
+    Check_CallStaticLongMethodA,
+    Check_CallStaticFloatMethod,
+    Check_CallStaticFloatMethodV,
+    Check_CallStaticFloatMethodA,
+    Check_CallStaticDoubleMethod,
+    Check_CallStaticDoubleMethodV,
+    Check_CallStaticDoubleMethodA,
+    Check_CallStaticVoidMethod,
+    Check_CallStaticVoidMethodV,
+    Check_CallStaticVoidMethodA,
+
+    Check_GetStaticFieldID,
+
+    Check_GetStaticObjectField,
+    Check_GetStaticBooleanField,
+    Check_GetStaticByteField,
+    Check_GetStaticCharField,
+    Check_GetStaticShortField,
+    Check_GetStaticIntField,
+    Check_GetStaticLongField,
+    Check_GetStaticFloatField,
+    Check_GetStaticDoubleField,
+
+    Check_SetStaticObjectField,
+    Check_SetStaticBooleanField,
+    Check_SetStaticByteField,
+    Check_SetStaticCharField,
+    Check_SetStaticShortField,
+    Check_SetStaticIntField,
+    Check_SetStaticLongField,
+    Check_SetStaticFloatField,
+    Check_SetStaticDoubleField,
+
+    Check_NewString,
+
+    Check_GetStringLength,
+    Check_GetStringChars,
+    Check_ReleaseStringChars,
+
+    Check_NewStringUTF,
+    Check_GetStringUTFLength,
+    Check_GetStringUTFChars,
+    Check_ReleaseStringUTFChars,
+
+    Check_GetArrayLength,
+    Check_NewObjectArray,
+    Check_GetObjectArrayElement,
+    Check_SetObjectArrayElement,
+
+    Check_NewBooleanArray,
+    Check_NewByteArray,
+    Check_NewCharArray,
+    Check_NewShortArray,
+    Check_NewIntArray,
+    Check_NewLongArray,
+    Check_NewFloatArray,
+    Check_NewDoubleArray,
+
+    Check_GetBooleanArrayElements,
+    Check_GetByteArrayElements,
+    Check_GetCharArrayElements,
+    Check_GetShortArrayElements,
+    Check_GetIntArrayElements,
+    Check_GetLongArrayElements,
+    Check_GetFloatArrayElements,
+    Check_GetDoubleArrayElements,
+
+    Check_ReleaseBooleanArrayElements,
+    Check_ReleaseByteArrayElements,
+    Check_ReleaseCharArrayElements,
+    Check_ReleaseShortArrayElements,
+    Check_ReleaseIntArrayElements,
+    Check_ReleaseLongArrayElements,
+    Check_ReleaseFloatArrayElements,
+    Check_ReleaseDoubleArrayElements,
+
+    Check_GetBooleanArrayRegion,
+    Check_GetByteArrayRegion,
+    Check_GetCharArrayRegion,
+    Check_GetShortArrayRegion,
+    Check_GetIntArrayRegion,
+    Check_GetLongArrayRegion,
+    Check_GetFloatArrayRegion,
+    Check_GetDoubleArrayRegion,
+    Check_SetBooleanArrayRegion,
+    Check_SetByteArrayRegion,
+    Check_SetCharArrayRegion,
+    Check_SetShortArrayRegion,
+    Check_SetIntArrayRegion,
+    Check_SetLongArrayRegion,
+    Check_SetFloatArrayRegion,
+    Check_SetDoubleArrayRegion,
+
+    Check_RegisterNatives,
+    Check_UnregisterNatives,
+
+    Check_MonitorEnter,
+    Check_MonitorExit,
+
+    Check_GetJavaVM,
+
+    Check_GetStringRegion,
+    Check_GetStringUTFRegion,
+
+    Check_GetPrimitiveArrayCritical,
+    Check_ReleasePrimitiveArrayCritical,
+
+    Check_GetStringCritical,
+    Check_ReleaseStringCritical,
+
+    Check_NewWeakGlobalRef,
+    Check_DeleteWeakGlobalRef,
+
+    Check_ExceptionCheck,
+
+    Check_NewDirectByteBuffer,
+    Check_GetDirectBufferAddress,
+    Check_GetDirectBufferCapacity,
+
+    Check_GetObjectRefType
+};
+
+static const struct JNIInvokeInterface gCheckInvokeInterface = {
+    NULL,
+    NULL,
+    NULL,
+
+    Check_DestroyJavaVM,
+    Check_AttachCurrentThread,
+    Check_DetachCurrentThread,
+
+    Check_GetEnv,
+
+    Check_AttachCurrentThreadAsDaemon,
+};
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv) {
+    assert(pEnv->funcTable != &gCheckNativeInterface);
+    pEnv->baseFuncTable = pEnv->funcTable;
+    pEnv->funcTable = &gCheckNativeInterface;
+}
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniVm(JavaVMExt* pVm) {
+    assert(pVm->funcTable != &gCheckInvokeInterface);
+    pVm->baseFuncTable = pVm->funcTable;
+    pVm->funcTable = &gCheckInvokeInterface;
+}
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
new file mode 100644
index 0000000..27174e7
--- /dev/null
+++ b/vm/Dalvik.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * All-inclusive internal header file.  Include this to get everything useful.
+ */
+#ifndef DALVIK_DALVIK_H_
+#define DALVIK_DALVIK_H_
+
+#include "Common.h"
+#include "Inlines.h"
+#include "Misc.h"
+#include "Bits.h"
+#include "BitVector.h"
+#include "libdex/SysUtil.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexFile.h"
+#include "libdex/DexProto.h"
+#include "libdex/DexUtf.h"
+#include "DvmDex.h"
+#include "RawDexFile.h"
+#include "Sync.h"
+#include "oo/Object.h"
+#include "Native.h"
+#include "native/InternalNative.h"
+
+#include "DalvikVersion.h"
+#include "Debugger.h"
+#include "Profile.h"
+#include "UtfString.h"
+#include "Intern.h"
+#include "ReferenceTable.h"
+#include "IndirectRefTable.h"
+#include "AtomicCache.h"
+#include "Thread.h"
+#include "Ddm.h"
+#include "Hash.h"
+#include "interp/Stack.h"
+#include "oo/Class.h"
+#include "oo/Resolve.h"
+#include "oo/Array.h"
+#include "Exception.h"
+#include "alloc/Alloc.h"
+#include "alloc/CardTable.h"
+#include "alloc/HeapDebug.h"
+#include "alloc/WriteBarrier.h"
+#include "oo/AccessCheck.h"
+#include "JarFile.h"
+#include "jdwp/Jdwp.h"
+#include "SignalCatcher.h"
+#include "StdioConverter.h"
+#include "JniInternal.h"
+#include "LinearAlloc.h"
+#include "analysis/DexVerify.h"
+#include "analysis/DexPrepare.h"
+#include "analysis/RegisterMap.h"
+#include "Init.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/InstrUtils.h"
+#include "AllocTracker.h"
+#include "PointerSet.h"
+#if defined(WITH_JIT)
+#include "compiler/Compiler.h"
+#endif
+#include "Globals.h"
+#include "reflect/Reflect.h"
+#include "oo/TypeCheck.h"
+#include "Atomic.h"
+#include "interp/Interp.h"
+#include "InlineNative.h"
+#include "oo/ObjectInlines.h"
+
+#endif  // DALVIK_DALVIK_H_
diff --git a/vm/Ddm.cpp b/vm/Ddm.cpp
new file mode 100644
index 0000000..d441ec4
--- /dev/null
+++ b/vm/Ddm.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Handle Dalvik Debug Monitor requests and events.
+ *
+ * Remember that all DDM traffic is big-endian since it travels over the
+ * JDWP connection.
+ */
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
+ * need to process each, accumulate the replies, and ship the whole thing
+ * back.
+ *
+ * Returns "true" if we have a reply.  The reply buffer is newly allocated,
+ * and includes the chunk type/length, followed by the data.
+ *
+ * TODO: we currently assume that the request and reply include a single
+ * chunk.  If this becomes inconvenient we will need to adapt.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen)
+{
+    Thread* self = dvmThreadSelf();
+    const int kChunkHdrLen = 8;
+    ArrayObject* dataArray = NULL;
+    Object* chunk = NULL;
+    bool result = false;
+
+    assert(dataLen >= 0);
+
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
+        if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
+            dvmLogExceptionStackTrace();
+            dvmClearException(self);
+            goto bail;
+        }
+    }
+
+    /*
+     * The chunk handlers are written in the Java programming language, so
+     * we need to convert the buffer to a byte array.
+     */
+    dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
+    if (dataArray == NULL) {
+        ALOGW("array alloc failed (%d)", dataLen);
+        dvmClearException(self);
+        goto bail;
+    }
+    memcpy(dataArray->contents, buf, dataLen);
+
+    /*
+     * Run through and find all chunks.  [Currently just find the first.]
+     */
+    unsigned int offset, length, type;
+    type = get4BE((u1*)dataArray->contents + 0);
+    length = get4BE((u1*)dataArray->contents + 4);
+    offset = kChunkHdrLen;
+    if (offset+length > (unsigned int) dataLen) {
+        ALOGW("WARNING: bad chunk found (len=%u pktLen=%d)", length, dataLen);
+        goto bail;
+    }
+
+    /*
+     * Call the handler.
+     */
+    JValue callRes;
+    dvmCallMethod(self, gDvm.methDalvikDdmcServer_dispatch, NULL, &callRes,
+        type, dataArray, offset, length);
+    if (dvmCheckException(self)) {
+        ALOGI("Exception thrown by dispatcher for 0x%08x", type);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        goto bail;
+    }
+
+    ArrayObject* replyData;
+    chunk = (Object*) callRes.l;
+    if (chunk == NULL)
+        goto bail;
+
+    /* not strictly necessary -- we don't alloc from managed heap here */
+    dvmAddTrackedAlloc(chunk, self);
+
+    /*
+     * Pull the pieces out of the chunk.  We copy the results into a
+     * newly-allocated buffer that the caller can free.  We don't want to
+     * continue using the Chunk object because nothing has a reference to it.
+     *
+     * We could avoid this by returning type/data/offset/length and having
+     * the caller be aware of the object lifetime issues, but that
+     * integrates the JDWP code more tightly into the VM, and doesn't work
+     * if we have responses for multiple chunks.
+     *
+     * So we're pretty much stuck with copying data around multiple times.
+     */
+    type = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_type);
+    replyData =
+        (ArrayObject*) dvmGetFieldObject(chunk, gDvm.offDalvikDdmcChunk_data);
+    offset = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_offset);
+    length = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_length);
+
+    ALOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d",
+        type, replyData, offset, length);
+
+    if (length == 0 || replyData == NULL)
+        goto bail;
+    if (offset + length > replyData->length) {
+        ALOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d",
+            offset, length, replyData->length);
+        goto bail;
+    }
+
+    u1* reply;
+    reply = (u1*) malloc(length + kChunkHdrLen);
+    if (reply == NULL) {
+        ALOGW("malloc %d failed", length+kChunkHdrLen);
+        goto bail;
+    }
+    set4BE(reply + 0, type);
+    set4BE(reply + 4, length);
+    memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);
+
+    *pReplyBuf = reply;
+    *pReplyLen = length + kChunkHdrLen;
+    result = true;
+
+    ALOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d",
+        (char*) reply, reply, length);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) dataArray, self);
+    dvmReleaseTrackedAlloc(chunk, self);
+    return result;
+}
+
+/* defined in org.apache.harmony.dalvik.ddmc.DdmServer */
+#define CONNECTED       1
+#define DISCONNECTED    2
+
+/*
+ * Broadcast an event to all handlers.
+ */
+static void broadcast(int event)
+{
+    Thread* self = dvmThreadSelf();
+
+    if (self->status != THREAD_RUNNING) {
+        ALOGE("ERROR: DDM broadcast with thread status=%d", self->status);
+        /* try anyway? */
+    }
+
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
+        if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
+            dvmLogExceptionStackTrace();
+            dvmClearException(self);
+            return;
+        }
+    }
+
+    JValue unused;
+    dvmCallMethod(self, gDvm.methDalvikDdmcServer_broadcast, NULL, &unused,
+        event);
+    if (dvmCheckException(self)) {
+        ALOGI("Exception thrown by broadcast(%d)", event);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        return;
+    }
+}
+
+/*
+ * First DDM packet has arrived over JDWP.  Notify the press.
+ *
+ * We can do some initialization here too.
+ */
+void dvmDdmConnected()
+{
+    // TODO: any init
+
+    ALOGV("Broadcasting DDM connect");
+    broadcast(CONNECTED);
+}
+
+/*
+ * JDWP connection has dropped.
+ *
+ * Do some cleanup.
+ */
+void dvmDdmDisconnected()
+{
+    ALOGV("Broadcasting DDM disconnect");
+    broadcast(DISCONNECTED);
+
+    gDvm.ddmThreadNotification = false;
+}
+
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable)
+{
+    /*
+     * We lock the thread list to avoid sending duplicate events or missing
+     * a thread change.  We should be okay holding this lock while sending
+     * the messages out.  (We have to hold it while accessing a live thread.)
+     */
+    dvmLockThreadList(NULL);
+    gDvm.ddmThreadNotification = enable;
+
+    if (enable) {
+        Thread* thread;
+        for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            //ALOGW("notify %d", thread->threadId);
+            dvmDdmSendThreadNotification(thread, true);
+        }
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Send a notification when a thread starts or stops.
+ *
+ * Because we broadcast the full set of threads when the notifications are
+ * first enabled, it's possible for "thread" to be actively executing.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started)
+{
+    if (!gDvm.ddmThreadNotification) {
+        return;
+    }
+
+    StringObject* nameObj = NULL;
+    Object* threadObj = thread->threadObj;
+
+    if (threadObj != NULL) {
+        nameObj = (StringObject*)
+            dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
+    }
+
+    int type, len;
+    u1 buf[256];
+
+    if (started) {
+        const u2* chars;
+        u2* outChars;
+        size_t stringLen;
+
+        type = CHUNK_TYPE("THCR");
+
+        if (nameObj != NULL) {
+            stringLen = nameObj->length();
+            chars = nameObj->chars();
+        } else {
+            stringLen = 0;
+            chars = NULL;
+        }
+
+        /* leave room for the two integer fields */
+        if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) {
+            stringLen = (sizeof(buf) - sizeof(u4)*2) / 2;
+        }
+        len = stringLen*2 + sizeof(u4)*2;
+
+        set4BE(&buf[0x00], thread->threadId);
+        set4BE(&buf[0x04], stringLen);
+
+        /* copy the UTF-16 string, transforming to big-endian */
+        outChars = (u2*)(void*)&buf[0x08];
+        while (stringLen--) {
+            set2BE((u1*) (outChars++), *chars++);
+        }
+    } else {
+        type = CHUNK_TYPE("THDE");
+
+        len = 4;
+
+        set4BE(&buf[0x00], thread->threadId);
+    }
+
+    dvmDbgDdmSendChunk(type, len, buf);
+}
+
+/*
+ * Send a notification when a thread's name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName)
+{
+    if (!gDvm.ddmThreadNotification) {
+        return;
+    }
+
+    size_t stringLen = newName->length();
+    const u2* chars = newName->chars();
+
+    /*
+     * Output format:
+     *  (4b) thread ID
+     *  (4b) stringLen
+     *  (xb) string chars
+     */
+    int bufLen = 4 + 4 + (stringLen * 2);
+    u1 buf[bufLen];
+
+    set4BE(&buf[0x00], threadId);
+    set4BE(&buf[0x04], stringLen);
+    u2* outChars = (u2*)(void*)&buf[0x08];
+    while (stringLen--) {
+        set2BE((u1*) (outChars++), *chars++);
+    }
+
+    dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf);
+}
+
+/*
+ * Generate the contents of a THST chunk.  The data encompasses all known
+ * threads.
+ *
+ * Response has:
+ *  (1b) header len
+ *  (1b) bytes per entry
+ *  (2b) thread count
+ * Then, for each thread:
+ *  (4b) threadId
+ *  (1b) thread status
+ *  (4b) tid
+ *  (4b) utime
+ *  (4b) stime
+ *  (1b) is daemon?
+ *
+ * The length fields exist in anticipation of adding additional fields
+ * without wanting to break ddms or bump the full protocol version.  I don't
+ * think it warrants full versioning.  They might be extraneous and could
+ * be removed from a future version.
+ *
+ * Returns a new byte[] with the data inside, or NULL on failure.  The
+ * caller must call dvmReleaseTrackedAlloc() on the array.
+ */
+ArrayObject* dvmDdmGenerateThreadStats()
+{
+    const int kHeaderLen = 4;
+    const int kBytesPerEntry = 18;
+
+    dvmLockThreadList(NULL);
+
+    Thread* thread;
+    int threadCount = 0;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next)
+        threadCount++;
+
+    /*
+     * Create a temporary buffer.  We can't perform heap allocation with
+     * the thread list lock held (could cause a GC).  The output is small
+     * enough to sit on the stack.
+     */
+    int bufLen = kHeaderLen + threadCount * kBytesPerEntry;
+    u1 tmpBuf[bufLen];
+    u1* buf = tmpBuf;
+
+    set1(buf+0, kHeaderLen);
+    set1(buf+1, kBytesPerEntry);
+    set2BE(buf+2, (u2) threadCount);
+    buf += kHeaderLen;
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        bool isDaemon = false;
+
+        ProcStatData procStatData;
+        if (!dvmGetThreadStats(&procStatData, thread->systemTid)) {
+            /* failed; show zero */
+            memset(&procStatData, 0, sizeof(procStatData));
+        }
+
+        Object* threadObj = thread->threadObj;
+        if (threadObj != NULL) {
+            isDaemon = dvmGetFieldBoolean(threadObj,
+                            gDvm.offJavaLangThread_daemon);
+        }
+
+        set4BE(buf+0, thread->threadId);
+        set1(buf+4, thread->status);
+        set4BE(buf+5, thread->systemTid);
+        set4BE(buf+9, procStatData.utime);
+        set4BE(buf+13, procStatData.stime);
+        set1(buf+17, isDaemon);
+
+        buf += kBytesPerEntry;
+    }
+    dvmUnlockThreadList();
+
+
+    /*
+     * Create a byte array to hold the data.
+     */
+    ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT);
+    if (arrayObj != NULL)
+        memcpy(arrayObj->contents, tmpBuf, bufLen);
+    return arrayObj;
+}
+
+
+/*
+ * Find the specified thread and return its stack trace as an array of
+ * StackTraceElement objects.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int* traceBuf;
+
+    dvmLockThreadList(self);
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadId == threadId)
+            break;
+    }
+    if (thread == NULL) {
+        ALOGI("dvmDdmGetStackTraceById: threadid=%d not found", threadId);
+        dvmUnlockThreadList();
+        return NULL;
+    }
+
+    /*
+     * Suspend the thread, pull out the stack trace, then resume the thread
+     * and release the thread list lock.  If we're being asked to examine
+     * our own stack trace, skip the suspend/resume.
+     */
+    size_t stackDepth;
+    if (thread != self)
+        dvmSuspendThread(thread);
+    traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
+    if (thread != self)
+        dvmResumeThread(thread);
+    dvmUnlockThreadList();
+
+    /*
+     * Convert the raw buffer into an array of StackTraceElement.
+     */
+    ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+    free(traceBuf);
+    return trace;
+}
+
+/*
+ * Gather up the allocation data and copy it into a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations()
+{
+    u1* data;
+    size_t len;
+
+    if (!dvmGenerateTrackedAllocationReport(&data, &len)) {
+        /* assume OOM */
+        dvmThrowOutOfMemoryError("recent alloc native");
+        return NULL;
+    }
+
+    ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT);
+    if (arrayObj != NULL)
+        memcpy(arrayObj->contents, data, len);
+    return arrayObj;
+}
diff --git a/vm/Ddm.h b/vm/Ddm.h
new file mode 100644
index 0000000..fe2fe75
--- /dev/null
+++ b/vm/Ddm.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Dalvik Debug Monitor
+ */
+#ifndef DALVIK_DDM_H_
+#define DALVIK_DDM_H_
+
+#include <stdbool.h>
+#include "Thread.h"
+
+/*
+ * Handle a packet full of DDM goodness.
+ *
+ * Returns "true" if we have anything to say in return; in which case,
+ * "*pReplyBuf" and "*pReplyLen" will also be set.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen);
+
+/*
+ * Deal with the DDM server connecting and disconnecting.
+ */
+void dvmDdmConnected(void);
+void dvmDdmDisconnected(void);
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable);
+
+/*
+ * If thread start/stop notification is enabled, call this when threads
+ * are created or die.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started);
+
+/*
+ * If thread start/stop notification is enabled, call this when the
+ * thread name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName);
+
+/*
+ * Generate a byte[] full of thread stats for a THST packet.
+ */
+ArrayObject* dvmDdmGenerateThreadStats(void);
+
+/*
+ * Let the heap know that the HPIF when value has changed.
+ *
+ * @return true iff the when value is supported by the VM.
+ */
+bool dvmDdmHandleHpifChunk(int when);
+
+/*
+ * Let the heap know that the HPSG or NHSG what/when values have changed.
+ *
+ * @param native false for an HPSG chunk, true for an NHSG chunk
+ *
+ * @return true iff the what/when values are supported by the VM.
+ */
+bool dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native);
+
+/*
+ * Get an array of StackTraceElement objects for the specified thread.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId);
+
+/*
+ * Gather up recent allocation data and return it in a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations(void);
+
+#endif  // DALVIK_DDM_H_
diff --git a/vm/Debugger.cpp b/vm/Debugger.cpp
new file mode 100644
index 0000000..5c44f93
--- /dev/null
+++ b/vm/Debugger.cpp
@@ -0,0 +1,2967 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Link between JDWP and the VM.  The code here only runs as a result of
+ * requests from the debugger, so speed is not essential.  Maintaining
+ * isolation of the JDWP code should make it easier to maintain and reuse.
+ *
+ * Collecting all debugger-related pieces here will also allow us to #ifdef
+ * the JDWP code out of release builds.
+ */
+#include "Dalvik.h"
+
+/*
+Notes on garbage collection and object registration
+
+JDWP does not allow the debugger to assume that objects passed to it
+will not be garbage collected.  It specifies explicit commands (e.g.
+ObjectReference.DisableCollection) to allow the debugger to manage
+object lifetime.  It does, however, require that the VM not re-use an
+object ID unless an explicit "dispose" call has been made, and if the
+VM asks for a now-collected object we must return INVALID_OBJECT.
+
+JDWP also requires that, while the VM is suspended, no garbage collection
+occur.  The JDWP docs suggest that this is obvious, because no threads
+can be running.  Unfortunately it's not entirely clear how to deal
+with situations where the debugger itself allocates strings or executes
+code as part of displaying variables.  The easiest way to enforce this,
+short of disabling GC whenever the debugger is connected, is to ensure
+that the debugger thread can't cause a GC: it has to expand the heap or
+fail to allocate.  (Might want to make that "is debugger thread AND all
+other threads are suspended" to avoid unnecessary heap expansion by a
+poorly-timed JDWP request.)
+
+We use an "object registry" so that we can separate our internal
+representation from what we show the debugger.  This allows us to
+return a registry table index instead of a pointer or handle.
+
+There are various approaches we can take to achieve correct behavior:
+
+(1) Disable garbage collection entirely while the debugger is attached.
+This is very easy, but doesn't allow extended debugging sessions on
+small devices.
+
+(2) Keep a list of all object references requested by or sent to the
+debugger, and include the list in the GC root set.  This ensures that
+objects the debugger might care about don't go away.  This is straightforward,
+but it can cause us to hold on to large objects and prevent finalizers from
+being executed.
+
+(3) Keep a list of what amount to weak object references.  This way we
+don't interfere with the GC, and can support JDWP requests like
+"ObjectReference.IsCollected".
+
+The current implementation is #2.  The set should be reasonably small and
+performance isn't critical, so a simple expanding array can be used.
+
+
+Notes on threads:
+
+The VM has a Thread struct associated with every active thread.  The
+ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
+object, so to retrieve the VM's Thread struct we have to scan through the
+list looking for a match.
+
+When a thread goes away, we lock the list and free the struct.  To
+avoid having the thread list updated or Thread structs freed out from
+under us, we want to acquire and hold the thread list lock while we're
+performing operations on Threads.  Exceptions to this rule are noted in
+a couple of places.
+
+We can speed this up a bit by adding a Thread struct pointer to the
+java/lang/Thread object, and ensuring that both are discarded at the
+same time.
+*/
+
+#define THREAD_GROUP_ALL ((ObjectId) 0x12345)   // magic, internal-only value
+
+#define kSlot0Sub   1000    // Eclipse workaround
+
+/*
+ * System init.  We don't allocate the registry until first use.
+ * Make sure we do this before initializing JDWP.
+ */
+bool dvmDebuggerStartup()
+{
+    if (!dvmBreakpointStartup())
+        return false;
+
+    gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
+    return (gDvm.dbgRegistry != NULL);
+}
+
+/*
+ * Free registry storage.
+ */
+void dvmDebuggerShutdown()
+{
+    dvmHashTableFree(gDvm.dbgRegistry);
+    gDvm.dbgRegistry = NULL;
+    dvmBreakpointShutdown();
+}
+
+
+/*
+ * Pass these through to the VM functions.  Allows extended checking
+ * (e.g. "errorcheck" mutexes).  If nothing else we can assert() success.
+ */
+void dvmDbgInitMutex(pthread_mutex_t* pMutex)
+{
+    dvmInitMutex(pMutex);
+}
+void dvmDbgLockMutex(pthread_mutex_t* pMutex)
+{
+    dvmLockMutex(pMutex);
+}
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
+{
+    dvmUnlockMutex(pMutex);
+}
+void dvmDbgInitCond(pthread_cond_t* pCond)
+{
+    pthread_cond_init(pCond, NULL);
+}
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+    assert(cc == 0);
+}
+void dvmDbgCondSignal(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+    assert(cc == 0);
+}
+void dvmDbgCondBroadcast(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+    assert(cc == 0);
+}
+
+
+/* keep track of type, in case we need to distinguish them someday */
+enum RegistryType {
+    kObjectId = 0xc1, kRefTypeId
+};
+
+/*
+ * Hash function for object IDs.  Since objects are at least 8 bytes, and
+ * could someday be allocated on 16-byte boundaries, we don't want to use
+ * the low 4 bits in our hash.
+ */
+static inline u4 registryHash(u4 val)
+{
+    return val >> 4;
+}
+
+/*
+ * (This is a dvmHashTableLookup() callback.)
+ */
+static int registryCompare(const void* obj1, const void* obj2)
+{
+    return (int) obj1 - (int) obj2;
+}
+
+
+/*
+ * Determine if an id is already in the list.
+ *
+ * If the list doesn't yet exist, this creates it.
+ *
+ * Lock the registry before calling here.
+ */
+#ifndef NDEBUG
+static bool lookupId(ObjectId id)
+{
+    void* found;
+
+    found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+                (void*)(u4) id, registryCompare, false);
+    if (found == NULL)
+        return false;
+    assert(found == (void*)(u4) id);
+    return true;
+}
+#endif
+
+/*
+ * Register an object, if it hasn't already been.
+ *
+ * This is used for both ObjectId and RefTypeId.  In theory we don't have
+ * to register RefTypeIds unless we're worried about classes unloading.
+ *
+ * Null references must be represented as zero, or the debugger will get
+ * very confused.
+ */
+static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
+{
+    ObjectId id;
+
+    if (obj == NULL)
+        return 0;
+
+    assert((u4) obj != 0xcccccccc);
+    assert((u4) obj > 0x100);
+
+    id = (ObjectId)(u4)obj | ((u8) type) << 32;
+    if (!reg)
+        return id;
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    if (!gDvm.debuggerConnected) {
+        /* debugger has detached while we were doing stuff? */
+        ALOGI("ignoring registerObject request in thread=%d",
+            dvmThreadSelf()->threadId);
+        //dvmAbort();
+        goto bail;
+    }
+
+    dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+                (void*)(u4) id, registryCompare, true);
+
+bail:
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+    return id;
+}
+
+/*
+ * Verify that an object has been registered.  If it hasn't, the debugger
+ * is asking for something we didn't send it, which means something
+ * somewhere is broken.
+ *
+ * If speed is an issue we can encode the registry index in the high
+ * four bytes.  We could also just hard-wire this to "true".
+ *
+ * Note this actually takes both ObjectId and RefTypeId.
+ */
+#ifndef NDEBUG
+static bool objectIsRegistered(ObjectId id, RegistryType type)
+{
+    UNUSED_PARAMETER(type);
+
+    if (id == 0)        // null reference?
+        return true;
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    bool result = lookupId(id);
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+    return result;
+}
+#endif
+
+/*
+ * Convert to/from a RefTypeId.
+ *
+ * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
+ */
+static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
+{
+    return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
+}
+#if 0
+static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
+{
+    return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
+}
+#endif
+static ClassObject* refTypeIdToClassObject(RefTypeId id)
+{
+    assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
+    return (ClassObject*)(u4) id;
+}
+
+/*
+ * Convert to/from an ObjectId.
+ */
+static ObjectId objectToObjectId(const Object* obj)
+{
+    return registerObject(obj, kObjectId, true);
+}
+static ObjectId objectToObjectIdNoReg(const Object* obj)
+{
+    return registerObject(obj, kObjectId, false);
+}
+static Object* objectIdToObject(ObjectId id)
+{
+    assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
+    return (Object*)(u4) id;
+}
+
+/*
+ * Register an object ID that might not have been registered previously.
+ *
+ * Normally this wouldn't happen -- the conversion to an ObjectId would
+ * have added the object to the registry -- but in some cases (e.g.
+ * throwing exceptions) we really want to do the registration late.
+ */
+void dvmDbgRegisterObjectId(ObjectId id)
+{
+    Object* obj = (Object*)(u4) id;
+    ALOGV("+++ registering %p (%s)", obj, obj->clazz->descriptor);
+    registerObject(obj, kObjectId, true);
+}
+
+/*
+ * Convert to/from a MethodId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index.  For now we just use the Method*.
+ */
+static MethodId methodToMethodId(const Method* meth)
+{
+    return (MethodId)(u4) meth;
+}
+static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
+{
+    // TODO? verify "id" is actually a method in "refTypeId"
+    return (Method*)(u4) id;
+}
+
+/*
+ * Convert to/from a FieldId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index.  For now we just use the Field*.
+ */
+static FieldId fieldToFieldId(const Field* field)
+{
+    return (FieldId)(u4) field;
+}
+static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
+{
+    // TODO? verify "id" is actually a field in "refTypeId"
+    return (Field*)(u4) id;
+}
+
+/*
+ * Convert to/from a FrameId.
+ *
+ * We just return a pointer to the stack frame.
+ */
+static FrameId frameToFrameId(const void* frame)
+{
+    return (FrameId)(u4) frame;
+}
+static u4* frameIdToFrame(FrameId id)
+{
+    return (u4*)(u4) id;
+}
+
+
+/*
+ * Get the invocation request state.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq()
+{
+    return &dvmThreadSelf()->invokeReq;
+}
+
+/*
+ * Enable the object registry, but don't enable debugging features yet.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgConnected()
+{
+    assert(!gDvm.debuggerConnected);
+
+    ALOGV("JDWP has attached");
+    assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
+    gDvm.debuggerConnected = true;
+}
+
+/*
+ * Enable all debugging features, including scans for breakpoints.
+ *
+ * This is a no-op if we're already active.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgActive()
+{
+    if (gDvm.debuggerActive)
+        return;
+
+    ALOGI("Debugger is active");
+    dvmInitBreakpoints();
+    gDvm.debuggerActive = true;
+    dvmEnableAllSubMode(kSubModeDebuggerActive);
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+}
+
+/*
+ * Disable debugging features.
+ *
+ * Set "debuggerConnected" to false, which disables use of the object
+ * registry.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgDisconnected()
+{
+    assert(gDvm.debuggerConnected);
+
+    gDvm.debuggerActive = false;
+    dvmDisableAllSubMode(kSubModeDebuggerActive);
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    gDvm.debuggerConnected = false;
+
+    ALOGD("Debugger has detached; object registry had %d entries",
+        dvmHashTableNumEntries(gDvm.dbgRegistry));
+    //int i;
+    //for (i = 0; i < gDvm.dbgRegistryNext; i++)
+    //    LOGVV("%4d: 0x%llx", i, gDvm.dbgRegistryTable[i]);
+
+    dvmHashTableClear(gDvm.dbgRegistry);
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+}
+
+/*
+ * Returns "true" if a debugger is connected.
+ *
+ * Does not return "true" if it's just a DDM server.
+ */
+bool dvmDbgIsDebuggerConnected()
+{
+    return gDvm.debuggerActive;
+}
+
+/*
+ * Get time since last debugger activity.  Used when figuring out if the
+ * debugger has finished configuring us.
+ */
+s8 dvmDbgLastDebuggerActivity()
+{
+    return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
+}
+
+/*
+ * JDWP thread is running, don't allow GC.
+ */
+int dvmDbgThreadRunning()
+{
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * JDWP thread is idle, allow GC.
+ */
+int dvmDbgThreadWaiting()
+{
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * Restore state returned by Running/Waiting calls.
+ */
+int dvmDbgThreadContinuing(int status)
+{
+    ThreadStatus newStatus = static_cast<ThreadStatus>(status);
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
+    return static_cast<int>(oldStatus);
+}
+
+/*
+ * The debugger wants us to exit.
+ */
+void dvmDbgExit(int status)
+{
+    // TODO? invoke System.exit() to perform exit processing; ends up
+    // in System.exitInternal(), which can call JNI exit hook
+    ALOGI("GC lifetime allocation: %d bytes", gDvm.allocProf.allocCount);
+    if (CALC_CACHE_STATS) {
+        dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+        dvmDumpBootClassPath();
+    }
+    exit(status);
+}
+
+
+/*
+ * ===========================================================================
+ *      Class, Object, Array
+ * ===========================================================================
+ */
+
+/*
+ * Get the class's type descriptor from a reference type ID.
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(id);
+    return clazz->descriptor;
+}
+
+/*
+ * Convert a RefTypeId to an ObjectId.
+ */
+ObjectId dvmDbgGetClassObject(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return objectToObjectId((Object*) clazz);
+}
+
+/*
+ * Return the superclass of a class (will be NULL for java/lang/Object).
+ */
+RefTypeId dvmDbgGetSuperclass(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return classObjectToRefTypeId(clazz->super);
+}
+
+/*
+ * Return a class's defining class loader.
+ */
+RefTypeId dvmDbgGetClassLoader(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return objectToObjectId(clazz->classLoader);
+}
+
+/*
+ * Return a class's access flags.
+ */
+u4 dvmDbgGetAccessFlags(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return clazz->accessFlags & JAVA_FLAGS_MASK;
+}
+
+/*
+ * Is this class an interface?
+ */
+bool dvmDbgIsInterface(RefTypeId id)
+{
+    ClassObject* clazz = refTypeIdToClassObject(id);
+    return dvmIsInterfaceClass(clazz);
+}
+
+/*
+ * dvmHashForeach callback
+ */
+static int copyRefType(void* vclazz, void* varg)
+{
+    RefTypeId** pRefType = (RefTypeId**)varg;
+    **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
+    (*pRefType)++;
+    return 0;
+}
+
+/*
+ * Get the complete list of reference classes (i.e. all classes except
+ * the primitive types).
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
+{
+    RefTypeId* pRefType;
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+    pRefType = *pClassRefBuf =
+        (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
+
+    if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
+        ALOGW("Warning: problem getting class list");
+        /* not really expecting this to happen */
+    } else {
+        assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
+    }
+
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the list of reference classes "visible" to the specified class
+ * loader.  A class is visible to a class loader if the ClassLoader object
+ * is the defining loader or is listed as an initiating loader.
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+    RefTypeId** pClassRefBuf)
+{
+    Object* classLoader;
+    int numClasses = 0, maxClasses;
+
+    classLoader = objectIdToObject(classLoaderId);
+    // I don't think classLoader can be NULL, but the spec doesn't say
+
+    LOGVV("GetVisibleList: comparing to %p", classLoader);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+
+    /* over-allocate the return buffer */
+    maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+    *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
+
+    /*
+     * Run through the list, looking for matches.
+     */
+    HashIter iter;
+    for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
+
+        if (clazz->classLoader == classLoader ||
+            dvmLoaderInInitiatingList(clazz, classLoader))
+        {
+            LOGVV("  match '%s'", clazz->descriptor);
+            (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
+        }
+    }
+    *pNumClasses = numClasses;
+
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
+ *
+ * Our class descriptors are in the correct format, so we just return that.
+ */
+static const char* jniSignature(ClassObject* clazz)
+{
+    return clazz->descriptor;
+}
+
+/*
+ * Get information about a class.
+ *
+ * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
+ * the class.
+ */
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+    const char** pSignature)
+{
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+
+    if (clazz->descriptor[0] == '[') {
+        /* generated array class */
+        *pStatus = CS_VERIFIED | CS_PREPARED;
+        *pTypeTag = TT_ARRAY;
+    } else {
+        if (clazz->status == CLASS_ERROR)
+            *pStatus = CS_ERROR;
+        else
+            *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
+        if (dvmIsInterfaceClass(clazz))
+            *pTypeTag = TT_INTERFACE;
+        else
+            *pTypeTag = TT_CLASS;
+    }
+    if (pSignature != NULL)
+        *pSignature = jniSignature(clazz);
+}
+
+/*
+ * Search the list of loaded classes for a match.
+ */
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+        RefTypeId* pRefTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindLoadedClass(classDescriptor);
+    if (clazz != NULL) {
+        *pRefTypeId = classObjectToRefTypeId(clazz);
+        return true;
+    } else
+        return false;
+}
+
+
+/*
+ * Get an object's class and "type tag".
+ */
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+    RefTypeId* pRefTypeId)
+{
+    Object* obj = objectIdToObject(objectId);
+
+    if (dvmIsArrayClass(obj->clazz))
+        *pRefTypeTag = TT_ARRAY;
+    else if (dvmIsInterfaceClass(obj->clazz))
+        *pRefTypeTag = TT_INTERFACE;
+    else
+        *pRefTypeTag = TT_CLASS;
+    *pRefTypeId = classObjectToRefTypeId(obj->clazz);
+}
+
+/*
+ * Get a class object's "type tag".
+ */
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
+{
+    ClassObject* clazz = refTypeIdToClassObject(refTypeId);
+
+    if (dvmIsArrayClass(clazz))
+        return TT_ARRAY;
+    else if (dvmIsInterfaceClass(clazz))
+        return TT_INTERFACE;
+    else
+        return TT_CLASS;
+}
+
+/*
+ * Get a class' signature.
+ */
+const char* dvmDbgGetSignature(RefTypeId refTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    return jniSignature(clazz);
+}
+
+/*
+ * Get class' source file.
+ *
+ * Returns a newly-allocated string.
+ */
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    return clazz->sourceFile;
+}
+
+/*
+ * Get an object's type name.  (For log message display only.)
+ */
+const char* dvmDbgGetObjectTypeName(ObjectId objectId)
+{
+    if (objectId == 0)
+        return "(null)";
+
+    Object* obj = objectIdToObject(objectId);
+    return jniSignature(obj->clazz);
+}
+
+/*
+ * Determine whether or not a tag represents a primitive type.
+ */
+static bool isTagPrimitive(u1 tag)
+{
+    switch (tag) {
+    case JT_BYTE:
+    case JT_CHAR:
+    case JT_FLOAT:
+    case JT_DOUBLE:
+    case JT_INT:
+    case JT_LONG:
+    case JT_SHORT:
+    case JT_VOID:
+    case JT_BOOLEAN:
+        return true;
+    case JT_ARRAY:
+    case JT_OBJECT:
+    case JT_STRING:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+        return false;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        return false;
+    }
+}
+
+/*
+ * Determine the best tag type given an object's class.
+ */
+static u1 tagFromClass(ClassObject* clazz)
+{
+    if (dvmIsArrayClass(clazz))
+        return JT_ARRAY;
+
+    if (clazz == gDvm.classJavaLangString) {
+        return JT_STRING;
+    } else if (dvmIsTheClassClass(clazz)) {
+        return JT_CLASS_OBJECT;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
+        return JT_THREAD;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
+        return JT_THREAD_GROUP;
+    } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
+        return JT_CLASS_LOADER;
+    } else {
+        return JT_OBJECT;
+    }
+}
+
+/*
+ * Return a basic tag value based solely on a type descriptor.
+ *
+ * The ASCII value maps directly to the JDWP tag constants, so we don't
+ * need to do much here.  This does not return the fancier tags like
+ * JT_THREAD.
+ */
+static u1 basicTagFromDescriptor(const char* descriptor)
+{
+    return descriptor[0];
+}
+
+/*
+ * Objects declared to hold Object might actually hold a more specific
+ * type.  The debugger may take a special interest in these (e.g. it
+ * wants to display the contents of Strings), so we want to return an
+ * appropriate tag.
+ *
+ * Null objects are tagged JT_OBJECT.
+ */
+static u1 tagFromObject(const Object* obj)
+{
+    if (obj == NULL)
+        return JT_OBJECT;
+    return tagFromClass(obj->clazz);
+}
+
+/*
+ * Determine the tag for an object.
+ *
+ * "objectId" may be 0 (i.e. NULL reference).
+ */
+u1 dvmDbgGetObjectTag(ObjectId objectId)
+{
+    return tagFromObject(objectIdToObject(objectId));
+}
+
+/*
+ * Get the widths of the specified JDWP.Tag value.
+ */
+int dvmDbgGetTagWidth(int tag)
+{
+    switch (tag) {
+    case JT_VOID:
+        return 0;
+    case JT_BYTE:
+    case JT_BOOLEAN:
+        return 1;
+    case JT_CHAR:
+    case JT_SHORT:
+        return 2;
+    case JT_FLOAT:
+    case JT_INT:
+        return 4;
+    case JT_ARRAY:
+    case JT_OBJECT:
+    case JT_STRING:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+    case JT_CLASS_OBJECT:
+        return sizeof(ObjectId);
+    case JT_DOUBLE:
+    case JT_LONG:
+        return 8;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        return -1;
+    }
+}
+
+
+/*
+ * Return the length of the specified array.
+ */
+int dvmDbgGetArrayLength(ObjectId arrayId)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    assert(dvmIsArray(arrayObj));
+    return arrayObj->length;
+}
+
+/*
+ * Return a tag indicating the general type of elements in the array.
+ */
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+
+    ClassObject* arrayClass = arrayObj->clazz;
+    u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
+    if (!isTagPrimitive(tag)) {
+        /* try to refine it */
+        tag = tagFromClass(arrayClass->elementClass);
+    }
+
+    return tag;
+}
+
+/*
+ * Copy a series of values with the specified width, changing the byte
+ * ordering to big-endian.
+ */
+static void copyValuesToBE(u1* out, const u1* in, int count, int width)
+{
+    int i;
+
+    switch (width) {
+    case 1:
+        memcpy(out, in, count);
+        break;
+    case 2:
+        for (i = 0; i < count; i++)
+            *(((u2*) out)+i) = get2BE(in + i*2);
+        break;
+    case 4:
+        for (i = 0; i < count; i++)
+            *(((u4*) out)+i) = get4BE(in + i*4);
+        break;
+    case 8:
+        for (i = 0; i < count; i++)
+            *(((u8*) out)+i) = get8BE(in + i*8);
+        break;
+    default:
+        assert(false);
+    }
+}
+
+/*
+ * Copy a series of values with the specified width, changing the
+ * byte order from big-endian.
+ */
+static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
+{
+    int i;
+
+    switch (width) {
+    case 1:
+        memcpy(out, in, count);
+        break;
+    case 2:
+        for (i = 0; i < count; i++)
+            set2BE(out + i*2, *((u2*)in + i));
+        break;
+    case 4:
+        for (i = 0; i < count; i++)
+            set4BE(out + i*4, *((u4*)in + i));
+        break;
+    case 8:
+        for (i = 0; i < count; i++)
+            set8BE(out + i*8, *((u8*)in + i));
+        break;
+    default:
+        assert(false);
+    }
+}
+
+/*
+ * Output a piece of an array to the reply buffer.
+ *
+ * Returns "false" if something looks fishy.
+ */
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+    ExpandBuf* pReply)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    const u1* data = (const u1*)arrayObj->contents;
+    u1 tag;
+
+    assert(dvmIsArray(arrayObj));
+
+    if (firstIndex + count > (int)arrayObj->length) {
+        ALOGW("Request for index=%d + count=%d excceds length=%d",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
+
+    if (isTagPrimitive(tag)) {
+        int width = dvmDbgGetTagWidth(tag);
+        u1* outBuf;
+
+        outBuf = expandBufAddSpace(pReply, count * width);
+
+        copyValuesToBE(outBuf, data + firstIndex*width, count, width);
+    } else {
+        Object** pObjects;
+        int i;
+
+        pObjects = (Object**) data;
+        pObjects += firstIndex;
+
+        ALOGV("    --> copying %d object IDs", count);
+        //assert(tag == JT_OBJECT);     // could be object or "refined" type
+
+        for (i = 0; i < count; i++, pObjects++) {
+            u1 thisTag;
+            if (*pObjects != NULL)
+                thisTag = tagFromObject(*pObjects);
+            else
+                thisTag = tag;
+            expandBufAdd1(pReply, thisTag);
+            expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Set a range of elements in an array from the data in "buf".
+ */
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+    const u1* buf)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+    u1* data = (u1*)arrayObj->contents;
+    u1 tag;
+
+    assert(dvmIsArray(arrayObj));
+
+    if (firstIndex + count > (int)arrayObj->length) {
+        ALOGW("Attempt to set index=%d + count=%d excceds length=%d",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
+
+    if (isTagPrimitive(tag)) {
+        int width = dvmDbgGetTagWidth(tag);
+
+        ALOGV("    --> setting %d '%c' width=%d", count, tag, width);
+
+        copyValuesFromBE(data + firstIndex*width, buf, count, width);
+    } else {
+        Object** pObjects;
+        int i;
+
+        pObjects = (Object**) data;
+        pObjects += firstIndex;
+
+        ALOGV("    --> setting %d objects", count);
+
+        /* should do array type check here */
+        for (i = 0; i < count; i++) {
+            ObjectId id = dvmReadObjectId(&buf);
+            *pObjects++ = objectIdToObject(id);
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Create a new string.
+ *
+ * The only place the reference will be held in the VM is in our registry.
+ */
+ObjectId dvmDbgCreateString(const char* str)
+{
+    StringObject* strObj;
+
+    strObj = dvmCreateStringFromCstr(str);
+    dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+    return objectToObjectId((Object*) strObj);
+}
+
+/*
+ * Allocate a new object of the specified type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateObject(RefTypeId classId)
+{
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    return objectToObjectId(newObj);
+}
+
+/*
+ * Allocate a new array object of the specified type and length.  The
+ * type is the array type, not the element type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
+{
+    ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
+    Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    return objectToObjectId(newObj);
+}
+
+/*
+ * Determine if "instClassId" is an instance of "classId".
+ */
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
+{
+    ClassObject* instClazz = refTypeIdToClassObject(instClassId);
+    ClassObject* clazz = refTypeIdToClassObject(classId);
+
+    return dvmInstanceof(instClazz, clazz);
+}
+
+
+/*
+ * ===========================================================================
+ *      Method and Field
+ * ===========================================================================
+ */
+
+/*
+ * Get the method name from a MethodId.
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
+{
+    Method* meth;
+
+    meth = methodIdToMethod(refTypeId, id);
+    return meth->name;
+}
+
+/*
+ * Augment the access flags for synthetic methods and fields by setting
+ * the (as described by the spec) "0xf0000000 bit".  Also, strip out any
+ * flags not specified by the Java programming language.
+ */
+static u4 augmentedAccessFlags(u4 accessFlags)
+{
+    accessFlags &= JAVA_FLAGS_MASK;
+
+    if ((accessFlags & ACC_SYNTHETIC) != 0) {
+        return accessFlags | 0xf0000000;
+    } else {
+        return accessFlags;
+    }
+}
+
+/*
+ * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
+ * output all fields declared by the class.  Inherited fields are
+ * not included.
+ */
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply)
+{
+    ClassObject* clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    u4 declared = clazz->sfieldCount + clazz->ifieldCount;
+    expandBufAdd4BE(pReply, declared);
+
+    for (int i = 0; i < clazz->sfieldCount; i++) {
+        Field* field = &clazz->sfields[i];
+        expandBufAddFieldId(pReply, fieldToFieldId(field));
+        expandBufAddUtf8String(pReply, (const u1*) field->name);
+        expandBufAddUtf8String(pReply, (const u1*) field->signature);
+        if (withGeneric) {
+            static const u1 genericSignature[1] = "";
+            expandBufAddUtf8String(pReply, genericSignature);
+        }
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
+    }
+    for (int i = 0; i < clazz->ifieldCount; i++) {
+        Field* field = &clazz->ifields[i];
+        expandBufAddFieldId(pReply, fieldToFieldId(field));
+        expandBufAddUtf8String(pReply, (const u1*) field->name);
+        expandBufAddUtf8String(pReply, (const u1*) field->signature);
+        if (withGeneric) {
+            static const u1 genericSignature[1] = "";
+            expandBufAddUtf8String(pReply, genericSignature);
+        }
+        expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
+    }
+}
+
+/*
+ * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
+ * output all methods declared by the class.  Inherited methods are
+ * not included.
+ */
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply)
+{
+    DexStringCache stringCache;
+    static const u1 genericSignature[1] = "";
+    ClassObject* clazz;
+    Method* meth;
+    u4 declared;
+    int i;
+
+    dexStringCacheInit(&stringCache);
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    declared = clazz->directMethodCount + clazz->virtualMethodCount;
+    expandBufAdd4BE(pReply, declared);
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        meth = &clazz->directMethods[i];
+
+        expandBufAddMethodId(pReply, methodToMethodId(meth));
+        expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+        expandBufAddUtf8String(pReply,
+            (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+                    &stringCache));
+
+        if (withGeneric)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        meth = &clazz->virtualMethods[i];
+
+        expandBufAddMethodId(pReply, methodToMethodId(meth));
+        expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+        expandBufAddUtf8String(pReply,
+            (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+                    &stringCache));
+
+        if (withGeneric)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
+    }
+
+    dexStringCacheRelease(&stringCache);
+}
+
+/*
+ * Output all interfaces directly implemented by the class.
+ */
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
+{
+    ClassObject* clazz;
+    int i, count;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    count = clazz->interfaceCount;
+    expandBufAdd4BE(pReply, count);
+    for (i = 0; i < count; i++) {
+        ClassObject* iface = clazz->interfaces[i];
+        expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
+    }
+}
+
+struct DebugCallbackContext {
+    int numItems;
+    ExpandBuf* pReply;
+    // used by locals table
+    bool withGeneric;
+};
+
+static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+    DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+    expandBufAdd8BE(pContext->pReply, address);
+    expandBufAdd4BE(pContext->pReply, lineNum);
+    pContext->numItems++;
+
+    return 0;
+}
+
+/*
+ * For Method.LineTable: output the line table.
+ *
+ * Note we operate in Dalvik's 16-bit units rather than bytes.
+ */
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+    ExpandBuf* pReply)
+{
+    Method* method;
+    u8 start, end;
+    DebugCallbackContext context;
+
+    memset (&context, 0, sizeof(DebugCallbackContext));
+
+    method = methodIdToMethod(refTypeId, methodId);
+    if (dvmIsNativeMethod(method)) {
+        start = (u8) -1;
+        end = (u8) -1;
+    } else {
+        start = 0;
+        end = dvmGetMethodInsnsSize(method);
+    }
+
+    expandBufAdd8BE(pReply, start);
+    expandBufAdd8BE(pReply, end);
+
+    // Add numLines later
+    size_t numLinesOffset = expandBufGetLength(pReply);
+    expandBufAdd4BE(pReply, 0);
+
+    context.pReply = pReply;
+
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+        dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        lineTablePositionsCb, NULL, &context);
+
+    set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
+}
+
+/*
+ * Eclipse appears to expect that the "this" reference is in slot zero.
+ * If it's not, the "variables" display will show two copies of "this",
+ * possibly because it gets "this" from SF.ThisObject and then displays
+ * all locals with nonzero slot numbers.
+ *
+ * So, we remap the item in slot 0 to 1000, and remap "this" to zero.  On
+ * SF.GetValues / SF.SetValues we map them back.
+ */
+static int tweakSlot(int slot, const char* name)
+{
+    int newSlot = slot;
+
+    if (strcmp(name, "this") == 0)      // only remap "this" ptr
+        newSlot = 0;
+    else if (slot == 0)                 // always remap slot 0
+        newSlot = kSlot0Sub;
+
+    ALOGV("untweak: %d to %d", slot, newSlot);
+    return newSlot;
+}
+
+/*
+ * Reverse Eclipse hack.
+ */
+static int untweakSlot(int slot, const void* framePtr)
+{
+    int newSlot = slot;
+
+    if (slot == kSlot0Sub) {
+        newSlot = 0;
+    } else if (slot == 0) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+        const Method* method = saveArea->method;
+        newSlot = method->registersSize - method->insSize;
+    }
+
+    ALOGV("untweak: %d to %d", slot, newSlot);
+    return newSlot;
+}
+
+static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
+        u4 endAddress, const char *name, const char *descriptor,
+        const char *signature)
+{
+    DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+    reg = (u2) tweakSlot(reg, name);
+
+    ALOGV("    %2d: %d(%d) '%s' '%s' slot=%d",
+        pContext->numItems, startAddress, endAddress - startAddress,
+        name, descriptor, reg);
+
+    expandBufAdd8BE(pContext->pReply, startAddress);
+    expandBufAddUtf8String(pContext->pReply, (const u1*)name);
+    expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
+    if (pContext->withGeneric) {
+        expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
+    }
+    expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
+    expandBufAdd4BE(pContext->pReply, reg);
+
+    pContext->numItems++;
+}
+
+/*
+ * For Method.VariableTable[WithGeneric]: output information about local
+ * variables for the specified method.
+ */
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
+    bool withGeneric, ExpandBuf* pReply)
+{
+    Method* method;
+    DebugCallbackContext context;
+
+    memset (&context, 0, sizeof(DebugCallbackContext));
+
+    method = methodIdToMethod(refTypeId, methodId);
+
+    expandBufAdd4BE(pReply, method->insSize);
+
+    // Add numLocals later
+    size_t numLocalsOffset = expandBufGetLength(pReply);
+    expandBufAdd4BE(pReply, 0);
+
+    context.pReply = pReply;
+    context.withGeneric = withGeneric;
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+        dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        NULL, variableTableCb, &context);
+
+    set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
+}
+
+/*
+ * Get the basic tag for an instance field.
+ */
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
+{
+    Object* obj = objectIdToObject(objId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    const Field* field = fieldIdToField(classId, fieldId);
+    return basicTagFromDescriptor(field->signature);
+}
+
+/*
+ * Get the basic tag for a static field.
+ */
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
+{
+    const Field* field = fieldIdToField(refTypeId, fieldId);
+    return basicTagFromDescriptor(field->signature);
+}
+
+
+/*
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
+ */
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
+{
+    Object* obj = objectIdToObject(objectId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
+    u1 tag = basicTagFromDescriptor(ifield->signature);
+
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        ALOGV("    --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
+    } else {
+        ALOGV("    --> ifieldId %x --> tag '%c'", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
+            break;
+        case JT_INT:
+        case JT_FLOAT:
+            expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
+            break;
+        case JT_LONG:
+        case JT_DOUBLE:
+            expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
+            break;
+        default:
+            ALOGE("ERROR: unhandled field type '%s'", ifield->signature);
+            assert(false);
+            break;
+        }
+    }
+}
+
+/*
+ * Set the value of the specified field.
+ */
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+    int width)
+{
+    Object* obj = objectIdToObject(objectId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    InstField* field = (InstField*) fieldIdToField(classId, fieldId);
+
+    switch (field->signature[0]) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(width == 2);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(width == 4);
+        dvmSetFieldInt(obj, field->byteOffset, value);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(width == 8);
+        dvmSetFieldLong(obj, field->byteOffset, value);
+        break;
+    default:
+        ALOGE("ERROR: unhandled class type '%s'", field->signature);
+        assert(false);
+        break;
+    }
+}
+
+/*
+ * Copy the value of a static field into the output buffer, preceded
+ * by an appropriate tag.  The tag is based on the value held by the
+ * field, not the field's type.
+ */
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply)
+{
+    StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+    u1 tag = basicTagFromDescriptor(sfield->signature);
+
+    if (tag == JT_ARRAY || tag == JT_OBJECT) {
+        Object* objVal = dvmGetStaticFieldObject(sfield);
+        tag = tagFromObject(objVal);
+        expandBufAdd1(pReply, tag);
+        expandBufAddObjectId(pReply, objectToObjectId(objVal));
+        ALOGV("    --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
+    } else {
+        JValue value;
+
+        ALOGV("    --> sfieldId %x --> tag '%c'", fieldId, tag);
+        expandBufAdd1(pReply, tag);
+
+        switch (tag) {
+        case JT_BOOLEAN:
+            expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
+            break;
+        case JT_BYTE:
+            expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
+            break;
+        case JT_SHORT:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
+            break;
+        case JT_CHAR:
+            expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
+            break;
+        case JT_INT:
+            expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
+            break;
+        case JT_FLOAT:
+            value.f = dvmGetStaticFieldFloat(sfield);
+            expandBufAdd4BE(pReply, value.i);
+            break;
+        case JT_LONG:
+            expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
+            break;
+        case JT_DOUBLE:
+            value.d = dvmGetStaticFieldDouble(sfield);
+            expandBufAdd8BE(pReply, value.j);
+            break;
+        default:
+            ALOGE("ERROR: unhandled field type '%s'", sfield->signature);
+            assert(false);
+            break;
+        }
+    }
+}
+
+/*
+ * Set the value of a static field.
+ */
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    u8 rawValue, int width)
+{
+    StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+    Object* objVal;
+    JValue value;
+
+    value.j = rawValue;
+
+    switch (sfield->signature[0]) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        dvmSetStaticFieldBoolean(sfield, value.z);
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        dvmSetStaticFieldByte(sfield, value.b);
+        break;
+    case JT_SHORT:
+        assert(width == 2);
+        dvmSetStaticFieldShort(sfield, value.s);
+        break;
+    case JT_CHAR:
+        assert(width == 2);
+        dvmSetStaticFieldChar(sfield, value.c);
+        break;
+    case JT_INT:
+        assert(width == 4);
+        dvmSetStaticFieldInt(sfield, value.i);
+        break;
+    case JT_FLOAT:
+        assert(width == 4);
+        dvmSetStaticFieldFloat(sfield, value.f);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        objVal = objectIdToObject(rawValue);
+        dvmSetStaticFieldObject(sfield, objVal);
+        break;
+    case JT_LONG:
+        assert(width == 8);
+        dvmSetStaticFieldLong(sfield, value.j);
+        break;
+    case JT_DOUBLE:
+        assert(width == 8);
+        dvmSetStaticFieldDouble(sfield, value.d);
+        break;
+    default:
+        ALOGE("ERROR: unhandled class type '%s'", sfield->signature);
+        assert(false);
+        break;
+    }
+}
+
+/*
+ * Convert a string object to a UTF-8 string.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgStringToUtf8(ObjectId strId)
+{
+    StringObject* strObj = (StringObject*) objectIdToObject(strId);
+
+    return dvmCreateCstrFromString(strObj);
+}
+
+
+/*
+ * ===========================================================================
+ *      Thread and ThreadGroup
+ * ===========================================================================
+ */
+
+/*
+ * Convert a thread object to a Thread ptr.
+ *
+ * This currently requires running through the list of threads and finding
+ * a match.
+ *
+ * IMPORTANT: grab gDvm.threadListLock before calling here.
+ */
+static Thread* threadObjToThread(Object* threadObj)
+{
+    Thread* thread;
+
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadObj == threadObj)
+            break;
+    }
+
+    return thread;
+}
+
+/*
+ * Get the status and suspend state of a thread.
+ */
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
+    u4* pSuspendStatus)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    switch (thread->status) {
+    case THREAD_ZOMBIE:         *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_RUNNING:        *pThreadStatus = TS_RUNNING;    break;
+    case THREAD_TIMED_WAIT:     *pThreadStatus = TS_SLEEPING;   break;
+    case THREAD_MONITOR:        *pThreadStatus = TS_MONITOR;    break;
+    case THREAD_WAIT:           *pThreadStatus = TS_WAIT;       break;
+    case THREAD_INITIALIZING:   *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_STARTING:       *pThreadStatus = TS_ZOMBIE;     break;
+    case THREAD_NATIVE:         *pThreadStatus = TS_RUNNING;    break;
+    case THREAD_VMWAIT:         *pThreadStatus = TS_WAIT;       break;
+    case THREAD_SUSPENDED:      *pThreadStatus = TS_RUNNING;    break;
+    default:
+        assert(false);
+        *pThreadStatus = THREAD_ZOMBIE;
+        break;
+    }
+
+    if (dvmIsSuspended(thread))
+        *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
+    else
+        *pSuspendStatus = 0;
+
+    result = true;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Get the thread's suspend count.
+ */
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    u4 result = 0;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    result = thread->suspendCount;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Determine whether or not a thread exists in the VM's thread list.
+ *
+ * Returns "true" if the thread exists.
+ */
+bool dvmDbgThreadExists(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        result = false;
+    else
+        result = true;
+
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Determine whether or not a thread is suspended.
+ *
+ * Returns "false" if the thread is running or doesn't exist.
+ */
+bool dvmDbgIsSuspended(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    result = dvmIsSuspended(thread);
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetSystemThreadGroupId()
+{
+    Object* groupObj = dvmGetSystemThreadGroup();
+    return objectToObjectId(groupObj);
+}
+
+/*
+ * Return the ObjectId for the "main" thread group.
+ */
+ObjectId dvmDbgGetMainThreadGroupId()
+{
+    Object* groupObj = dvmGetMainThreadGroup();
+    return objectToObjectId(groupObj);
+}
+
+/*
+ * Get the name of a thread.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadName(ObjectId threadId)
+{
+    Object* threadObj;
+    StringObject* nameStr;
+    char* str;
+    char* result;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                                                gDvm.offJavaLangThread_name);
+    str = dvmCreateCstrFromString(nameStr);
+    result = (char*) malloc(strlen(str) + 20);
+
+    /* lock the thread list, so the thread doesn't vanish while we work */
+    dvmLockThreadList(NULL);
+    Thread* thread = threadObjToThread(threadObj);
+    if (thread != NULL)
+        sprintf(result, "<%d> %s", thread->threadId, str);
+    else
+        sprintf(result, "%s", str);
+    dvmUnlockThreadList();
+
+    free(str);
+    return result;
+}
+
+/*
+ * Get a thread's group.
+ */
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
+{
+    Object* threadObj;
+    Object* group;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
+    return objectToObjectId(group);
+}
+
+
+/*
+ * Get the name of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
+{
+    Object* threadGroup;
+    StringObject* nameStr;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    nameStr = (StringObject*)
+        dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
+    return dvmCreateCstrFromString(nameStr);
+}
+
+/*
+ * Get the parent of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
+{
+    Object* threadGroup;
+    Object* parent;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
+    return objectToObjectId(parent);
+}
+
+/*
+ * Get the list of threads in the thread group.
+ *
+ * We do this by running through the full list of threads and returning
+ * the ones that have the ThreadGroup object as their owner.
+ *
+ * If threadGroupId is set to "kAllThreads", we ignore the group field and
+ * return all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+    ObjectId** ppThreadIds, u4* pThreadCount)
+{
+    Object* targetThreadGroup = NULL;
+    Thread* thread;
+    int count;
+
+    if (threadGroupId != THREAD_GROUP_ALL) {
+        targetThreadGroup = objectIdToObject(threadGroupId);
+        assert(targetThreadGroup != NULL);
+    }
+
+    dvmLockThreadList(NULL);
+
+    thread = gDvm.threadList;
+    count = 0;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        Object* group;
+
+        /* Skip over the JDWP support thread.  Some debuggers
+         * get bent out of shape when they can't suspend and
+         * query all threads, so it's easier if we just don't
+         * tell them about us.
+         */
+        if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        /* This thread is currently being created, and isn't ready
+         * to be seen by the debugger yet.
+         */
+        if (thread->threadObj == NULL)
+            continue;
+
+        group = dvmGetFieldObject(thread->threadObj,
+                    gDvm.offJavaLangThread_group);
+        if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+            count++;
+    }
+
+    *pThreadCount = count;
+
+    if (count == 0) {
+        *ppThreadIds = NULL;
+    } else {
+        ObjectId* ptr;
+        ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
+
+        for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            Object* group;
+
+            /* Skip over the JDWP support thread.  Some debuggers
+             * get bent out of shape when they can't suspend and
+             * query all threads, so it's easier if we just don't
+             * tell them about us.
+             */
+            if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+                continue;
+
+            /* This thread is currently being created, and isn't ready
+             * to be seen by the debugger yet.
+             */
+            if (thread->threadObj == NULL)
+                continue;
+
+            group = dvmGetFieldObject(thread->threadObj,
+                        gDvm.offJavaLangThread_group);
+            if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+            {
+                *ptr++ = objectToObjectId(thread->threadObj);
+                count--;
+            }
+        }
+
+        assert(count == 0);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Get all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
+{
+    dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
+}
+
+
+/*
+ * Count up the #of frames on the thread's stack.
+ *
+ * Returns -1 on failure.
+ */
+int dvmDbgGetThreadFrameCount(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+    int count = -1;
+
+    threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+    thread = threadObjToThread(threadObj);
+    if (thread != NULL) {
+        count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
+    }
+    dvmUnlockThreadList();
+
+    return count;
+}
+
+/*
+ * Get info for frame N from the specified thread's stack.
+ */
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+    JdwpLocation* pLoc)
+{
+    Object* threadObj;
+    Thread* thread;
+    void* framePtr;
+    int count;
+
+    threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    framePtr = thread->interpSave.curFrame;
+    count = 0;
+    while (framePtr != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)framePtr)) {
+            if (count == num) {
+                *pFrameId = frameToFrameId(framePtr);
+                if (dvmIsInterfaceClass(method->clazz))
+                    pLoc->typeTag = TT_INTERFACE;
+                else
+                    pLoc->typeTag = TT_CLASS;
+                pLoc->classId = classObjectToRefTypeId(method->clazz);
+                pLoc->methodId = methodToMethodId(method);
+                if (dvmIsNativeMethod(method))
+                    pLoc->idx = (u8)-1;
+                else
+                    pLoc->idx = saveArea->xtra.currentPc - method->insns;
+                dvmUnlockThreadList();
+                return true;
+            }
+
+            count++;
+        }
+
+        framePtr = saveArea->prevFrame;
+    }
+
+bail:
+    dvmUnlockThreadList();
+    return false;
+}
+
+/*
+ * Get the ThreadId for the current thread.
+ */
+ObjectId dvmDbgGetThreadSelfId()
+{
+    Thread* self = dvmThreadSelf();
+    return objectToObjectId(self->threadObj);
+}
+
+/*
+ * Suspend the VM.
+ */
+void dvmDbgSuspendVM(bool isEvent)
+{
+    dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Resume the VM.
+ */
+void dvmDbgResumeVM()
+{
+    dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Suspend one thread (not ourselves).
+ */
+void dvmDbgSuspendThread(ObjectId threadId)
+{
+    Object* threadObj = objectIdToObject(threadId);
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL) {
+        /* can happen if our ThreadDeath notify crosses in the mail */
+        ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
+    } else {
+        dvmSuspendThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Resume one thread (not ourselves).
+ */
+void dvmDbgResumeThread(ObjectId threadId)
+{
+    Object* threadObj = objectIdToObject(threadId);
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL) {
+        ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
+    } else {
+        dvmResumeThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Suspend ourselves after sending an event to the debugger.
+ */
+void dvmDbgSuspendSelf()
+{
+    dvmSuspendSelf(true);
+}
+
+/*
+ * Get the "this" object for the specified frame.
+ */
+static Object* getThisObject(const u4* framePtr)
+{
+    const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+    const Method* method = saveArea->method;
+    int argOffset = method->registersSize - method->insSize;
+    Object* thisObj;
+
+    if (method == NULL) {
+        /* this is a "break" frame? */
+        assert(false);
+        return NULL;
+    }
+
+    LOGVV("  Pulling this object for frame at %p", framePtr);
+    LOGVV("    Method='%s' native=%d static=%d this=%p",
+        method->name, dvmIsNativeMethod(method),
+        dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
+
+    /*
+     * No "this" pointer for statics.  No args on the interp stack for
+     * native methods invoked directly from the VM.
+     */
+    if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
+        thisObj = NULL;
+    else
+        thisObj = (Object*) framePtr[argOffset];
+
+    if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
+        ALOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
+            framePtr, method->clazz->descriptor, method->name);
+        thisObj = NULL;
+    }
+
+    return thisObj;
+}
+
+/*
+ * Return the "this" object for the specified frame.  The thread must be
+ * suspended.
+ */
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
+{
+    const u4* framePtr = frameIdToFrame(frameId);
+    Object* thisObj;
+
+    UNUSED_PARAMETER(threadId);
+
+    thisObj = getThisObject(framePtr);
+
+    *pThisId = objectToObjectId(thisObj);
+    return true;
+}
+
+/*
+ * Copy the value of a method argument or local variable into the
+ * specified buffer.  The value will be preceeded with the tag.
+ *
+ * The debugger includes the tags in the request.  Object tags may
+ * be updated with a more refined type.
+ */
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u1* buf, int expectedLen)
+{
+    const u4* framePtr = frameIdToFrame(frameId);
+    Object* objVal;
+    u4 intVal;
+    u8 longVal;
+
+    UNUSED_PARAMETER(threadId);
+
+    slot = untweakSlot(slot, framePtr);     // Eclipse workaround
+
+    switch (tag) {
+    case JT_BOOLEAN:
+        assert(expectedLen == 1);
+        intVal = framePtr[slot];
+        set1(buf+1, intVal != 0);
+        break;
+    case JT_BYTE:
+        assert(expectedLen == 1);
+        intVal = framePtr[slot];
+        set1(buf+1, intVal);
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(expectedLen == 2);
+        intVal = framePtr[slot];
+        set2BE(buf+1, intVal);
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(expectedLen == 4);
+        intVal = framePtr[slot];
+        set4BE(buf+1, intVal);
+        break;
+    case JT_ARRAY:
+        assert(expectedLen == sizeof(ObjectId));
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+            if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
+                ALOGW("JDWP: slot %d expected to hold array, %p invalid",
+                    slot, objVal);
+                dvmAbort();         // DEBUG: make it obvious
+                objVal = NULL;
+                tag = JT_OBJECT;    // JT_ARRAY not expected for NULL ref
+            }
+            dvmSetObjectId(buf+1, objectToObjectId(objVal));
+        }
+        break;
+    case JT_OBJECT:
+        assert(expectedLen == sizeof(ObjectId));
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+
+            if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
+                ALOGW("JDWP: slot %d expected to hold object, %p invalid",
+                    slot, objVal);
+                dvmAbort();         // DEBUG: make it obvious
+                objVal = NULL;
+            }
+            tag = tagFromObject(objVal);
+            dvmSetObjectId(buf+1, objectToObjectId(objVal));
+        }
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(expectedLen == 8);
+        memcpy(&longVal, &framePtr[slot], 8);
+        set8BE(buf+1, longVal);
+        break;
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        break;
+    }
+
+    /* prepend tag, which may have been updated */
+    set1(buf, tag);
+}
+
+/*
+ * Copy a new value into an argument or local variable.
+ */
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
+    u8 value, int width)
+{
+    u4* framePtr = frameIdToFrame(frameId);
+
+    UNUSED_PARAMETER(threadId);
+
+    slot = untweakSlot(slot, framePtr);     // Eclipse workaround
+
+    switch (tag) {
+    case JT_BOOLEAN:
+        assert(width == 1);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_BYTE:
+        assert(width == 1);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(width == 2);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(width == 4);
+        framePtr[slot] = (u4)value;
+        break;
+    case JT_STRING:
+        /* The debugger calls VirtualMachine.CreateString to create a new
+         * string, then uses this to set the object reference, when you
+         * edit a String object */
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(width == sizeof(ObjectId));
+        framePtr[slot] = (u4) objectIdToObject(value);
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(width == 8);
+        memcpy(&framePtr[slot], &value, 8);
+        break;
+    case JT_VOID:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+        /* not expecting these from debugger; fall through to failure */
+    default:
+        ALOGE("ERROR: unhandled tag '%c'", tag);
+        assert(false);
+        break;
+    }
+}
+
+
+/*
+ * ===========================================================================
+ *      Debugger notification
+ * ===========================================================================
+ */
+
+/*
+ * Tell JDWP that a breakpoint address has been reached.
+ *
+ * "pcOffset" will be -1 for native methods.
+ * "thisPtr" will be NULL for static methods.
+ */
+void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
+    Object* thisPtr, int eventFlags)
+{
+    JdwpLocation loc;
+
+    if (dvmIsInterfaceClass(method->clazz))
+        loc.typeTag = TT_INTERFACE;
+    else
+        loc.typeTag = TT_CLASS;
+    loc.classId = classObjectToRefTypeId(method->clazz);
+    loc.methodId = methodToMethodId(method);
+    loc.idx = pcOffset;
+
+    /*
+     * Note we use "NoReg" so we don't keep track of references that are
+     * never actually sent to the debugger.  The "thisPtr" is only used to
+     * compare against registered events.
+     */
+
+    if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
+            objectToObjectIdNoReg(thisPtr), eventFlags))
+    {
+        classObjectToRefTypeId(method->clazz);
+        objectToObjectId(thisPtr);
+    }
+}
+
+/*
+ * Tell JDWP that an exception has occurred.
+ */
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+    int catchRelPc, Object* exception)
+{
+    JdwpLocation throwLoc, catchLoc;
+    const Method* throwMeth;
+    const Method* catchMeth;
+
+    throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
+    if (dvmIsInterfaceClass(throwMeth->clazz))
+        throwLoc.typeTag = TT_INTERFACE;
+    else
+        throwLoc.typeTag = TT_CLASS;
+    throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
+    throwLoc.methodId = methodToMethodId(throwMeth);
+    throwLoc.idx = throwRelPc;
+
+    if (catchRelPc < 0) {
+        memset(&catchLoc, 0, sizeof(catchLoc));
+    } else {
+        catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
+        if (dvmIsInterfaceClass(catchMeth->clazz))
+            catchLoc.typeTag = TT_INTERFACE;
+        else
+            catchLoc.typeTag = TT_CLASS;
+        catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
+        catchLoc.methodId = methodToMethodId(catchMeth);
+        catchLoc.idx = catchRelPc;
+    }
+
+    /* need this for InstanceOnly filters */
+    Object* thisObj = getThisObject((u4*)throwFp);
+
+    /*
+     * Hand the event to the JDWP exception handler.  Note we're using the
+     * "NoReg" objectID on the exception, which is not strictly correct --
+     * the exception object WILL be passed up to the debugger if the
+     * debugger is interested in the event.  We do this because the current
+     * implementation of the debugger object registry never throws anything
+     * away, and some people were experiencing a fatal build up of exception
+     * objects when dealing with certain libraries.
+     */
+    dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
+        objectToObjectIdNoReg(exception),
+        classObjectToRefTypeId(exception->clazz), &catchLoc,
+        objectToObjectId(thisObj));
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has started.
+ */
+void dvmDbgPostThreadStart(Thread* thread)
+{
+    if (gDvm.debuggerActive) {
+        dvmJdwpPostThreadChange(gDvm.jdwpState,
+            objectToObjectId(thread->threadObj), true);
+    }
+    if (gDvm.ddmThreadNotification)
+        dvmDdmSendThreadNotification(thread, true);
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has gone away.
+ */
+void dvmDbgPostThreadDeath(Thread* thread)
+{
+    if (gDvm.debuggerActive) {
+        dvmJdwpPostThreadChange(gDvm.jdwpState,
+            objectToObjectId(thread->threadObj), false);
+    }
+    if (gDvm.ddmThreadNotification)
+        dvmDdmSendThreadNotification(thread, false);
+}
+
+/*
+ * Tell JDWP that a new class has been prepared.
+ */
+void dvmDbgPostClassPrepare(ClassObject* clazz)
+{
+    const char* signature;
+    int tag;
+
+    if (dvmIsInterfaceClass(clazz))
+        tag = TT_INTERFACE;
+    else
+        tag = TT_CLASS;
+
+    // TODO - we currently always send both "verified" and "prepared" since
+    // debuggers seem to like that.  There might be some advantage to honesty,
+    // since the class may not yet be verified.
+    signature = jniSignature(clazz);
+    dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
+        signature, CS_VERIFIED | CS_PREPARED);
+}
+
+/*
+ * The JDWP event mechanism has registered an event with a LocationOnly
+ * mod.  Tell the interpreter to call us if we hit the specified
+ * address.
+ */
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
+{
+    Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+    assert(!dvmIsNativeMethod(method));
+    dvmAddBreakAddr(method, pLoc->idx);
+    return true;        /* assume success */
+}
+
+/*
+ * An event with a LocationOnly mod has been removed.
+ */
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
+{
+    Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+    assert(!dvmIsNativeMethod(method));
+    dvmClearBreakAddr(method, pLoc->idx);
+}
+
+/*
+ * The JDWP event mechanism has registered a single-step event.  Tell
+ * the interpreter about it.
+ */
+bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
+    JdwpStepDepth depth)
+{
+    Object* threadObj;
+    Thread* thread;
+    bool result = false;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    /*
+     * Get a pointer to the Thread struct for this ID.  The pointer will
+     * be used strictly for comparisons against the current thread pointer
+     * after the setup is complete, so we can safely release the lock.
+     */
+    dvmLockThreadList(NULL);
+    thread = threadObjToThread(threadObj);
+
+    if (thread == NULL) {
+        ALOGE("Thread for single-step not found");
+        goto bail;
+    }
+    if (!dvmIsSuspended(thread)) {
+        ALOGE("Thread for single-step not suspended");
+        assert(!"non-susp step");      // I want to know if this can happen
+        goto bail;
+    }
+
+    assert(dvmIsSuspended(thread));
+    if (!dvmAddSingleStep(thread, size, depth))
+        goto bail;
+
+    result = true;
+
+bail:
+    dvmUnlockThreadList();
+    return result;
+}
+
+/*
+ * A single-step event has been removed.
+ */
+void dvmDbgUnconfigureStep(ObjectId threadId)
+{
+    UNUSED_PARAMETER(threadId);
+
+    /* right now it's global, so don't need to find Thread */
+    dvmClearSingleStep(NULL);
+}
+
+/*
+ * Invoke a method in a thread that has been stopped on a breakpoint or
+ * other debugger event.  (This function is called from the JDWP thread.)
+ *
+ * Note that access control is not enforced, per spec.
+ */
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+    RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
+    u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
+{
+    Object* threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+
+    Thread* targetThread = threadObjToThread(threadObj);
+    if (targetThread == NULL) {
+        dvmUnlockThreadList();
+        return ERR_INVALID_THREAD;       /* thread does not exist */
+    }
+    if (!targetThread->invokeReq.ready) {
+        dvmUnlockThreadList();
+        return ERR_INVALID_THREAD;       /* thread not stopped by event */
+    }
+
+    /*
+     * We currently have a bug where we don't successfully resume the
+     * target thread if the suspend count is too deep.  We're expected to
+     * require one "resume" for each "suspend", but when asked to execute
+     * a method we have to resume fully and then re-suspend it back to the
+     * same level.  (The easiest way to cause this is to type "suspend"
+     * multiple times in jdb.)
+     *
+     * It's unclear what this means when the event specifies "resume all"
+     * and some threads are suspended more deeply than others.  This is
+     * a rare problem, so for now we just prevent it from hanging forever
+     * by rejecting the method invocation request.  Without this, we will
+     * be stuck waiting on a suspended thread.
+     */
+    if (targetThread->suspendCount > 1) {
+        ALOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
+             "for method exec",
+            dvmThreadSelf()->threadId, targetThread->threadId,
+            targetThread->suspendCount);
+        dvmUnlockThreadList();
+        return ERR_THREAD_SUSPENDED;     /* probably not expected here */
+    }
+
+    /*
+     * TODO: ought to screen the various IDs, and verify that the argument
+     * list is valid.
+     */
+
+    targetThread->invokeReq.obj = objectIdToObject(objectId);
+    targetThread->invokeReq.thread = threadObj;
+    targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
+    targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
+    targetThread->invokeReq.numArgs = numArgs;
+    targetThread->invokeReq.argArray = argArray;
+    targetThread->invokeReq.options = options;
+    targetThread->invokeReq.invokeNeeded = true;
+
+    /*
+     * This is a bit risky -- if the thread goes away we're sitting high
+     * and dry -- but we must release this before the dvmResumeAllThreads
+     * call, and it's unwise to hold it during dvmWaitForSuspend.
+     */
+    dvmUnlockThreadList();
+
+    /*
+     * We change our (JDWP thread) status, which should be THREAD_RUNNING,
+     * so the VM can suspend for a GC if the invoke request causes us to
+     * run out of memory.  It's also a good idea to change it before locking
+     * the invokeReq mutex, although that should never be held for long.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+
+    ALOGV("    Transferring control to event thread");
+    dvmLockMutex(&targetThread->invokeReq.lock);
+
+    if ((options & INVOKE_SINGLE_THREADED) == 0) {
+        ALOGV("      Resuming all threads");
+        dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+    } else {
+        ALOGV("      Resuming event thread only");
+        dvmResumeThread(targetThread);
+    }
+
+    /*
+     * Wait for the request to finish executing.
+     */
+    while (targetThread->invokeReq.invokeNeeded) {
+        pthread_cond_wait(&targetThread->invokeReq.cv,
+                          &targetThread->invokeReq.lock);
+    }
+    dvmUnlockMutex(&targetThread->invokeReq.lock);
+    ALOGV("    Control has returned from event thread");
+
+    /* wait for thread to re-suspend itself */
+    dvmWaitForSuspend(targetThread);
+
+    /*
+     * Done waiting, switch back to RUNNING.
+     */
+    dvmChangeStatus(self, oldStatus);
+
+    /*
+     * Suspend the threads.  We waited for the target thread to suspend
+     * itself, so all we need to do is suspend the others.
+     *
+     * The suspendAllThreads() call will double-suspend the event thread,
+     * so we want to resume the target thread once to keep the books straight.
+     */
+    if ((options & INVOKE_SINGLE_THREADED) == 0) {
+        ALOGV("      Suspending all threads");
+        dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+        ALOGV("      Resuming event thread to balance the count");
+        dvmResumeThread(targetThread);
+    }
+
+    /*
+     * Set up the result.
+     */
+    *pResultTag = targetThread->invokeReq.resultTag;
+    if (isTagPrimitive(targetThread->invokeReq.resultTag))
+        *pResultValue = targetThread->invokeReq.resultValue.j;
+    else {
+        Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
+        *pResultValue = objectToObjectId(tmpObj);
+    }
+    *pExceptObj = targetThread->invokeReq.exceptObj;
+    return targetThread->invokeReq.err;
+}
+
+/*
+ * Return a basic tag value for the return type.
+ */
+static u1 getReturnTypeBasicTag(const Method* method)
+{
+    const char* descriptor = dexProtoGetReturnType(&method->prototype);
+    return basicTagFromDescriptor(descriptor);
+}
+
+/*
+ * Execute the method described by "*pReq".
+ *
+ * We're currently in VMWAIT, because we're stopped on a breakpoint.  We
+ * want to switch to RUNNING while we execute.
+ */
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
+{
+    Thread* self = dvmThreadSelf();
+    const Method* meth;
+    Object* oldExcept;
+    ThreadStatus oldStatus;
+
+    /*
+     * We can be called while an exception is pending in the VM.  We need
+     * to preserve that across the method invocation.
+     */
+    oldExcept = dvmGetException(self);
+    if (oldExcept != NULL) {
+        dvmAddTrackedAlloc(oldExcept, self);
+        dvmClearException(self);
+    }
+
+    oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Translate the method through the vtable, unless we're calling a
+     * direct method or the debugger wants to suppress it.
+     */
+    if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
+        dvmIsDirectMethod(pReq->method))
+    {
+        meth = pReq->method;
+    } else {
+        meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
+    }
+    assert(meth != NULL);
+
+    assert(sizeof(jvalue) == sizeof(u8));
+
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        ALOGV("JDWP invoking method %p/%p %s.%s:%s",
+            pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
+        free(desc);
+    }
+
+    dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
+        (jvalue*)pReq->argArray);
+    pReq->exceptObj = objectToObjectId(dvmGetException(self));
+    pReq->resultTag = getReturnTypeBasicTag(meth);
+    if (pReq->exceptObj != 0) {
+        Object* exc = dvmGetException(self);
+        ALOGD("  JDWP invocation returning with exceptObj=%p (%s)",
+            exc, exc->clazz->descriptor);
+        //dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        /*
+         * Nothing should try to use this, but it looks like something is.
+         * Make it null to be safe.
+         */
+        pReq->resultValue.j = 0; /*0xadadadad;*/
+    } else if (pReq->resultTag == JT_OBJECT) {
+        /* if no exception thrown, examine object result more closely */
+        u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
+        if (newTag != pReq->resultTag) {
+            LOGVV("  JDWP promoted result from %d to %d",
+                pReq->resultTag, newTag);
+            pReq->resultTag = newTag;
+        }
+
+        /*
+         * Register the object.  We don't actually need an ObjectId yet,
+         * but we do need to be sure that the GC won't move or discard the
+         * object when we switch out of RUNNING.  The ObjectId conversion
+         * will add the object to the "do not touch" list.
+         *
+         * We can't use the "tracked allocation" mechanism here because
+         * the object is going to be handed off to a different thread.
+         */
+        objectToObjectId((Object*)pReq->resultValue.l);
+    }
+
+    if (oldExcept != NULL) {
+        dvmSetException(self, oldExcept);
+        dvmReleaseTrackedAlloc(oldExcept, self);
+    }
+    dvmChangeStatus(self, oldStatus);
+}
+
+// for dvmAddressSetForLine
+struct AddressSetContext {
+    bool lastAddressValid;
+    u4 lastAddress;
+    u4 lineNum;
+    AddressSet *pSet;
+};
+
+// for dvmAddressSetForLine
+static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
+{
+    AddressSetContext *pContext = (AddressSetContext *)cnxt;
+
+    if (lineNum == pContext->lineNum) {
+        if (!pContext->lastAddressValid) {
+            // Everything from this address until the next line change is ours
+            pContext->lastAddress = address;
+            pContext->lastAddressValid = true;
+        }
+        // else, If we're already in a valid range for this lineNum,
+        // just keep going (shouldn't really happen)
+    } else if (pContext->lastAddressValid) { // and the line number is new
+        u4 i;
+        // Add everything from the last entry up until here to the set
+        for (i = pContext->lastAddress; i < address; i++) {
+            dvmAddressSetSet(pContext->pSet, i);
+        }
+
+        pContext->lastAddressValid = false;
+    }
+
+    // there may be multiple entries for a line
+    return 0;
+}
+/*
+ * Build up a set of bytecode addresses associated with a line number
+ */
+const AddressSet *dvmAddressSetForLine(const Method* method, int line)
+{
+    AddressSet *result;
+    const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
+    u4 insnsSize = dvmGetMethodInsnsSize(method);
+    AddressSetContext context;
+
+    result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
+    result->setSize = insnsSize;
+
+    memset(&context, 0, sizeof(context));
+    context.pSet = result;
+    context.lineNum = line;
+    context.lastAddressValid = false;
+
+    dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
+        method->clazz->descriptor,
+        method->prototype.protoIdx,
+        method->accessFlags,
+        addressSetCb, NULL, &context);
+
+    // If the line number was the last in the position table...
+    if (context.lastAddressValid) {
+        u4 i;
+        for (i = context.lastAddress; i < insnsSize; i++) {
+            dvmAddressSetSet(result, i);
+        }
+    }
+
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      Dalvik Debug Monitor support
+ * ===========================================================================
+ */
+
+/*
+ * We have received a DDM packet over JDWP.  Hand it off to the VM.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen)
+{
+    return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
+}
+
+/*
+ * First DDM packet has arrived over JDWP.  Notify the press.
+ */
+void dvmDbgDdmConnected()
+{
+    dvmDdmConnected();
+}
+
+/*
+ * JDWP connection has dropped.
+ */
+void dvmDbgDdmDisconnected()
+{
+    dvmDdmDisconnected();
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it.
+ */
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
+{
+    assert(buf != NULL);
+    struct iovec vec[1] = { {(void*)buf, len} };
+    dvmDbgDdmSendChunkV(type, vec, 1);
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it.  The chunk is
+ * concatenated from multiple source buffers.
+ */
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
+{
+    if (gDvm.jdwpState == NULL) {
+        ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
+            type);
+        return;
+    }
+
+    dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
+}
diff --git a/vm/Debugger.h b/vm/Debugger.h
new file mode 100644
index 0000000..760463e
--- /dev/null
+++ b/vm/Debugger.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik-specific side of debugger support.  (The JDWP code is intended to
+ * be relatively generic.)
+ */
+#ifndef DALVIK_DEBUGGER_H_
+#define DALVIK_DEBUGGER_H_
+
+#include <pthread.h>
+#include "Common.h"
+#include "Misc.h"
+#include "jdwp/Jdwp.h"
+
+/* fwd decl */
+struct Object;
+struct ClassObject;
+struct Method;
+struct Thread;
+
+/*
+ * Used by StepControl to track a set of addresses associated with
+ * a single line.
+ */
+struct AddressSet {
+    u4 setSize;
+    u1 set[1];
+};
+
+INLINE void dvmAddressSetSet(AddressSet *pSet, u4 toSet)
+{
+    if (toSet < pSet->setSize) {
+        pSet->set[toSet/8] |= 1 << (toSet % 8);
+    }
+}
+
+INLINE bool dvmAddressSetGet(const AddressSet *pSet, u4 toGet)
+{
+    if (toGet < pSet->setSize) {
+        return (pSet->set[toGet/8] & (1 << (toGet % 8))) != 0;
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Single-step management.
+ */
+struct StepControl {
+    /* request */
+    JdwpStepSize size;
+    JdwpStepDepth depth;
+    Thread* thread;         /* don't deref; for comparison only */
+
+    /* current state */
+    bool active;
+    const Method* method;
+    int line;           /* line #; could be -1 */
+    const AddressSet* pAddressSet;    /* if non-null, address set for line */
+    int frameDepth;
+};
+
+/*
+ * Invoke-during-breakpoint support.
+ */
+struct DebugInvokeReq {
+    /* boolean; only set when we're in the tail end of an event handler */
+    bool ready;
+
+    /* boolean; set if the JDWP thread wants this thread to do work */
+    bool invokeNeeded;
+
+    /* request */
+    Object* obj;        /* not used for ClassType.InvokeMethod */
+    Object* thread;
+    ClassObject* clazz;
+    Method* method;
+    u4 numArgs;
+    u8* argArray;   /* will be NULL if numArgs==0 */
+    u4 options;
+
+    /* result */
+    JdwpError err;
+    u1 resultTag;
+    JValue resultValue;
+    ObjectId exceptObj;
+
+    /* condition variable to wait on while the method executes */
+    pthread_mutex_t lock;
+    pthread_cond_t cv;
+};
+
+/* system init/shutdown */
+bool dvmDebuggerStartup(void);
+void dvmDebuggerShutdown(void);
+
+void dvmDbgInitMutex(pthread_mutex_t* pMutex);
+void dvmDbgLockMutex(pthread_mutex_t* pMutex);
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex);
+void dvmDbgInitCond(pthread_cond_t* pCond);
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex);
+void dvmDbgCondSignal(pthread_cond_t* pCond);
+void dvmDbgCondBroadcast(pthread_cond_t* pCond);
+
+/*
+ * Return the DebugInvokeReq for the current thread.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq(void);
+
+/*
+ * Enable/disable breakpoints and step modes.  Used to provide a heads-up
+ * when the debugger attaches.
+ */
+void dvmDbgConnected(void);
+void dvmDbgActive(void);
+void dvmDbgDisconnected(void);
+
+/*
+ * Returns "true" if a debugger is connected.  Returns "false" if it's
+ * just DDM.
+ */
+bool dvmDbgIsDebuggerConnected(void);
+
+/*
+ * Time, in milliseconds, since the last debugger activity.  Does not
+ * include DDMS activity.  Returns -1 if there has been no activity.
+ * Returns 0 if we're in the middle of handling a debugger request.
+ */
+s8 dvmDbgLastDebuggerActivity(void);
+
+/*
+ * Block/allow GC depending on what we're doing.  These return the old
+ * status, which can be fed to dvmDbgThreadGoing() to restore the previous
+ * mode.
+ */
+int dvmDbgThreadRunning(void);
+int dvmDbgThreadWaiting(void);
+int dvmDbgThreadContinuing(int status);
+
+/*
+ * The debugger wants the VM to exit.
+ */
+void dvmDbgExit(int status);
+
+/*
+ * Class, Object, Array
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id);
+ObjectId dvmDbgGetClassObject(RefTypeId id);
+RefTypeId dvmDbgGetSuperclass(RefTypeId id);
+ObjectId dvmDbgGetClassLoader(RefTypeId id);
+u4 dvmDbgGetAccessFlags(RefTypeId id);
+bool dvmDbgIsInterface(RefTypeId id);
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf);
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+        RefTypeId** pClassRefBuf);
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+    const char** pSignature);
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+        RefTypeId* pRefTypeId);
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+    RefTypeId* pRefTypeId);
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId);
+const char* dvmDbgGetSignature(RefTypeId refTypeId);
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId);
+const char* dvmDbgGetObjectTypeName(ObjectId objectId);
+u1 dvmDbgGetObjectTag(ObjectId objectId);
+int dvmDbgGetTagWidth(int tag);
+
+int dvmDbgGetArrayLength(ObjectId arrayId);
+u1 dvmDbgGetArrayElementTag(ObjectId arrayId);
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+    ExpandBuf* pReply);
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+    const u1* buf);
+
+ObjectId dvmDbgCreateString(const char* str);
+ObjectId dvmDbgCreateObject(RefTypeId classId);
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length);
+
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId);
+
+/*
+ * Method and Field
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id);
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply);
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply);
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply);
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+    ExpandBuf* pReply);
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId id,
+    bool withGeneric, ExpandBuf* pReply);
+
+u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId);
+u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId);
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply);
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+    int width);
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    ExpandBuf* pReply);
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+    u8 rawValue, int width);
+
+char* dvmDbgStringToUtf8(ObjectId strId);
+
+/*
+ * Thread, ThreadGroup, Frame
+ */
+char* dvmDbgGetThreadName(ObjectId threadId);
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId);
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId);
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId);
+ObjectId dvmDbgGetSystemThreadGroupId(void);
+ObjectId dvmDbgGetMainThreadGroupId(void);
+
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* threadStatus,
+    u4* suspendStatus);
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId);
+bool dvmDbgThreadExists(ObjectId threadId);
+bool dvmDbgIsSuspended(ObjectId threadId);
+//void dvmDbgWaitForSuspend(ObjectId threadId);
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+    ObjectId** ppThreadIds, u4* pThreadCount);
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount);
+int dvmDbgGetThreadFrameCount(ObjectId threadId);
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+    JdwpLocation* pLoc);
+
+ObjectId dvmDbgGetThreadSelfId(void);
+void dvmDbgSuspendVM(bool isEvent);
+void dvmDbgResumeVM(void);
+void dvmDbgSuspendThread(ObjectId threadId);
+void dvmDbgResumeThread(ObjectId threadId);
+void dvmDbgSuspendSelf(void);
+
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId);
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u1* buf, int expectedLen);
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+    u1 tag, u8 value, int width);
+
+
+/*
+ * Debugger notification
+ */
+void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
+    Object* thisPtr, int eventFlags);
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+    int catchRelPc, Object* exception);
+void dvmDbgPostThreadStart(Thread* thread);
+void dvmDbgPostThreadDeath(Thread* thread);
+void dvmDbgPostClassPrepare(ClassObject* clazz);
+
+/* for "eventFlags" */
+enum {
+    DBG_BREAKPOINT      = 0x01,
+    DBG_SINGLE_STEP     = 0x02,
+    DBG_METHOD_ENTRY    = 0x04,
+    DBG_METHOD_EXIT     = 0x08,
+};
+
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc);
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc);
+bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
+    JdwpStepDepth depth);
+void dvmDbgUnconfigureStep(ObjectId threadId);
+
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+    RefTypeId classId, MethodId methodId, u4 numArgs, u8* argArray,
+    u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj);
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq);
+
+/* Make an AddressSet for a line, for single stepping */
+const AddressSet *dvmAddressSetForLine(const Method* method, int line);
+
+/* perform "late registration" of an object ID */
+void dvmDbgRegisterObjectId(ObjectId id);
+
+/*
+ * DDM support.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+    int* pReplyLen);
+void dvmDbgDdmConnected(void);
+void dvmDbgDdmDisconnected(void);
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf);
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt);
+
+#define CHUNK_TYPE(_name) \
+    ((_name)[0] << 24 | (_name)[1] << 16 | (_name)[2] << 8 | (_name)[3])
+
+#endif  // DALVIK_DEBUGGER_H_
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
new file mode 100644
index 0000000..67bd5a3
--- /dev/null
+++ b/vm/Dvm.mk
@@ -0,0 +1,372 @@
+# Copyright (C) 2008 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.
+
+#
+# Common definitions for host or target builds of libdvm.
+#
+# If you enable or disable optional features here, make sure you do
+# a "clean" build -- not everything depends on Dalvik.h.  (See Android.mk
+# for the exact command.)
+#
+
+
+#
+# Compiler defines.
+#
+
+LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2
+LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-variable
+LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"
+LOCAL_CFLAGS += -D__STDC_LIMIT_MACROS
+
+ifneq ($(strip $(LOCAL_CLANG)),true)
+LOCAL_CFLAGS += -fno-align-jumps
+endif
+
+ifeq ($(MALLOC_IMPL),jemalloc)
+LOCAL_CFLAGS += -DUSE_JEMALLOC
+else
+LOCAL_CFLAGS += -DUSE_DLMALLOC
+endif
+
+#
+# Optional features.  These may impact the size or performance of the VM.
+#
+
+# Make a debugging version when building the simulator (if not told
+# otherwise) and when explicitly asked.
+dvm_make_debug_vm := false
+ifneq ($(strip $(DEBUG_DALVIK_VM)),)
+  dvm_make_debug_vm := $(DEBUG_DALVIK_VM)
+endif
+
+ifeq ($(dvm_make_debug_vm),true)
+  #
+  # "Debug" profile:
+  # - debugger enabled
+  # - profiling enabled
+  # - tracked-reference verification enabled
+  # - allocation limits enabled
+  # - GDB helpers enabled
+  # - LOGV
+  # - assert()
+  #
+  LOCAL_CFLAGS += -DWITH_INSTR_CHECKS
+  LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+  LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
+  LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1
+  #LOCAL_CFLAGS += -DCHECK_MUTEX
+  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
+  # add some extra stuff to make it easier to examine with GDB
+  LOCAL_CFLAGS += -DEASY_GDB
+  # overall config may be for a "release" build, so reconfigure these
+  LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+else  # !dvm_make_debug_vm
+  #
+  # "Performance" profile:
+  # - all development features disabled
+  # - compiler optimizations enabled (redundant for "release" builds)
+  # - (debugging and profiling still enabled)
+  #
+  #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
+  # "-O2" is redundant for device (release) but useful for sim (debug)
+  #LOCAL_CFLAGS += -O2 -Winline
+  #LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+  LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
+  # if you want to try with assertions on the device, add:
+  #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+endif  # !dvm_make_debug_vm
+
+# bug hunting: checksum and verify interpreted stack when making JNI calls
+#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK
+
+LOCAL_SRC_FILES := \
+	AllocTracker.cpp \
+	Atomic.cpp.arm \
+	AtomicCache.cpp \
+	BitVector.cpp.arm \
+	CheckJni.cpp \
+	Ddm.cpp \
+	Debugger.cpp \
+	DvmDex.cpp \
+	Exception.cpp \
+	Hash.cpp \
+	IndirectRefTable.cpp.arm \
+	Init.cpp \
+	InitRefs.cpp \
+	InlineNative.cpp.arm \
+	Inlines.cpp \
+	Intern.cpp \
+	Jni.cpp \
+	JarFile.cpp \
+	LinearAlloc.cpp \
+	Misc.cpp \
+	Native.cpp \
+	PointerSet.cpp \
+	Profile.cpp \
+	RawDexFile.cpp \
+	ReferenceTable.cpp \
+	SignalCatcher.cpp \
+	StdioConverter.cpp \
+	Sync.cpp \
+	Thread.cpp \
+	UtfString.cpp \
+	alloc/Alloc.cpp \
+	alloc/CardTable.cpp \
+	alloc/HeapBitmap.cpp.arm \
+	alloc/HeapDebug.cpp \
+	alloc/Heap.cpp.arm \
+	alloc/DdmHeap.cpp \
+	alloc/Verify.cpp \
+	alloc/Visit.cpp \
+	analysis/CodeVerify.cpp \
+	analysis/DexPrepare.cpp \
+	analysis/DexVerify.cpp \
+	analysis/Liveness.cpp \
+	analysis/Optimize.cpp \
+	analysis/RegisterMap.cpp \
+	analysis/VerifySubs.cpp \
+	analysis/VfyBasicBlock.cpp \
+	hprof/Hprof.cpp \
+	hprof/HprofClass.cpp \
+	hprof/HprofHeap.cpp \
+	hprof/HprofOutput.cpp \
+	hprof/HprofString.cpp \
+	interp/Interp.cpp.arm \
+	interp/Stack.cpp \
+	jdwp/ExpandBuf.cpp \
+	jdwp/JdwpAdb.cpp \
+	jdwp/JdwpConstants.cpp \
+	jdwp/JdwpEvent.cpp \
+	jdwp/JdwpHandler.cpp \
+	jdwp/JdwpMain.cpp \
+	jdwp/JdwpSocket.cpp \
+	mterp/Mterp.cpp.arm \
+	mterp/out/InterpC-portable.cpp.arm \
+	native/InternalNative.cpp \
+	native/dalvik_bytecode_OpcodeInfo.cpp \
+	native/dalvik_system_DexFile.cpp \
+	native/dalvik_system_VMDebug.cpp \
+	native/dalvik_system_VMRuntime.cpp \
+	native/dalvik_system_VMStack.cpp \
+	native/dalvik_system_ZygoteHooks.cpp \
+	native/java_lang_Class.cpp \
+	native/java_lang_Double.cpp \
+	native/java_lang_Float.cpp \
+	native/java_lang_Math.cpp \
+	native/java_lang_Object.cpp \
+	native/java_lang_Runtime.cpp \
+	native/java_lang_String.cpp \
+	native/java_lang_System.cpp \
+	native/java_lang_Throwable.cpp \
+	native/java_lang_VMClassLoader.cpp \
+	native/java_lang_VMThread.cpp \
+	native/java_lang_reflect_AccessibleObject.cpp \
+	native/java_lang_reflect_Array.cpp \
+	native/java_lang_reflect_Constructor.cpp \
+	native/java_lang_reflect_Field.cpp \
+	native/java_lang_reflect_Method.cpp \
+	native/java_lang_reflect_Proxy.cpp \
+	native/java_util_concurrent_atomic_AtomicLong.cpp \
+	native/org_apache_harmony_dalvik_NativeTestTarget.cpp \
+	native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp \
+	native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp \
+	native/sun_misc_Unsafe.cpp \
+	oo/AccessCheck.cpp \
+	oo/Array.cpp \
+	oo/Class.cpp \
+	oo/Object.cpp \
+	oo/Resolve.cpp \
+	oo/TypeCheck.cpp \
+	reflect/Annotation.cpp \
+	reflect/Proxy.cpp \
+	reflect/Reflect.cpp \
+	test/TestHash.cpp \
+	test/TestIndirectRefTable.cpp
+
+# TODO: this is the wrong test, but what's the right one?
+ifneq ($(filter arm mips,$(dvm_arch)),)
+  LOCAL_SRC_FILES += os/android.cpp
+else
+  LOCAL_SRC_FILES += os/linux.cpp
+endif
+
+WITH_COPYING_GC := $(strip $(WITH_COPYING_GC))
+
+ifeq ($(WITH_COPYING_GC),true)
+  LOCAL_CFLAGS += -DWITH_COPYING_GC
+  LOCAL_SRC_FILES += \
+	alloc/Copying.cpp.arm
+else
+  LOCAL_SRC_FILES += \
+	alloc/DlMalloc.cpp \
+	alloc/HeapSource.cpp \
+	alloc/MarkSweep.cpp.arm
+endif
+
+WITH_JIT := $(strip $(WITH_JIT))
+
+ifeq ($(WITH_JIT),true)
+  LOCAL_CFLAGS += -DWITH_JIT
+  LOCAL_SRC_FILES += \
+	compiler/Compiler.cpp \
+	compiler/Frontend.cpp \
+	compiler/Utility.cpp \
+	compiler/InlineTransformation.cpp \
+	compiler/IntermediateRep.cpp \
+	compiler/Dataflow.cpp \
+	compiler/SSATransformation.cpp \
+	compiler/Loop.cpp \
+	compiler/Ralloc.cpp \
+	interp/Jit.cpp
+endif
+
+LOCAL_C_INCLUDES += \
+	dalvik \
+	dalvik/vm \
+	external/zlib \
+	libcore/include \
+
+MTERP_ARCH_KNOWN := false
+
+ifeq ($(dvm_arch),arm)
+  #dvm_arch_variant := armv7-a
+  #LOCAL_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp
+  LOCAL_CFLAGS += -Werror
+  MTERP_ARCH_KNOWN := true
+  # Select architecture-specific sources (armv5te, armv7-a, etc.)
+  LOCAL_SRC_FILES += \
+		arch/arm/CallOldABI.S \
+		arch/arm/CallEABI.S \
+		arch/arm/HintsEABI.cpp \
+		mterp/out/InterpC-$(dvm_arch_variant).cpp.arm \
+		mterp/out/InterpAsm-$(dvm_arch_variant).S
+
+  ifeq ($(WITH_JIT),true)
+    LOCAL_SRC_FILES += \
+		compiler/codegen/RallocUtil.cpp \
+		compiler/codegen/arm/$(dvm_arch_variant)/Codegen.cpp \
+		compiler/codegen/arm/$(dvm_arch_variant)/CallingConvention.S \
+		compiler/codegen/arm/Assemble.cpp \
+		compiler/codegen/arm/ArchUtility.cpp \
+		compiler/codegen/arm/LocalOptimizations.cpp \
+		compiler/codegen/arm/GlobalOptimizations.cpp \
+		compiler/codegen/arm/ArmRallocUtil.cpp \
+		compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+  endif
+endif
+
+ifeq ($(dvm_arch),mips)
+  MTERP_ARCH_KNOWN := true
+  LOCAL_C_INCLUDES += external/libffi/$(TARGET_OS)-$(TARGET_ARCH)
+  LOCAL_SHARED_LIBRARIES += libffi
+  LOCAL_SRC_FILES += \
+		arch/mips/CallO32.S \
+		arch/mips/HintsO32.cpp \
+		arch/generic/Call.cpp \
+		mterp/out/InterpC-mips.cpp \
+		mterp/out/InterpAsm-mips.S
+
+  ifeq ($(WITH_JIT),true)
+    dvm_arch_variant := mips
+    LOCAL_SRC_FILES += \
+		compiler/codegen/mips/RallocUtil.cpp \
+		compiler/codegen/mips/$(dvm_arch_variant)/Codegen.cpp \
+		compiler/codegen/mips/$(dvm_arch_variant)/CallingConvention.S \
+		compiler/codegen/mips/Assemble.cpp \
+		compiler/codegen/mips/ArchUtility.cpp \
+		compiler/codegen/mips/LocalOptimizations.cpp \
+		compiler/codegen/mips/GlobalOptimizations.cpp \
+		compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+  endif
+
+  ifeq ($(strip $(ARCH_HAVE_ALIGNED_DOUBLES)),true)
+    LOCAL_CFLAGS += -DARCH_HAVE_ALIGNED_DOUBLES
+  endif
+endif
+
+ifeq ($(dvm_arch),x86)
+  ifeq ($(dvm_os),linux)
+    MTERP_ARCH_KNOWN := true
+    LOCAL_CFLAGS += -DDVM_JMP_TABLE_MTERP=1 \
+                    -DMTERP_STUB
+    LOCAL_SRC_FILES += \
+		arch/$(dvm_arch)/Call386ABI.S \
+		arch/$(dvm_arch)/Hints386ABI.cpp \
+		mterp/out/InterpC-$(dvm_arch).cpp \
+		mterp/out/InterpAsm-$(dvm_arch).S
+    ifeq ($(WITH_JIT),true)
+      LOCAL_CFLAGS += -DARCH_IA32
+      LOCAL_SRC_FILES += \
+                compiler/codegen/x86/LowerAlu.cpp \
+                compiler/codegen/x86/LowerConst.cpp \
+                compiler/codegen/x86/LowerMove.cpp \
+                compiler/codegen/x86/Lower.cpp \
+                compiler/codegen/x86/LowerHelper.cpp \
+                compiler/codegen/x86/LowerJump.cpp \
+                compiler/codegen/x86/LowerObject.cpp \
+                compiler/codegen/x86/AnalysisO1.cpp \
+                compiler/codegen/x86/BytecodeVisitor.cpp \
+                compiler/codegen/x86/NcgAot.cpp \
+                compiler/codegen/x86/CodegenInterface.cpp \
+                compiler/codegen/x86/LowerInvoke.cpp \
+                compiler/codegen/x86/LowerReturn.cpp \
+                compiler/codegen/x86/NcgHelper.cpp \
+                compiler/codegen/x86/LowerGetPut.cpp
+
+      # need apache harmony x86 encoder/decoder
+      LOCAL_C_INCLUDES += \
+                dalvik/vm/compiler/codegen/x86/libenc
+      LOCAL_SRC_FILES += \
+                compiler/codegen/x86/libenc/enc_base.cpp \
+                compiler/codegen/x86/libenc/dec_base.cpp \
+                compiler/codegen/x86/libenc/enc_wrapper.cpp \
+                compiler/codegen/x86/libenc/enc_tabl.cpp
+
+    endif
+  endif
+endif
+
+ifeq ($(MTERP_ARCH_KNOWN),false)
+  # unknown architecture, try to use FFI
+  LOCAL_C_INCLUDES += external/libffi/$(dvm_os)-$(dvm_arch)
+
+  ifeq ($(dvm_os)-$(dvm_arch),darwin-x86)
+      # OSX includes libffi, so just make the linker aware of it directly.
+      LOCAL_LDLIBS += -lffi
+  else
+      LOCAL_SHARED_LIBRARIES += libffi
+  endif
+
+  LOCAL_SRC_FILES += \
+		arch/generic/Call.cpp \
+		arch/generic/Hints.cpp \
+		mterp/out/InterpC-allstubs.cpp
+
+  # The following symbols are usually defined in the asm file, but
+  # since we don't have an asm file in this case, we instead just
+  # peg them at 0 here, and we add an #ifdef'able define for good
+  # measure, too.
+  LOCAL_CFLAGS += -DdvmAsmInstructionStart=0 -DdvmAsmInstructionEnd=0 \
+	-DdvmAsmSisterStart=0 -DdvmAsmSisterEnd=0 -DDVM_NO_ASM_INTERP=1
+endif
+
+# Needed because getLongFromArray etc. are defined in
+# vm/mterp/c/header.cpp, but only used if some asm
+# implementations aren't available.
+# To fix this without generating unused functions,
+# gen-mterp.py would need to be a lot more intelligent
+# (picking just the parts of header.cpp that are
+# actually used in C code). Doesn't seem to be worth it.
+LOCAL_CFLAGS += -Wno-error=unused-function
diff --git a/vm/DvmDex.cpp b/vm/DvmDex.cpp
new file mode 100644
index 0000000..28cd64e
--- /dev/null
+++ b/vm/DvmDex.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * VM-specific state associated with a DEX file.
+ */
+#include "Dalvik.h"
+#include <sys/mman.h>
+
+/*
+ * Create auxillary data structures.
+ *
+ * We need a 4-byte pointer for every reference to a class, method, field,
+ * or string constant.  Summed up over all loaded DEX files (including the
+ * whoppers in the boostrap class path), this adds up to be quite a bit
+ * of native memory.
+ *
+ * For more traditional VMs these values could be stuffed into the loaded
+ * class file constant pool area, but we don't have that luxury since our
+ * classes are memory-mapped read-only.
+ *
+ * The DEX optimizer will remove the need for some of these (e.g. we won't
+ * use the entry for virtual methods that are only called through
+ * invoke-virtual-quick), creating the possibility of some space reduction
+ * at dexopt time.
+ */
+
+static DvmDex* allocateAuxStructures(DexFile* pDexFile)
+{
+    DvmDex* pDvmDex;
+    const DexHeader* pHeader;
+    u4 stringSize, classSize, methodSize, fieldSize;
+
+    pHeader = pDexFile->pHeader;
+
+    stringSize = pHeader->stringIdsSize * sizeof(struct StringObject*);
+    classSize  = pHeader->typeIdsSize * sizeof(struct ClassObject*);
+    methodSize = pHeader->methodIdsSize * sizeof(struct Method*);
+    fieldSize  = pHeader->fieldIdsSize * sizeof(struct Field*);
+
+    u4 totalSize = sizeof(DvmDex) +
+                   stringSize + classSize + methodSize + fieldSize;
+
+    u1 *blob = (u1 *)dvmAllocRegion(totalSize,
+                              PROT_READ | PROT_WRITE, "dalvik-aux-structure");
+    if ((void *)blob == MAP_FAILED)
+        return NULL;
+
+    pDvmDex = (DvmDex*)blob;
+    blob += sizeof(DvmDex);
+
+    pDvmDex->pDexFile = pDexFile;
+    pDvmDex->pHeader = pHeader;
+
+    pDvmDex->pResStrings = (struct StringObject**)blob;
+    blob += stringSize;
+    pDvmDex->pResClasses = (struct ClassObject**)blob;
+    blob += classSize;
+    pDvmDex->pResMethods = (struct Method**)blob;
+    blob += methodSize;
+    pDvmDex->pResFields = (struct Field**)blob;
+
+    ALOGV("+++ DEX %p: allocateAux (%d+%d+%d+%d)*4 = %d bytes",
+        pDvmDex, stringSize/4, classSize/4, methodSize/4, fieldSize/4,
+        stringSize + classSize + methodSize + fieldSize);
+
+    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
+
+    dvmInitMutex(&pDvmDex->modLock);
+
+    return pDvmDex;
+}
+
+/*
+ * Given an open optimized DEX file, map it into read-only shared memory and
+ * parse the contents.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
+{
+    DvmDex* pDvmDex;
+    DexFile* pDexFile;
+    MemMapping memMap;
+    int parseFlags = kDexParseDefault;
+    int result = -1;
+
+    if (gDvm.verifyDexChecksum)
+        parseFlags |= kDexParseVerifyChecksum;
+
+    if (lseek(fd, 0, SEEK_SET) < 0) {
+        ALOGE("lseek rewind failed");
+        goto bail;
+    }
+
+    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
+        ALOGE("Unable to map file");
+        goto bail;
+    }
+
+    pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
+    if (pDexFile == NULL) {
+        ALOGE("DEX parse failed");
+        sysReleaseShmem(&memMap);
+        goto bail;
+    }
+
+    pDvmDex = allocateAuxStructures(pDexFile);
+    if (pDvmDex == NULL) {
+        dexFileFree(pDexFile);
+        sysReleaseShmem(&memMap);
+        goto bail;
+    }
+
+    /* tuck this into the DexFile so it gets released later */
+    sysCopyMap(&pDvmDex->memMap, &memMap);
+    pDvmDex->isMappedReadOnly = true;
+    *ppDvmDex = pDvmDex;
+    result = 0;
+
+bail:
+    return result;
+}
+
+/*
+ * Create a DexFile structure for a "partial" DEX.  This is one that is in
+ * the process of being optimized.  The optimization header isn't finished
+ * and we won't have any of the auxillary data tables, so we have to do
+ * the initialization slightly differently.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
+{
+    DvmDex* pDvmDex;
+    DexFile* pDexFile;
+    int parseFlags = kDexParseDefault;
+    int result = -1;
+
+    /* -- file is incomplete, new checksum has not yet been calculated
+    if (gDvm.verifyDexChecksum)
+        parseFlags |= kDexParseVerifyChecksum;
+    */
+
+    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
+    if (pDexFile == NULL) {
+        ALOGE("DEX parse failed");
+        goto bail;
+    }
+    pDvmDex = allocateAuxStructures(pDexFile);
+    if (pDvmDex == NULL) {
+        dexFileFree(pDexFile);
+        goto bail;
+    }
+
+    pDvmDex->isMappedReadOnly = false;
+    *ppDvmDex = pDvmDex;
+    result = 0;
+
+bail:
+    return result;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DvmDex.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex)
+{
+    u4 totalSize;
+
+    if (pDvmDex == NULL)
+        return;
+
+    dvmDestroyMutex(&pDvmDex->modLock);
+
+    totalSize  = pDvmDex->pHeader->stringIdsSize * sizeof(struct StringObject*);
+    totalSize += pDvmDex->pHeader->typeIdsSize * sizeof(struct ClassObject*);
+    totalSize += pDvmDex->pHeader->methodIdsSize * sizeof(struct Method*);
+    totalSize += pDvmDex->pHeader->fieldIdsSize * sizeof(struct Field*);
+    totalSize += sizeof(DvmDex);
+
+    dexFileFree(pDvmDex->pDexFile);
+
+    ALOGV("+++ DEX %p: freeing aux structs", pDvmDex);
+    dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
+    sysReleaseShmem(&pDvmDex->memMap);
+    munmap(pDvmDex, totalSize);
+}
+
+
+/*
+ * Change the byte at the specified address to a new value.  If the location
+ * already has the new value, do nothing.
+ *
+ * This requires changing the access permissions to read-write, updating
+ * the value, and then resetting the permissions.
+ *
+ * We need to ensure mutual exclusion at a page granularity to avoid a race
+ * where one threads sets read-write, another thread sets read-only, and
+ * then the first thread does a write.  Since we don't do a lot of updates,
+ * and the window is small, we just use a lock across the entire DvmDex.
+ * We're only trying to make the page state change atomic; it's up to the
+ * caller to ensure that multiple threads aren't stomping on the same
+ * location (e.g. breakpoints and verifier/optimizer changes happening
+ * simultaneously).
+ *
+ * TODO: if we're back to the original state of the page, use
+ * madvise(MADV_DONTNEED) to release the private/dirty copy.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
+{
+    if (*addr == newVal) {
+        ALOGV("+++ byte at %p is already 0x%02x", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    ALOGV("+++ change byte at %p from 0x%02x to 0x%02x", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RW) failed");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RO) failed");
+        /* expected on files mounted from FAT; keep going */
+    }
+
+    dvmUnlockMutex(&pDvmDex->modLock);
+
+    return true;
+}
+
+/*
+ * Change the 2-byte value at the specified address to a new value.  If the
+ * location already has the new value, do nothing.
+ *
+ * Otherwise works like dvmDexChangeDex1.
+ */
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
+{
+    if (*addr == newVal) {
+        ALOGV("+++ value at %p is already 0x%04x", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    ALOGV("+++ change 2byte at %p from 0x%04x to 0x%04x", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RW) failed");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
+        ALOGD("NOTE: DEX page access change (->RO) failed");
+        /* expected on files mounted from FAT; keep going */
+    }
+
+    dvmUnlockMutex(&pDvmDex->modLock);
+
+    return true;
+}
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
new file mode 100644
index 0000000..1785c26
--- /dev/null
+++ b/vm/DvmDex.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * The VM wraps some additional data structures around the DexFile.  These
+ * are defined here.
+ */
+#ifndef DALVIK_DVMDEX_H_
+#define DALVIK_DVMDEX_H_
+
+#include "jni.h"
+#include "libdex/DexFile.h"
+
+/* extern */
+struct ClassObject;
+struct HashTable;
+struct InstField;
+struct Method;
+struct StringObject;
+
+
+/*
+ * Some additional VM data structures that are associated with the DEX file.
+ */
+struct DvmDex {
+    /* pointer to the DexFile we're associated with */
+    DexFile*            pDexFile;
+
+    /* clone of pDexFile->pHeader (it's used frequently enough) */
+    const DexHeader*    pHeader;
+
+    /* interned strings; parallel to "stringIds" */
+    struct StringObject** pResStrings;
+
+    /* resolved classes; parallel to "typeIds" */
+    struct ClassObject** pResClasses;
+
+    /* resolved methods; parallel to "methodIds" */
+    struct Method**     pResMethods;
+
+    /* resolved instance fields; parallel to "fieldIds" */
+    /* (this holds both InstField and StaticField) */
+    struct Field**      pResFields;
+
+    /* interface method lookup cache */
+    struct AtomicCache* pInterfaceCache;
+
+    /* shared memory region with file contents */
+    bool                isMappedReadOnly;
+    MemMapping          memMap;
+
+    jobject dex_object;
+
+    /* lock ensuring mutual exclusion during updates */
+    pthread_mutex_t     modLock;
+};
+
+
+/*
+ * Given a file descriptor for an open "optimized" DEX file, map it into
+ * memory and parse the contents.
+ *
+ * On success, returns 0 and sets "*ppDvmDex" to a newly-allocated DvmDex.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex);
+
+/*
+ * Open a partial DEX file.  Only useful as part of the optimization process.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex);
+
+/*
+ * Free a DvmDex structure, along with any associated structures.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex);
+
+
+/*
+ * Change the 1- or 2-byte value at the specified address to a new value.  If
+ * the location already has the new value, do nothing.
+ *
+ * This does not make any synchronization guarantees.  The caller must
+ * ensure exclusivity vs. other callers.
+ *
+ * For the 2-byte call, the pointer should have 16-bit alignment.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal);
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal);
+
+
+/*
+ * Return the requested item if it has been resolved, or NULL if it hasn't.
+ */
+INLINE struct StringObject* dvmDexGetResolvedString(const DvmDex* pDvmDex,
+    u4 stringIdx)
+{
+    assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+    return pDvmDex->pResStrings[stringIdx];
+}
+INLINE struct ClassObject* dvmDexGetResolvedClass(const DvmDex* pDvmDex,
+    u4 classIdx)
+{
+    assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+    return pDvmDex->pResClasses[classIdx];
+}
+INLINE struct Method* dvmDexGetResolvedMethod(const DvmDex* pDvmDex,
+    u4 methodIdx)
+{
+    assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+    return pDvmDex->pResMethods[methodIdx];
+}
+INLINE struct Field* dvmDexGetResolvedField(const DvmDex* pDvmDex,
+    u4 fieldIdx)
+{
+    assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+    return pDvmDex->pResFields[fieldIdx];
+}
+
+/*
+ * Update the resolved item table.  Resolution always produces the same
+ * result, so we're not worried about atomicity here.
+ */
+INLINE void dvmDexSetResolvedString(DvmDex* pDvmDex, u4 stringIdx,
+    struct StringObject* str)
+{
+    assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+    pDvmDex->pResStrings[stringIdx] = str;
+}
+INLINE void dvmDexSetResolvedClass(DvmDex* pDvmDex, u4 classIdx,
+    struct ClassObject* clazz)
+{
+    assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+    pDvmDex->pResClasses[classIdx] = clazz;
+}
+INLINE void dvmDexSetResolvedMethod(DvmDex* pDvmDex, u4 methodIdx,
+    struct Method* method)
+{
+    assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+    pDvmDex->pResMethods[methodIdx] = method;
+}
+INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx,
+    struct Field* field)
+{
+    assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+    pDvmDex->pResFields[fieldIdx] = field;
+}
+
+#endif  // DALVIK_DVMDEX_H_
diff --git a/vm/Exception.cpp b/vm/Exception.cpp
new file mode 100644
index 0000000..36f2d20
--- /dev/null
+++ b/vm/Exception.cpp
@@ -0,0 +1,1454 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Exception handling.
+ */
+#include "Dalvik.h"
+#include "libdex/DexCatch.h"
+
+#include <stdlib.h>
+
+/*
+Notes on Exception Handling
+
+We have one fairly sticky issue to deal with: creating the exception stack
+trace.  The trouble is that we need the current value of the program
+counter for the method now being executed, but that's only held in a local
+variable or hardware register in the main interpreter loop.
+
+The exception mechanism requires that the current stack trace be associated
+with a Throwable at the time the Throwable is constructed.  The construction
+may or may not be associated with a throw.  We have three situations to
+consider:
+
+ (1) A Throwable is created with a "new Throwable" statement in the
+     application code, for immediate or deferred use with a "throw" statement.
+ (2) The VM throws an exception from within the interpreter core, e.g.
+     after an integer divide-by-zero.
+ (3) The VM throws an exception from somewhere deeper down, e.g. while
+     trying to link a class.
+
+We need to have the current value for the PC, which means that for
+situation (3) the interpreter loop must copy it to an externally-accessible
+location before handling any opcode that could cause the VM to throw
+an exception.  We can't store it globally, because the various threads
+would trample each other.  We can't store it in the Thread structure,
+because it'll get overwritten as soon as the Throwable constructor starts
+executing.  It needs to go on the stack, but our stack frames hold the
+caller's *saved* PC, not the current PC.
+
+Situation #1 doesn't require special handling.  Situation #2 could be dealt
+with by passing the PC into the exception creation function.  The trick
+is to solve situation #3 in a way that adds minimal overhead to common
+operations.  Making it more costly to throw an exception is acceptable.
+
+There are a few ways to deal with this:
+
+ (a) Change "savedPc" to "currentPc" in the stack frame.  All of the
+     stack logic gets offset by one frame.  The current PC is written
+     to the current stack frame when necessary.
+ (b) Write the current PC into the current stack frame, but without
+     replacing "savedPc".  The JNI local refs pointer, which is only
+     used for native code, can be overloaded to save space.
+ (c) In dvmThrowException(), push an extra stack frame on, with the
+     current PC in it.  The current PC is written into the Thread struct
+     when necessary, and copied out when the VM throws.
+ (d) Before doing something that might throw an exception, push a
+     temporary frame on with the saved PC in it.
+
+Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
+and interpreted stacks.
+
+Solution (b) retains the simplicity of (a) without rearranging the stack,
+but now in some cases we're storing the PC twice, which feels wrong.
+
+Solution (c) usually works, because we push the saved PC onto the stack
+before the Throwable construction can overwrite the copy in Thread.  One
+way solution (c) could break is:
+ - Interpreter saves the PC
+ - Execute some bytecode, which runs successfully (and alters the saved PC)
+ - Throw an exception before re-saving the PC (i.e in the same opcode)
+This is a risk for anything that could cause <clinit> to execute, e.g.
+executing a static method or accessing a static field.  Attemping to access
+a field that doesn't exist in a class that does exist might cause this.
+It may be possible to simply bracket the dvmCallMethod*() functions to
+save/restore it.
+
+Solution (d) incurs additional overhead, but may have other benefits (e.g.
+it's easy to find the stack frames that should be removed before storage
+in the Throwable).
+
+Current plan is option (b), because it's simple, fast, and doesn't change
+the way the stack works.
+*/
+
+/* fwd */
+static bool initException(Object* exception, const char* msg, Object* cause,
+    Thread* self);
+
+void dvmThrowExceptionFmtV(ClassObject* exceptionClass,
+    const char* fmt, va_list args)
+{
+    char msgBuf[512];
+
+    vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+    dvmThrowChainedException(exceptionClass, msgBuf, NULL);
+}
+
+void dvmThrowChainedException(ClassObject* excepClass, const char* msg,
+    Object* cause)
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+
+    if (excepClass == NULL) {
+        /*
+         * The exception class was passed in as NULL. This might happen
+         * early on in VM initialization. There's nothing better to do
+         * than just log the message as an error and abort.
+         */
+        ALOGE("Fatal error: %s", msg);
+        dvmAbort();
+    }
+
+    /* make sure the exception is initialized */
+    if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
+        ALOGE("ERROR: unable to initialize exception class '%s'",
+            excepClass->descriptor);
+        if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
+            dvmAbort();
+        dvmThrowChainedException(gDvm.exInternalError,
+            "failed to init original exception class", cause);
+        return;
+    }
+
+    exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
+    if (exception == NULL) {
+        /*
+         * We're in a lot of trouble.  We might be in the process of
+         * throwing an out-of-memory exception, in which case the
+         * pre-allocated object will have been thrown when our object alloc
+         * failed.  So long as there's an exception raised, return and
+         * allow the system to try to recover.  If not, something is broken
+         * and we need to bail out.
+         */
+        if (dvmCheckException(self))
+            goto bail;
+        ALOGE("FATAL: unable to allocate exception '%s' '%s'",
+            excepClass->descriptor, msg != NULL ? msg : "(no msg)");
+        dvmAbort();
+    }
+
+    /*
+     * Init the exception.
+     */
+    if (gDvm.optimizing) {
+        /* need the exception object, but can't invoke interpreted code */
+        ALOGV("Skipping init of exception %s '%s'",
+            excepClass->descriptor, msg);
+    } else {
+        assert(excepClass == exception->clazz);
+        if (!initException(exception, msg, cause, self)) {
+            /*
+             * Whoops.  If we can't initialize the exception, we can't use
+             * it.  If there's an exception already set, the constructor
+             * probably threw an OutOfMemoryError.
+             */
+            if (!dvmCheckException(self)) {
+                /*
+                 * We're required to throw something, so we just
+                 * throw the pre-constructed internal error.
+                 */
+                self->exception = gDvm.internalErrorObj;
+            }
+            goto bail;
+        }
+    }
+
+    self->exception = exception;
+
+bail:
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowChainedExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor,
+    Object* cause)
+{
+    char* message = dvmDescriptorToName(messageDescriptor);
+
+    dvmThrowChainedException(exceptionClass, message, cause);
+    free(message);
+}
+
+/*
+ * Find and return an exception constructor method that can take the
+ * indicated parameters, or return NULL if no such constructor exists.
+ */
+static Method* findExceptionInitMethod(ClassObject* excepClass,
+    bool hasMessage, bool hasCause)
+{
+    if (hasMessage) {
+        Method* result;
+
+        if (hasCause) {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+        } else {
+            result = dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/String;)V");
+        }
+
+        if (result != NULL) {
+            return result;
+        }
+
+        if (hasCause) {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>",
+                    "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
+        } else {
+            return dvmFindDirectMethodByDescriptor(
+                    excepClass, "<init>", "(Ljava/lang/Object;)V");
+        }
+    } else if (hasCause) {
+        return dvmFindDirectMethodByDescriptor(
+                excepClass, "<init>", "(Ljava/lang/Throwable;)V");
+    } else {
+        return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+    }
+}
+
+/*
+ * Initialize an exception with an appropriate constructor.
+ *
+ * "exception" is the exception object to initialize.
+ * Either or both of "msg" and "cause" may be null.
+ * "self" is dvmThreadSelf(), passed in so we don't have to look it up again.
+ *
+ * If the process of initializing the exception causes another
+ * exception (e.g., OutOfMemoryError) to be thrown, return an error
+ * and leave self->exception intact.
+ */
+static bool initException(Object* exception, const char* msg, Object* cause,
+    Thread* self)
+{
+    enum {
+        kInitUnknown,
+        kInitNoarg,
+        kInitMsg,
+        kInitMsgThrow,
+        kInitThrow
+    } initKind = kInitUnknown;
+    Method* initMethod = NULL;
+    ClassObject* excepClass = exception->clazz;
+    StringObject* msgStr = NULL;
+    bool result = false;
+    bool needInitCause = false;
+
+    assert(self != NULL);
+    assert(self->exception == NULL);
+
+    /* if we have a message, create a String */
+    if (msg == NULL)
+        msgStr = NULL;
+    else {
+        msgStr = dvmCreateStringFromCstr(msg);
+        if (msgStr == NULL) {
+            ALOGW("Could not allocate message string \"%s\" while "
+                    "throwing internal exception (%s)",
+                    msg, excepClass->descriptor);
+            goto bail;
+        }
+    }
+
+    if (cause != NULL) {
+        if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) {
+            ALOGE("Tried to init exception with cause '%s'",
+                cause->clazz->descriptor);
+            dvmAbort();
+        }
+    }
+
+    /*
+     * The Throwable class has four public constructors:
+     *  (1) Throwable()
+     *  (2) Throwable(String message)
+     *  (3) Throwable(String message, Throwable cause)  (added in 1.4)
+     *  (4) Throwable(Throwable cause)                  (added in 1.4)
+     *
+     * The first two are part of the original design, and most exception
+     * classes should support them.  The third prototype was used by
+     * individual exceptions. e.g. ClassNotFoundException added it in 1.2.
+     * The general "cause" mechanism was added in 1.4.  Some classes,
+     * such as IllegalArgumentException, initially supported the first
+     * two, but added the second two in a later release.
+     *
+     * Exceptions may be picky about how their "cause" field is initialized.
+     * If you call ClassNotFoundException(String), it may choose to
+     * initialize its "cause" field to null.  Doing so prevents future
+     * calls to Throwable.initCause().
+     *
+     * So, if "cause" is not NULL, we need to look for a constructor that
+     * takes a throwable.  If we can't find one, we fall back on calling
+     * #1/#2 and making a separate call to initCause().  Passing a null ref
+     * for "message" into Throwable(String, Throwable) is allowed, but we
+     * prefer to use the Throwable-only version because it has different
+     * behavior.
+     *
+     * java.lang.TypeNotPresentException is a strange case -- it has #3 but
+     * not #2.  (Some might argue that the constructor is actually not #3,
+     * because it doesn't take the message string as an argument, but it
+     * has the same effect and we can work with it here.)
+     *
+     * java.lang.AssertionError is also a strange case -- it has a
+     * constructor that takes an Object, but not one that takes a String.
+     * There may be other cases like this, as well, so we generally look
+     * for an Object-taking constructor if we can't find one that takes
+     * a String.
+     */
+    if (cause == NULL) {
+        if (msgStr == NULL) {
+            initMethod = findExceptionInitMethod(excepClass, false, false);
+            initKind = kInitNoarg;
+        } else {
+            initMethod = findExceptionInitMethod(excepClass, true, false);
+            if (initMethod != NULL) {
+                initKind = kInitMsg;
+            } else {
+                /* no #2, try #3 */
+                initMethod = findExceptionInitMethod(excepClass, true, true);
+                if (initMethod != NULL) {
+                    initKind = kInitMsgThrow;
+                }
+            }
+        }
+    } else {
+        if (msgStr == NULL) {
+            initMethod = findExceptionInitMethod(excepClass, false, true);
+            if (initMethod != NULL) {
+                initKind = kInitThrow;
+            } else {
+                initMethod = findExceptionInitMethod(excepClass, false, false);
+                initKind = kInitNoarg;
+                needInitCause = true;
+            }
+        } else {
+            initMethod = findExceptionInitMethod(excepClass, true, true);
+            if (initMethod != NULL) {
+                initKind = kInitMsgThrow;
+            } else {
+                initMethod = findExceptionInitMethod(excepClass, true, false);
+                initKind = kInitMsg;
+                needInitCause = true;
+            }
+        }
+    }
+
+    if (initMethod == NULL) {
+        /*
+         * We can't find the desired constructor.  This can happen if a
+         * subclass of java/lang/Throwable doesn't define an expected
+         * constructor, e.g. it doesn't provide one that takes a string
+         * when a message has been provided.
+         */
+        ALOGW("WARNING: exception class '%s' missing constructor "
+            "(msg='%s' kind=%d)",
+            excepClass->descriptor, msg, initKind);
+        assert(strcmp(excepClass->descriptor,
+                      "Ljava/lang/RuntimeException;") != 0);
+        dvmThrowChainedException(gDvm.exRuntimeException,
+            "re-throw on exception class missing constructor", NULL);
+        goto bail;
+    }
+
+    /*
+     * Call the constructor with the appropriate arguments.
+     */
+    JValue unused;
+    switch (initKind) {
+    case kInitNoarg:
+        LOGVV("+++ exc noarg (ic=%d)", needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused);
+        break;
+    case kInitMsg:
+        LOGVV("+++ exc msg (ic=%d)", needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, msgStr);
+        break;
+    case kInitThrow:
+        LOGVV("+++ exc throw");
+        assert(!needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, cause);
+        break;
+    case kInitMsgThrow:
+        LOGVV("+++ exc msg+throw");
+        assert(!needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
+        break;
+    default:
+        assert(false);
+        goto bail;
+    }
+
+    /*
+     * It's possible the constructor has thrown an exception.  If so, we
+     * return an error and let our caller deal with it.
+     */
+    if (self->exception != NULL) {
+        ALOGW("Exception thrown (%s) while throwing internal exception (%s)",
+            self->exception->clazz->descriptor, exception->clazz->descriptor);
+        goto bail;
+    }
+
+    /*
+     * If this exception was caused by another exception, and we weren't
+     * able to find a cause-setting constructor, set the "cause" field
+     * with an explicit call.
+     */
+    if (needInitCause) {
+        Method* initCause;
+        initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause",
+            "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
+        if (initCause != NULL) {
+            dvmCallMethod(self, initCause, exception, &unused, cause);
+            if (self->exception != NULL) {
+                /* initCause() threw an exception; return an error and
+                 * let the caller deal with it.
+                 */
+                ALOGW("Exception thrown (%s) during initCause() "
+                        "of internal exception (%s)",
+                        self->exception->clazz->descriptor,
+                        exception->clazz->descriptor);
+                goto bail;
+            }
+        } else {
+            ALOGW("WARNING: couldn't find initCause in '%s'",
+                excepClass->descriptor);
+        }
+    }
+
+
+    result = true;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // NULL is ok
+    return result;
+}
+
+
+/*
+ * Clear the pending exception. This is used by the optimization and
+ * verification code, which mostly happens during runs of dexopt.
+ *
+ * This can also be called when the VM is in a "normal" state, e.g. when
+ * verifying classes that couldn't be verified at optimization time.
+ */
+void dvmClearOptException(Thread* self)
+{
+    self->exception = NULL;
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception)
+{
+    if (dvmInstanceof(exception->clazz, gDvm.exError) ||
+        dvmInstanceof(exception->clazz, gDvm.exRuntimeException))
+    {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+/*
+ * Wrap the now-pending exception in a different exception.  This is useful
+ * for reflection stuff that wants to hand a checked exception back from a
+ * method that doesn't declare it.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr)
+{
+    Thread* self = dvmThreadSelf();
+    Object* origExcep;
+    ClassObject* iteClass;
+
+    origExcep = dvmGetException(self);
+    dvmAddTrackedAlloc(origExcep, self);    // don't let the GC free it
+
+    dvmClearException(self);                // clear before class lookup
+    iteClass = dvmFindSystemClass(newExcepStr);
+    if (iteClass != NULL) {
+        Object* iteExcep;
+        Method* initMethod;
+
+        iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT);
+        if (iteExcep != NULL) {
+            initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>",
+                            "(Ljava/lang/Throwable;)V");
+            if (initMethod != NULL) {
+                JValue unused;
+                dvmCallMethod(self, initMethod, iteExcep, &unused,
+                    origExcep);
+
+                /* if <init> succeeded, replace the old exception */
+                if (!dvmCheckException(self))
+                    dvmSetException(self, iteExcep);
+            }
+            dvmReleaseTrackedAlloc(iteExcep, NULL);
+
+            /* if initMethod doesn't exist, or failed... */
+            if (!dvmCheckException(self))
+                dvmSetException(self, origExcep);
+        } else {
+            /* leave OutOfMemoryError pending */
+        }
+    } else {
+        /* leave ClassNotFoundException pending */
+    }
+
+    assert(dvmCheckException(self));
+    dvmReleaseTrackedAlloc(origExcep, self);
+}
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * The Throwable class initializes the "cause" field to "this" to
+ * differentiate between being initialized to null and never being
+ * initialized.  We check for that here and convert it to NULL.
+ */
+Object* dvmGetExceptionCause(const Object* exception)
+{
+    if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) {
+        ALOGE("Tried to get cause from object of type '%s'",
+            exception->clazz->descriptor);
+        dvmAbort();
+    }
+    Object* cause =
+        dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
+    if (cause == exception)
+        return NULL;
+    else
+        return cause;
+}
+
+/*
+ * Print the stack trace of the current exception on stderr.  This is called
+ * from the JNI ExceptionDescribe call.
+ *
+ * For consistency we just invoke the Throwable printStackTrace method,
+ * which might be overridden in the exception object.
+ *
+ * Exceptions thrown during the course of printing the stack trace are
+ * ignored.
+ */
+void dvmPrintExceptionStackTrace()
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+    Method* printMethod;
+
+    exception = self->exception;
+    if (exception == NULL)
+        return;
+
+    dvmAddTrackedAlloc(exception, self);
+    self->exception = NULL;
+    printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+                    "printStackTrace", "()V");
+    if (printMethod != NULL) {
+        JValue unused;
+        dvmCallMethod(self, printMethod, exception, &unused);
+    } else {
+        ALOGW("WARNING: could not find printStackTrace in %s",
+            exception->clazz->descriptor);
+    }
+
+    if (self->exception != NULL) {
+        ALOGW("NOTE: exception thrown while printing stack trace: %s",
+            self->exception->clazz->descriptor);
+    }
+
+    self->exception = exception;
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+/*
+ * Search the method's list of exceptions for a match.
+ *
+ * Returns the offset of the catch block on success, or -1 on failure.
+ */
+static int findCatchInMethod(Thread* self, const Method* method, int relPc,
+    ClassObject* excepClass)
+{
+    /*
+     * Need to clear the exception before entry.  Otherwise, dvmResolveClass
+     * might think somebody threw an exception while it was loading a class.
+     */
+    assert(!dvmCheckException(self));
+    assert(!dvmIsNativeMethod(method));
+
+    LOGVV("findCatchInMethod %s.%s excep=%s depth=%d",
+        method->clazz->descriptor, method->name, excepClass->descriptor,
+        dvmComputeExactFrameDepth(self->interpSave.curFrame));
+
+    DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexCode* pCode = dvmGetMethodCode(method);
+    DexCatchIterator iterator;
+
+    if (dexFindCatchHandler(&iterator, pCode, relPc)) {
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            if (handler->typeIdx == kDexNoIndex) {
+                /* catch-all */
+                ALOGV("Match on catch-all block at 0x%02x in %s.%s for %s",
+                        relPc, method->clazz->descriptor,
+                        method->name, excepClass->descriptor);
+                return handler->address;
+            }
+
+            ClassObject* throwable =
+                dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
+            if (throwable == NULL) {
+                /*
+                 * TODO: this behaves badly if we run off the stack
+                 * while trying to throw an exception.  The problem is
+                 * that, if we're in a class loaded by a class loader,
+                 * the call to dvmResolveClass has to ask the class
+                 * loader for help resolving any previously-unresolved
+                 * classes.  If this particular class loader hasn't
+                 * resolved StackOverflowError, it will call into
+                 * interpreted code, and blow up.
+                 *
+                 * We currently replace the previous exception with
+                 * the StackOverflowError, which means they won't be
+                 * catching it *unless* they explicitly catch
+                 * StackOverflowError, in which case we'll be unable
+                 * to resolve the class referred to by the "catch"
+                 * block.
+                 *
+                 * We end up getting a huge pile of warnings if we do
+                 * a simple synthetic test, because this method gets
+                 * called on every stack frame up the tree, and it
+                 * fails every time.
+                 *
+                 * This eventually bails out, effectively becoming an
+                 * uncatchable exception, so other than the flurry of
+                 * warnings it's not really a problem.  Still, we could
+                 * probably handle this better.
+                 */
+                throwable = dvmResolveClass(method->clazz, handler->typeIdx,
+                    true);
+                if (throwable == NULL) {
+                    /*
+                     * We couldn't find the exception they wanted in
+                     * our class files (or, perhaps, the stack blew up
+                     * while we were querying a class loader). Cough
+                     * up a warning, then move on to the next entry.
+                     * Keep the exception status clear.
+                     */
+                    ALOGW("Could not resolve class ref'ed in exception "
+                            "catch list (class index %d, exception %s)",
+                            handler->typeIdx,
+                            (self->exception != NULL) ?
+                            self->exception->clazz->descriptor : "(none)");
+                    dvmClearException(self);
+                    continue;
+                }
+            }
+
+            //ALOGD("ADDR MATCH, check %s instanceof %s",
+            //    excepClass->descriptor, pEntry->excepClass->descriptor);
+
+            if (dvmInstanceof(excepClass, throwable)) {
+                ALOGV("Match on catch block at 0x%02x in %s.%s for %s",
+                        relPc, method->clazz->descriptor,
+                        method->name, excepClass->descriptor);
+                return handler->address;
+            }
+        }
+    }
+
+    ALOGV("No matching catch block at 0x%02x in %s for %s",
+        relPc, method->name, excepClass->descriptor);
+    return -1;
+}
+
+/*
+ * Find a matching "catch" block.  "pc" is the relative PC within the
+ * current method, indicating the offset from the start in 16-bit units.
+ *
+ * Returns the offset to the catch block, or -1 if we run up against a
+ * break frame without finding anything.
+ *
+ * The class resolution stuff we have to do while evaluating the "catch"
+ * blocks could cause an exception.  The caller should clear the exception
+ * before calling here and restore it after.
+ *
+ * Sets *newFrame to the frame pointer of the frame with the catch block.
+ * If "scanOnly" is false, self->interpSave.curFrame is also set to this value.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    bool scanOnly, void** newFrame)
+{
+    u4* fp = self->interpSave.curFrame;
+    int catchAddr = -1;
+
+    assert(!dvmCheckException(self));
+
+    while (true) {
+        StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        catchAddr = findCatchInMethod(self, saveArea->method, relPc,
+                        exception->clazz);
+        if (catchAddr >= 0)
+            break;
+
+        /*
+         * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
+         * them as we unroll.  Dalvik uses what amount to generated
+         * "finally" blocks to take care of this for us.
+         */
+
+        /* output method profiling info */
+        if (!scanOnly) {
+            TRACE_METHOD_UNROLL(self, saveArea->method);
+        }
+
+        /*
+         * Move up one frame.  If the next thing up is a break frame,
+         * break out now so we're left unrolled to the last method frame.
+         * We need to point there so we can roll up the JNI local refs
+         * if this was a native method.
+         */
+        assert(saveArea->prevFrame != NULL);
+        if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) {
+            if (!scanOnly)
+                break;      // bail with catchAddr == -1
+
+            /*
+             * We're scanning for the debugger.  It needs to know if this
+             * exception is going to be caught or not, and we need to figure
+             * out if it will be caught *ever* not just between the current
+             * position and the next break frame.  We can't tell what native
+             * code is going to do, so we assume it never catches exceptions.
+             *
+             * Start by finding an interpreted code frame.
+             */
+            fp = saveArea->prevFrame;           // this is the break frame
+            saveArea = SAVEAREA_FROM_FP(fp);
+            fp = saveArea->prevFrame;           // this may be a good one
+            while (fp != NULL) {
+                if (!dvmIsBreakFrame((u4*)fp)) {
+                    saveArea = SAVEAREA_FROM_FP(fp);
+                    if (!dvmIsNativeMethod(saveArea->method))
+                        break;
+                }
+
+                fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+            }
+            if (fp == NULL)
+                break;      // bail with catchAddr == -1
+
+            /*
+             * Now fp points to the "good" frame.  When the interp code
+             * invoked the native code, it saved a copy of its current PC
+             * into xtra.currentPc.  Pull it out of there.
+             */
+            relPc =
+                saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
+        } else {
+            fp = saveArea->prevFrame;
+
+            /* savedPc in was-current frame goes with method in now-current */
+            relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
+        }
+    }
+
+    if (!scanOnly)
+        self->interpSave.curFrame = fp;
+
+    /*
+     * The class resolution in findCatchInMethod() could cause an exception.
+     * Clear it to be safe.
+     */
+    self->exception = NULL;
+
+    *newFrame = fp;
+    return catchAddr;
+}
+
+/*
+ * We have to carry the exception's stack trace around, but in many cases
+ * it will never be examined.  It makes sense to keep it in a compact,
+ * VM-specific object, rather than an array of Objects with strings.
+ *
+ * Pass in the thread whose stack we're interested in.  If "thread" is
+ * not self, the thread must be suspended.  This implies that the thread
+ * list lock is held, which means we can't allocate objects or we risk
+ * jamming the GC.  So, we allow this function to return different formats.
+ * (This shouldn't be called directly -- see the inline functions in the
+ * header file.)
+ *
+ * If "wantObject" is true, this returns a newly-allocated Object, which is
+ * presently an array of integers, but could become something else in the
+ * future.  If "wantObject" is false, return plain malloc data.
+ *
+ * NOTE: if we support class unloading, we will need to scan the class
+ * object references out of these arrays.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount)
+{
+    ArrayObject* stackData = NULL;
+    int* simpleData = NULL;
+    void* fp;
+    void* startFp;
+    size_t stackDepth;
+    int* intPtr;
+
+    if (pCount != NULL)
+        *pCount = 0;
+    fp = thread->interpSave.curFrame;
+
+    assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+    /*
+     * We're looking at a stack frame for code running below a Throwable
+     * constructor.  We want to remove the Throwable methods and the
+     * superclass initializations so the user doesn't see them when they
+     * read the stack dump.
+     *
+     * TODO: this just scrapes off the top layers of Throwable.  Might not do
+     * the right thing if we create an exception object or cause a VM
+     * exception while in a Throwable method.
+     */
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (dvmIsBreakFrame((u4*)fp))
+            break;
+        if (!dvmInstanceof(method->clazz, gDvm.exThrowable))
+            break;
+        //ALOGD("EXCEP: ignoring %s.%s",
+        //         method->clazz->descriptor, method->name);
+        fp = saveArea->prevFrame;
+    }
+    startFp = fp;
+
+    /*
+     * Compute the stack depth.
+     */
+    stackDepth = 0;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (!dvmIsBreakFrame((u4*)fp))
+            stackDepth++;
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    //ALOGD("EXCEP: stack depth is %d", stackDepth);
+
+    if (!stackDepth)
+        goto bail;
+
+    /*
+     * We need to store a pointer to the Method and the program counter.
+     * We have 4-byte pointers, so we use '[I'.
+     */
+    if (wantObject) {
+        assert(sizeof(Method*) == 4);
+        stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
+        if (stackData == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            goto bail;
+        }
+        intPtr = (int*)(void*)stackData->contents;
+    } else {
+        /* array of ints; first entry is stack depth */
+        assert(sizeof(Method*) == sizeof(int));
+        simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
+        if (simpleData == NULL)
+            goto bail;
+
+        assert(pCount != NULL);
+        intPtr = simpleData;
+    }
+    if (pCount != NULL)
+        *pCount = stackDepth;
+
+    fp = startFp;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)fp)) {
+            //ALOGD("EXCEP keeping %s.%s", method->clazz->descriptor,
+            //         method->name);
+
+            *intPtr++ = (int) method;
+            if (dvmIsNativeMethod(method)) {
+                *intPtr++ = 0;      /* no saved PC for native methods */
+            } else {
+                assert(saveArea->xtra.currentPc >= method->insns &&
+                        saveArea->xtra.currentPc <
+                        method->insns + dvmGetMethodInsnsSize(method));
+                *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
+            }
+
+            stackDepth--;       // for verification
+        }
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    assert(stackDepth == 0);
+
+bail:
+    if (wantObject) {
+        dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
+        return stackData;
+    } else {
+        return simpleData;
+    }
+}
+
+
+/*
+ * Given an Object previously created by dvmFillInStackTrace(), use the
+ * contents of the saved stack trace to generate an array of
+ * java/lang/StackTraceElement objects.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTrace(const Object* ostackData)
+{
+    const ArrayObject* stackData = (const ArrayObject*) ostackData;
+    size_t stackSize = stackData->length / 2;
+    const int* intVals = (const int*)(void*)stackData->contents;
+    return dvmGetStackTraceRaw(intVals, stackSize);
+}
+
+/*
+ * Generate an array of StackTraceElement objects from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, size_t stackDepth)
+{
+    /* allocate a StackTraceElement array */
+    ClassObject* klass = gDvm.classJavaLangStackTraceElementArray;
+    ArrayObject* array = dvmAllocArrayByClass(klass, stackDepth, ALLOC_DEFAULT);
+    if (array != NULL){
+      dvmFillStackTraceElements(intVals, stackDepth, array);
+      dvmReleaseTrackedAlloc((Object*) array, NULL);
+    }
+    return array;
+}
+
+/*
+ * Fills the StackTraceElement array elements from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ */
+void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray)
+{
+    unsigned int i;
+
+    /* init this if we haven't yet */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
+        dvmInitClass(gDvm.classJavaLangStackTraceElement);
+
+    /*
+     * Allocate and initialize a StackTraceElement for each stack frame.
+     * We use the standard constructor to configure the object.
+     */
+    for (i = 0; i < stackDepth; i++) {
+        Object* ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
+        if (ste == NULL) {
+            return;
+        }
+
+        Method* meth = (Method*) *intVals++;
+        int pc = *intVals++;
+
+        int lineNumber;
+        if (pc == -1)      // broken top frame?
+            lineNumber = 0;
+        else
+            lineNumber = dvmLineNumFromPC(meth, pc);
+
+        std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+        StringObject* className = dvmCreateStringFromCstr(dotName);
+
+        StringObject* methodName = dvmCreateStringFromCstr(meth->name);
+
+        const char* sourceFile = dvmGetMethodSourceFile(meth);
+        StringObject* fileName = (sourceFile != NULL) ? dvmCreateStringFromCstr(sourceFile) : NULL;
+
+        /*
+         * Invoke:
+         *  public StackTraceElement(String declaringClass, String methodName,
+         *      String fileName, int lineNumber)
+         * (where lineNumber==-2 means "native")
+         */
+        JValue unused;
+        dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,
+            ste, &unused, className, methodName, fileName, lineNumber);
+
+        dvmReleaseTrackedAlloc(ste, NULL);
+        dvmReleaseTrackedAlloc((Object*) className, NULL);
+        dvmReleaseTrackedAlloc((Object*) methodName, NULL);
+        dvmReleaseTrackedAlloc((Object*) fileName, NULL);
+
+        if (dvmCheckException(dvmThreadSelf())) {
+            return;
+        }
+
+        dvmSetObjectArrayElement(steArray, i, ste);
+    }
+}
+
+/*
+ * Dump the contents of a raw stack trace to the log.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth) {
+    /*
+     * Run through the array of stack frame data.
+     */
+    for (int i = 0; i < stackDepth; i++) {
+        Method* meth = (Method*) *intVals++;
+        int pc = *intVals++;
+
+        std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+        if (dvmIsNativeMethod(meth)) {
+            ALOGI("\tat %s.%s(Native Method)", dotName.c_str(), meth->name);
+        } else {
+            ALOGI("\tat %s.%s(%s:%d)",
+                dotName.c_str(), meth->name, dvmGetMethodSourceFile(meth),
+                dvmLineNumFromPC(meth, pc));
+        }
+    }
+}
+
+/*
+ * Get the message string.  We'd like to just grab the field out of
+ * Throwable, but the getMessage() function can be overridden by the
+ * sub-class.
+ *
+ * Returns the message string object, or NULL if it wasn't set or
+ * we encountered a failure trying to retrieve it.  The string will
+ * be added to the tracked references table.
+ */
+static StringObject* getExceptionMessage(Object* exception)
+{
+    Thread* self = dvmThreadSelf();
+    Method* getMessageMethod;
+    StringObject* messageStr = NULL;
+    Object* pendingException;
+
+    /*
+     * If an exception is pending, clear it while we work and restore
+     * it when we're done.
+     */
+    pendingException = dvmGetException(self);
+    if (pendingException != NULL) {
+        dvmAddTrackedAlloc(pendingException, self);
+        dvmClearException(self);
+    }
+
+    getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+            "getMessage", "()Ljava/lang/String;");
+    if (getMessageMethod != NULL) {
+        /* could be in NATIVE mode from CheckJNI, so switch state */
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+        JValue result;
+
+        dvmCallMethod(self, getMessageMethod, exception, &result);
+        messageStr = (StringObject*) result.l;
+        if (messageStr != NULL)
+            dvmAddTrackedAlloc((Object*) messageStr, self);
+
+        dvmChangeStatus(self, oldStatus);
+    } else {
+        ALOGW("WARNING: could not find getMessage in %s",
+            exception->clazz->descriptor);
+    }
+
+    if (dvmGetException(self) != NULL) {
+        ALOGW("NOTE: exception thrown while retrieving exception message: %s",
+            dvmGetException(self)->clazz->descriptor);
+        /* will be overwritten below */
+    }
+
+    dvmSetException(self, pendingException);
+    if (pendingException != NULL) {
+        dvmReleaseTrackedAlloc(pendingException, self);
+    }
+    return messageStr;
+}
+
+/*
+ * Print the direct stack trace of the given exception to the log.
+ */
+static void logStackTraceOf(Object* exception) {
+    std::string className(dvmHumanReadableDescriptor(exception->clazz->descriptor));
+    StringObject* messageStr = getExceptionMessage(exception);
+    if (messageStr != NULL) {
+        char* cp = dvmCreateCstrFromString(messageStr);
+        dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf());
+        messageStr = NULL;
+
+        ALOGI("%s: %s", className.c_str(), cp);
+        free(cp);
+    } else {
+        ALOGI("%s:", className.c_str());
+    }
+
+    /*
+     * This relies on the stackState field, which contains the "raw"
+     * form of the stack.  The Throwable class may clear this field
+     * after it generates the "cooked" form, in which case we'll have
+     * nothing to show.
+     */
+    const ArrayObject* stackData = (const ArrayObject*) dvmGetFieldObject(exception,
+                    gDvm.offJavaLangThrowable_stackState);
+    if (stackData == NULL) {
+        ALOGI("  (raw stack trace not found)");
+        return;
+    }
+
+    int stackSize = stackData->length / 2;
+    const int* intVals = (const int*)(void*)stackData->contents;
+
+    dvmLogRawStackTrace(intVals, stackSize);
+}
+
+/*
+ * Print the stack trace of the current thread's exception, as well as
+ * the stack traces of any chained exceptions, to the log. We extract
+ * the stored stack trace and process it internally instead of calling
+ * interpreted code.
+ */
+void dvmLogExceptionStackTrace()
+{
+    Object* exception = dvmThreadSelf()->exception;
+    Object* cause;
+
+    if (exception == NULL) {
+        ALOGW("tried to log a null exception?");
+        return;
+    }
+
+    for (;;) {
+        logStackTraceOf(exception);
+        cause = dvmGetExceptionCause(exception);
+        if (cause == NULL) {
+            break;
+        }
+        ALOGI("Caused by:");
+        exception = cause;
+    }
+}
+
+/*
+ * Helper for a few of the throw functions defined below. This throws
+ * the indicated exception, with a message based on a format in which
+ * "%s" is used exactly twice, first for a received class and second
+ * for the expected class.
+ */
+static void throwTypeError(ClassObject* exceptionClass, const char* fmt,
+    ClassObject* actual, ClassObject* desired)
+{
+    std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+    std::string desiredClassName(dvmHumanReadableDescriptor(desired->descriptor));
+    dvmThrowExceptionFmt(exceptionClass, fmt, actualClassName.c_str(), desiredClassName.c_str());
+}
+
+void dvmThrowAbstractMethodError(const char* msg) {
+    dvmThrowException(gDvm.exAbstractMethodError, msg);
+}
+
+void dvmThrowArithmeticException(const char* msg) {
+    dvmThrowException(gDvm.exArithmeticException, msg);
+}
+
+void dvmThrowArrayIndexOutOfBoundsException(int length, int index)
+{
+    dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+        "length=%d; index=%d", length, index);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType,
+        ClassObject* arrayType)
+{
+    throwTypeError(gDvm.exArrayStoreException,
+        "%s cannot be stored in an array of type %s",
+        objectType, arrayType);
+}
+
+void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label) {
+    std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+    dvmThrowExceptionFmt(gDvm.exArrayStoreException, "%s of type %s is not an array",
+            label, actualClassName.c_str());
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination)
+{
+    throwTypeError(gDvm.exArrayStoreException,
+        "Incompatible types: src=%s, dst=%s",
+        source, destination);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType,
+        ClassObject* arrayType)
+{
+    std::string objectClassName(dvmHumanReadableDescriptor(objectType->descriptor));
+    std::string arrayClassName(dvmHumanReadableDescriptor(arrayType->descriptor));
+    dvmThrowExceptionFmt(gDvm.exArrayStoreException,
+            "source[%d] of type %s cannot be stored in destination array of type %s",
+            index, objectClassName.c_str(), arrayClassName.c_str());
+}
+
+void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired)
+{
+    throwTypeError(gDvm.exClassCastException,
+        "%s cannot be cast to %s", actual, desired);
+}
+
+void dvmThrowClassCircularityError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exClassCircularityError,
+            descriptor);
+}
+
+void dvmThrowClassFormatError(const char* msg) {
+    dvmThrowException(gDvm.exClassFormatError, msg);
+}
+
+void dvmThrowClassNotFoundException(const char* name) {
+    dvmThrowChainedClassNotFoundException(name, NULL);
+}
+
+void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) {
+    /*
+     * Note: This exception is thrown in response to a request coming
+     * from client code for the name as given, so it is preferable to
+     * make the exception message be that string, per se, instead of
+     * trying to prettify it.
+     */
+    dvmThrowChainedException(gDvm.exClassNotFoundException, name, cause);
+}
+
+void dvmThrowExceptionInInitializerError()
+{
+    /*
+     * TODO: Should this just use dvmWrapException()?
+     */
+
+    if (gDvm.exExceptionInInitializerError == NULL || gDvm.exError == NULL) {
+        /*
+         * ExceptionInInitializerError isn't itself initialized. This
+         * can happen very early during VM startup if there is a
+         * problem with one of the corest-of-the-core classes, and it
+         * can possibly happen during a dexopt run. Rather than do
+         * anything fancier, we just abort here with a blatant
+         * message.
+         */
+        ALOGE("Fatal error during early class initialization:");
+        dvmLogExceptionStackTrace();
+        dvmAbort();
+    }
+
+    Thread* self = dvmThreadSelf();
+    Object* exception = dvmGetException(self);
+
+    // We only wrap non-Error exceptions; an Error can just be used as-is.
+    if (dvmInstanceof(exception->clazz, gDvm.exError)) {
+        return;
+    }
+
+    dvmAddTrackedAlloc(exception, self);
+    dvmClearException(self);
+
+    dvmThrowChainedException(gDvm.exExceptionInInitializerError,
+            NULL, exception);
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowFileNotFoundException(const char* msg) {
+    dvmThrowException(gDvm.exFileNotFoundException, msg);
+}
+
+void dvmThrowIOException(const char* msg) {
+    dvmThrowException(gDvm.exIOException, msg);
+}
+
+void dvmThrowIllegalAccessException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalAccessException, msg);
+}
+
+void dvmThrowIllegalAccessError(const char* msg) {
+    dvmThrowException(gDvm.exIllegalAccessError, msg);
+}
+
+void dvmThrowIllegalArgumentException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalArgumentException, msg);
+}
+
+void dvmThrowIllegalMonitorStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalMonitorStateException, msg);
+}
+
+void dvmThrowIllegalStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalStateException, msg);
+}
+
+void dvmThrowIllegalThreadStateException(const char* msg) {
+    dvmThrowException(gDvm.exIllegalThreadStateException, msg);
+}
+
+void dvmThrowIncompatibleClassChangeError(const char* msg) {
+    dvmThrowException(gDvm.exIncompatibleClassChangeError, msg);
+}
+
+void dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+        const char* descriptor)
+{
+    dvmThrowExceptionWithClassMessage(
+            gDvm.exIncompatibleClassChangeError, descriptor);
+}
+
+void dvmThrowInstantiationException(ClassObject* clazz, const char* extraDetail) {
+    std::string className(dvmHumanReadableDescriptor(clazz->descriptor));
+    dvmThrowExceptionFmt(gDvm.exInstantiationException,
+            "can't instantiate class %s%s%s", className.c_str(),
+            (extraDetail == NULL) ? "" : "; ",
+            (extraDetail == NULL) ? "" : extraDetail);
+}
+
+void dvmThrowInternalError(const char* msg) {
+    dvmThrowException(gDvm.exInternalError, msg);
+}
+
+void dvmThrowInterruptedException(const char* msg) {
+    dvmThrowException(gDvm.exInterruptedException, msg);
+}
+
+void dvmThrowLinkageError(const char* msg) {
+    dvmThrowException(gDvm.exLinkageError, msg);
+}
+
+void dvmThrowNegativeArraySizeException(s4 size) {
+    dvmThrowExceptionFmt(gDvm.exNegativeArraySizeException, "%d", size);
+}
+
+void dvmThrowNoClassDefFoundError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exNoClassDefFoundError,
+            descriptor);
+}
+
+void dvmThrowChainedNoClassDefFoundError(const char* descriptor,
+        Object* cause) {
+    dvmThrowChainedExceptionWithClassMessage(
+            gDvm.exNoClassDefFoundError, descriptor, cause);
+}
+
+void dvmThrowNoSuchFieldError(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchFieldError, msg);
+}
+
+void dvmThrowNoSuchFieldException(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchFieldException, msg);
+}
+
+void dvmThrowNoSuchMethodError(const char* msg) {
+    dvmThrowException(gDvm.exNoSuchMethodError, msg);
+}
+
+void dvmThrowNullPointerException(const char* msg) {
+    dvmThrowException(gDvm.exNullPointerException, msg);
+}
+
+void dvmThrowOutOfMemoryError(const char* msg) {
+    dvmThrowException(gDvm.exOutOfMemoryError, msg);
+}
+
+void dvmThrowRuntimeException(const char* msg) {
+    dvmThrowException(gDvm.exRuntimeException, msg);
+}
+
+void dvmThrowStaleDexCacheError(const char* msg) {
+    dvmThrowException(gDvm.exStaleDexCacheError, msg);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength,
+        jsize requestIndex) {
+    dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+            "length=%d; index=%d", stringLength, requestIndex);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength,
+        jsize requestStart, jsize requestLength) {
+    dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+            "length=%d; regionStart=%d; regionLength=%d",
+            stringLength, requestStart, requestLength);
+}
+
+void dvmThrowTypeNotPresentException(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exTypeNotPresentException,
+            descriptor);
+}
+
+void dvmThrowUnsatisfiedLinkError(const char* msg) {
+    dvmThrowException(gDvm.exUnsatisfiedLinkError, msg);
+}
+
+void dvmThrowUnsatisfiedLinkError(const char* msg, const Method* method) {
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+    char* className = dvmDescriptorToDot(method->clazz->descriptor);
+    dvmThrowExceptionFmt(gDvm.exUnsatisfiedLinkError, "%s: %s.%s:%s",
+        msg, className, method->name, desc);
+    free(className);
+    free(desc);
+}
+
+void dvmThrowUnsupportedOperationException(const char* msg) {
+    dvmThrowException(gDvm.exUnsupportedOperationException, msg);
+}
+
+void dvmThrowVerifyError(const char* descriptor) {
+    dvmThrowExceptionWithClassMessage(gDvm.exVerifyError, descriptor);
+}
+
+void dvmThrowVirtualMachineError(const char* msg) {
+    dvmThrowException(gDvm.exVirtualMachineError, msg);
+}
diff --git a/vm/Exception.h b/vm/Exception.h
new file mode 100644
index 0000000..055ed2b
--- /dev/null
+++ b/vm/Exception.h
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Exception handling.
+ */
+#ifndef DALVIK_EXCEPTION_H_
+#define DALVIK_EXCEPTION_H_
+
+/*
+ * Create a Throwable and throw an exception in the current thread (where
+ * "throwing" just means "set the thread's exception pointer").
+ *
+ * "msg" and/or "cause" may be NULL.
+ *
+ * If we have a bad exception hierarchy -- something in Throwable.<init>
+ * is missing -- then every attempt to throw an exception will result
+ * in another exception.  Exceptions are generally allowed to "chain"
+ * to other exceptions, so it's hard to auto-detect this problem.  It can
+ * only happen if the system classes are broken, so it's probably not
+ * worth spending cycles to detect it.
+ *
+ * We do have one case to worry about: if the classpath is completely
+ * wrong, we'll go into a death spin during startup because we can't find
+ * the initial class and then we can't find NoClassDefFoundError.  We have
+ * to handle this case.
+ */
+void dvmThrowChainedException(ClassObject* exceptionClass,
+    const char* msg, Object* cause);
+INLINE void dvmThrowException(ClassObject* exceptionClass,
+    const char* msg)
+{
+    dvmThrowChainedException(exceptionClass, msg, NULL);
+}
+
+/*
+ * Like dvmThrowException, but takes printf-style args for the message.
+ */
+void dvmThrowExceptionFmtV(ClassObject* exceptionClass,
+    const char* fmt, va_list args);
+void dvmThrowExceptionFmt(ClassObject* exceptionClass,
+    const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+INLINE void dvmThrowExceptionFmt(ClassObject* exceptionClass,
+    const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    dvmThrowExceptionFmtV(exceptionClass, fmt, args);
+    va_end(args);
+}
+
+/*
+ * Like dvmThrowChainedException, but take a class object
+ * instead of a name and turn the given message into the
+ * human-readable form for a descriptor.
+ */
+void dvmThrowChainedExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor,
+    Object* cause);
+
+/*
+ * Like dvmThrowException, but take a class object instead of a name
+ * and turn the given message into the human-readable form for a descriptor.
+ */
+INLINE void dvmThrowExceptionWithClassMessage(
+    ClassObject* exceptionClass, const char* messageDescriptor)
+{
+    dvmThrowChainedExceptionWithClassMessage(exceptionClass,
+            messageDescriptor, NULL);
+}
+
+/*
+ * Return the exception being thrown in the current thread, or NULL if
+ * no exception is pending.
+ */
+INLINE Object* dvmGetException(Thread* self) {
+    return self->exception;
+}
+
+/*
+ * Set the exception being thrown in the current thread.
+ */
+INLINE void dvmSetException(Thread* self, Object* exception)
+{
+    assert(exception != NULL);
+    self->exception = exception;
+}
+
+/*
+ * Clear the pending exception.
+ *
+ * (We use this rather than "set(null)" because we may need to have special
+ * fixups here for StackOverflowError stuff.  Calling "clear" in the code
+ * makes it obvious.)
+ */
+INLINE void dvmClearException(Thread* self) {
+    self->exception = NULL;
+}
+
+/*
+ * Clear the pending exception.  Used by the optimization and verification
+ * code, which has to run with "initializing" set to avoid going into a
+ * death-spin if the "class not found" exception can't be found.
+ */
+void dvmClearOptException(Thread* self);
+
+/*
+ * Returns "true" if an exception is pending.  Use this if you have a
+ * "self" pointer.
+ */
+INLINE bool dvmCheckException(Thread* self) {
+    return (self->exception != NULL);
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception);
+
+/*
+ * Wrap the now-pending exception in a different exception.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr);
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * Returns NULL if the field is null or uninitialized.
+ */
+Object* dvmGetExceptionCause(const Object* exception);
+
+/*
+ * Print the exception stack trace on stderr.  Calls the exception's
+ * print function.
+ */
+void dvmPrintExceptionStackTrace(void);
+
+/*
+ * Print the exception stack trace to the log file.  The exception stack
+ * trace is computed within the VM.
+ */
+void dvmLogExceptionStackTrace(void);
+
+/*
+ * Search for a catch block that matches "exception".
+ *
+ * "*newFrame" gets a copy of the new frame pointer.
+ *
+ * If "doUnroll" is set, we unroll "thread"s stack as we go (and update
+ * self->interpSave.curFrame with the same value as in *newFrame).
+ *
+ * Returns the offset to the catch code on success, or -1 if we couldn't
+ * find a catcher.
+ */
+extern "C" int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    bool doUnroll, void** newFrame);
+
+/*
+ * Support for saving exception stack traces and converting them to
+ * usable form.  Use the "FillIn" function to generate a compact array
+ * that represents the stack frames, then "GetStackTrace" to convert it
+ * to an array of StackTraceElement objects.
+ *
+ * Don't call the "Internal" form of the function directly.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount);
+/* return an [I for use by interpreted code */
+INLINE Object* dvmFillInStackTrace(Thread* thread) {
+    return (Object*) dvmFillInStackTraceInternal(thread, true, NULL);
+}
+ArrayObject* dvmGetStackTrace(const Object* stackState);
+/* return an int* and array count; caller must free() the return value */
+INLINE int* dvmFillInStackTraceRaw(Thread* thread, size_t* pCount) {
+    return (int*) dvmFillInStackTraceInternal(thread, false, pCount);
+}
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, size_t stackDepth);
+void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray);
+
+/*
+ * Print a formatted version of a raw stack trace to the log file.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth);
+
+/**
+ * Throw an AbstractMethodError in the current thread, with the given detail
+ * message.
+ */
+void dvmThrowAbstractMethodError(const char* msg);
+
+/**
+ * Throw an ArithmeticException in the current thread, with the given detail
+ * message.
+ */
+extern "C" void dvmThrowArithmeticException(const char* msg);
+
+/*
+ * Throw an ArrayIndexOutOfBoundsException in the current thread,
+ * using the given array length and index in the detail message.
+ */
+extern "C" void dvmThrowArrayIndexOutOfBoundsException(int length, int index);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given classes'
+ * names in the detail message, indicating that an object of the given type
+ * can't be stored into an array of the given type.
+ */
+extern "C" void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType, ClassObject* arrayType);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * class name and argument label in the detail message, indicating
+ * that it is not an array.
+ */
+void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * classes' names in the detail message, indicating that the arrays
+ * aren't compatible (for copying contents).
+ */
+void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination);
+
+/*
+ * Throw an ArrayStoreException in the current thread, using the given
+ * index and classes' names in the detail message, indicating that the
+ * object at the given index and of the given type cannot be stored
+ * into an array of the given type.
+ */
+void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType,
+        ClassObject* arrayType);
+
+/**
+ * Throw a ClassCastException in the current thread, using the given classes'
+ * names in the detail message.
+ */
+extern "C" void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired);
+
+/**
+ * Throw a ClassCircularityError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowClassCircularityError(const char* descriptor);
+
+/**
+ * Throw a ClassFormatError in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowClassFormatError(const char* msg);
+
+/**
+ * Throw a ClassNotFoundException in the current thread, with the given
+ * class name as the detail message.
+ */
+void dvmThrowClassNotFoundException(const char* name);
+
+/**
+ * Throw a ClassNotFoundException in the current thread, with the given
+ * cause, and the given class name as the detail message.
+ */
+void dvmThrowChainedClassNotFoundException(const char* name, Object* cause);
+
+/*
+ * Throw the VM-spec-mandated error when an exception is thrown during
+ * class initialization. Unlike other helper functions, this automatically
+ * wraps the current thread's pending exception.
+ */
+void dvmThrowExceptionInInitializerError(void);
+
+/**
+ * Throw a FileNotFoundException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowFileNotFoundException(const char* msg);
+
+/**
+ * Throw an IOException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowIOException(const char* msg);
+
+/**
+ * Throw an IllegalAccessError in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalAccessError(const char* msg);
+
+/**
+ * Throw an IllegalAccessException in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalAccessException(const char* msg);
+
+/**
+ * Throw an IllegalArgumentException in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowIllegalArgumentException(const char* msg);
+
+/**
+ * Throw an IllegalMonitorStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalMonitorStateException(const char* msg);
+
+/**
+ * Throw an IllegalStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalStateException(const char* msg);
+
+/**
+ * Throw an IllegalThreadStateException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowIllegalThreadStateException(const char* msg);
+
+/**
+ * Throw an IncompatibleClassChangeError in the current thread,
+ * the given detail message.
+ */
+void dvmThrowIncompatibleClassChangeError(const char* msg);
+
+/**
+ * Throw an IncompatibleClassChangeError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+        const char* descriptor);
+
+/**
+ * Throw an InstantiationException in the current thread, with
+ * the human-readable form of the given class as the detail message,
+ * with optional extra detail appended to the message.
+ */
+void dvmThrowInstantiationException(ClassObject* clazz,
+        const char* extraDetail);
+
+/**
+ * Throw an InternalError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowInternalError(const char* msg);
+
+/**
+ * Throw an InterruptedException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowInterruptedException(const char* msg);
+
+/**
+ * Throw a LinkageError in the current thread, with the
+ * given detail message.
+ */
+void dvmThrowLinkageError(const char* msg);
+
+/**
+ * Throw a NegativeArraySizeException in the current thread, with the
+ * given number as the detail message.
+ */
+extern "C" void dvmThrowNegativeArraySizeException(s4 size);
+
+/**
+ * Throw a NoClassDefFoundError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+extern "C" void dvmThrowNoClassDefFoundError(const char* descriptor);
+
+/**
+ * Throw a NoClassDefFoundError in the current thread, with the given
+ * cause, and the human-readable form of the given descriptor as the
+ * detail message.
+ */
+void dvmThrowChainedNoClassDefFoundError(const char* descriptor,
+        Object* cause);
+
+/**
+ * Throw a NoSuchFieldError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNoSuchFieldError(const char* msg);
+
+/**
+ * Throw a NoSuchFieldException in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowNoSuchFieldException(const char* msg);
+
+/**
+ * Throw a NoSuchMethodError in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNoSuchMethodError(const char* msg);
+
+/**
+ * Throw a NullPointerException in the current thread, with the given
+ * detail message.
+ */
+extern "C" void dvmThrowNullPointerException(const char* msg);
+
+/**
+ * Throw an OutOfMemoryError in the current thread, with the given
+ * detail message.
+ */
+void dvmThrowOutOfMemoryError(const char* msg);
+
+/**
+ * Throw a RuntimeException in the current thread, with the given detail
+ * message.
+ */
+void dvmThrowRuntimeException(const char* msg);
+
+/**
+ * Throw a StaleDexCacheError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowStaleDexCacheError(const char* msg);
+
+/**
+ * Throw a StringIndexOutOfBoundsException in the current thread, with
+ * a detail message specifying an actual length as well as a requested
+ * index.
+ */
+void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength,
+        jsize requestIndex);
+
+/**
+ * Throw a StringIndexOutOfBoundsException in the current thread, with
+ * a detail message specifying an actual length as well as a requested
+ * region.
+ */
+void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength,
+        jsize requestStart, jsize requestLength);
+
+/**
+ * Throw a TypeNotPresentException in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowTypeNotPresentException(const char* descriptor);
+
+/**
+ * Throw an UnsatisfiedLinkError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowUnsatisfiedLinkError(const char* msg);
+void dvmThrowUnsatisfiedLinkError(const char* msg, const Method* method);
+
+/**
+ * Throw an UnsupportedOperationException in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowUnsupportedOperationException(const char* msg);
+
+/**
+ * Throw a VerifyError in the current thread, with the
+ * human-readable form of the given descriptor as the detail message.
+ */
+void dvmThrowVerifyError(const char* descriptor);
+
+/**
+ * Throw a VirtualMachineError in the current thread, with
+ * the given detail message.
+ */
+void dvmThrowVirtualMachineError(const char* msg);
+
+#endif  // DALVIK_EXCEPTION_H_
diff --git a/vm/Globals.h b/vm/Globals.h
new file mode 100644
index 0000000..29f7356
--- /dev/null
+++ b/vm/Globals.h
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Variables with library scope.
+ *
+ * Prefer this over scattered static and global variables -- it's easier to
+ * view the state in a debugger, it makes clean shutdown simpler, we can
+ * trivially dump the state into a crash log, and it dodges most naming
+ * collisions that will arise when we are embedded in a larger program.
+ *
+ * If we want multiple VMs per process, this can get stuffed into TLS (or
+ * accessed through a Thread field).  May need to pass it around for some
+ * of the early initialization functions.
+ */
+#ifndef DALVIK_GLOBALS_H_
+#define DALVIK_GLOBALS_H_
+
+#include <string>
+#include <vector>
+
+#include <stdarg.h>
+#include <pthread.h>
+
+/* private structures */
+struct GcHeap;
+struct BreakpointSet;
+struct InlineSub;
+
+/*
+ * One of these for each -ea/-da/-esa/-dsa on the command line.
+ */
+struct AssertionControl {
+    char*   pkgOrClass;         /* package/class string, or NULL for esa/dsa */
+    int     pkgOrClassLen;      /* string length, for quick compare */
+    bool    enable;             /* enable or disable */
+    bool    isPackage;          /* string ended with "..."? */
+};
+
+/*
+ * 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.
+ */
+enum RegisterMapMode {
+    kRegisterMapModeUnknown = 0,
+    kRegisterMapModeTypePrecise,
+    kRegisterMapModeLivePrecise
+};
+
+/*
+ * Profiler clock source.
+ */
+enum ProfilerClockSource {
+    kProfilerClockSourceThreadCpu,
+    kProfilerClockSourceWall,
+    kProfilerClockSourceDual,
+};
+
+/*
+ * All fields are initialized to zero.
+ *
+ * Storage allocated here must be freed by a subsystem shutdown function.
+ */
+struct DvmGlobals {
+    /*
+     * Some options from the command line or environment.
+     */
+    char*       bootClassPathStr;
+    char*       classPathStr;
+
+    size_t      heapStartingSize;
+    size_t      heapMaximumSize;
+    size_t      heapGrowthLimit;
+    bool        lowMemoryMode;
+    double      heapTargetUtilization;
+    size_t      heapMinFree;
+    size_t      heapMaxFree;
+    size_t      stackSize;
+    size_t      mainThreadStackSize;
+
+    bool        verboseGc;
+    bool        verboseJni;
+    bool        verboseClass;
+    bool        verboseShutdown;
+
+    bool        jdwpAllowed;        // debugging allowed for this process?
+    bool        jdwpConfigured;     // has debugging info been provided?
+    JdwpTransportType jdwpTransport;
+    bool        jdwpServer;
+    char*       jdwpHost;
+    int         jdwpPort;
+    bool        jdwpSuspend;
+
+    ProfilerClockSource profilerClockSource;
+
+    /*
+     * Lock profiling threshold value in milliseconds.  Acquires that
+     * exceed threshold are logged.  Acquires within the threshold are
+     * logged with a probability of $\frac{time}{threshold}$ .  If the
+     * threshold is unset no additional logging occurs.
+     */
+    u4          lockProfThreshold;
+
+    int         (*vfprintfHook)(FILE*, const char*, va_list);
+    void        (*exitHook)(int);
+    void        (*abortHook)(void);
+    bool        (*isSensitiveThreadHook)(void);
+
+    char*       jniTrace;
+    bool        reduceSignals;
+    bool        noQuitHandler;
+    bool        verifyDexChecksum;
+    char*       stackTraceFile;     // for SIGQUIT-inspired output
+
+    bool        logStdio;
+
+    DexOptimizerMode    dexOptMode;
+    DexClassVerifyMode  classVerifyMode;
+
+    bool        generateRegisterMaps;
+    RegisterMapMode     registerMapMode;
+
+    bool        monitorVerification;
+
+    bool        dexOptForSmp;
+
+    /*
+     * GC option flags.
+     */
+    bool        preciseGc;
+    bool        preVerify;
+    bool        postVerify;
+    bool        concurrentMarkSweep;
+    bool        verifyCardTable;
+    bool        disableExplicitGc;
+
+    int         assertionCtrlCount;
+    AssertionControl*   assertionCtrl;
+
+    ExecutionMode   executionMode;
+
+    bool        commonInit; /* whether common stubs are generated */
+    bool        constInit; /* whether global constants are initialized */
+
+    /*
+     * VM init management.
+     */
+    bool        initializing;
+    bool        optimizing;
+
+    /*
+     * java.lang.System properties set from the command line with -D.
+     * This is effectively a set, where later entries override earlier
+     * ones.
+     */
+    std::vector<std::string>* properties;
+
+    /*
+     * Where the VM goes to find system classes.
+     */
+    ClassPathEntry* bootClassPath;
+    /* used by the DEX optimizer to load classes from an unfinished DEX */
+    DvmDex*     bootClassPathOptExtra;
+    bool        optimizingBootstrapClass;
+
+    /*
+     * Loaded classes, hashed by class name.  Each entry is a ClassObject*,
+     * allocated in GC space.
+     */
+    HashTable*  loadedClasses;
+
+    /*
+     * Value for the next class serial number to be assigned.  This is
+     * incremented as we load classes.  Failed loads and races may result
+     * in some numbers being skipped, and the serial number is not
+     * guaranteed to start at 1, so the current value should not be used
+     * as a count of loaded classes.
+     */
+    volatile int classSerialNumber;
+
+    /*
+     * Classes with a low classSerialNumber are probably in the zygote, and
+     * their InitiatingLoaderList is not used, to promote sharing. The list is
+     * kept here instead.
+     */
+    InitiatingLoaderList* initiatingLoaderList;
+
+    /*
+     * Interned strings.
+     */
+
+    /* A mutex that guards access to the interned string tables. */
+    pthread_mutex_t internLock;
+
+    /* Hash table of strings interned by the user. */
+    HashTable*  internedStrings;
+
+    /* Hash table of strings interned by the class loader. */
+    HashTable*  literalStrings;
+
+    /*
+     * Classes constructed directly by the vm.
+     */
+
+    /* the class Class */
+    ClassObject* classJavaLangClass;
+
+    /* synthetic classes representing primitive types */
+    ClassObject* typeVoid;
+    ClassObject* typeBoolean;
+    ClassObject* typeByte;
+    ClassObject* typeShort;
+    ClassObject* typeChar;
+    ClassObject* typeInt;
+    ClassObject* typeLong;
+    ClassObject* typeFloat;
+    ClassObject* typeDouble;
+
+    /* synthetic classes for arrays of primitives */
+    ClassObject* classArrayBoolean;
+    ClassObject* classArrayByte;
+    ClassObject* classArrayShort;
+    ClassObject* classArrayChar;
+    ClassObject* classArrayInt;
+    ClassObject* classArrayLong;
+    ClassObject* classArrayFloat;
+    ClassObject* classArrayDouble;
+
+    /*
+     * Quick lookups for popular classes used internally.
+     */
+    ClassObject* classJavaLangClassArray;
+    ClassObject* classJavaLangClassLoader;
+    ClassObject* classJavaLangObject;
+    ClassObject* classJavaLangObjectArray;
+    ClassObject* classJavaLangString;
+    ClassObject* classJavaLangThread;
+    ClassObject* classJavaLangVMThread;
+    ClassObject* classJavaLangThreadGroup;
+    ClassObject* classJavaLangStackTraceElement;
+    ClassObject* classJavaLangStackTraceElementArray;
+    ClassObject* classJavaLangAnnotationAnnotationArray;
+    ClassObject* classJavaLangAnnotationAnnotationArrayArray;
+    ClassObject* classJavaLangReflectAccessibleObject;
+    ClassObject* classJavaLangReflectConstructor;
+    ClassObject* classJavaLangReflectConstructorArray;
+    ClassObject* classJavaLangReflectField;
+    ClassObject* classJavaLangReflectFieldArray;
+    ClassObject* classJavaLangReflectMethod;
+    ClassObject* classJavaLangReflectMethodArray;
+    ClassObject* classJavaLangReflectProxy;
+    ClassObject* classJavaLangSystem;
+    ClassObject* classJavaNioDirectByteBuffer;
+    ClassObject* classLibcoreReflectAnnotationFactory;
+    ClassObject* classLibcoreReflectAnnotationMember;
+    ClassObject* classLibcoreReflectAnnotationMemberArray;
+    ClassObject* classOrgApacheHarmonyDalvikDdmcChunk;
+    ClassObject* classOrgApacheHarmonyDalvikDdmcDdmServer;
+    ClassObject* classJavaLangRefFinalizerReference;
+
+    /*
+     * classes representing exception types. The names here don't include
+     * packages, just to keep the use sites a bit less verbose. All are
+     * in java.lang, except where noted.
+     */
+    ClassObject* exAbstractMethodError;
+    ClassObject* exArithmeticException;
+    ClassObject* exArrayIndexOutOfBoundsException;
+    ClassObject* exArrayStoreException;
+    ClassObject* exClassCastException;
+    ClassObject* exClassCircularityError;
+    ClassObject* exClassFormatError;
+    ClassObject* exClassNotFoundException;
+    ClassObject* exError;
+    ClassObject* exExceptionInInitializerError;
+    ClassObject* exFileNotFoundException; /* in java.io */
+    ClassObject* exIOException;           /* in java.io */
+    ClassObject* exIllegalAccessError;
+    ClassObject* exIllegalAccessException;
+    ClassObject* exIllegalArgumentException;
+    ClassObject* exIllegalMonitorStateException;
+    ClassObject* exIllegalStateException;
+    ClassObject* exIllegalThreadStateException;
+    ClassObject* exIncompatibleClassChangeError;
+    ClassObject* exInstantiationError;
+    ClassObject* exInstantiationException;
+    ClassObject* exInternalError;
+    ClassObject* exInterruptedException;
+    ClassObject* exLinkageError;
+    ClassObject* exNegativeArraySizeException;
+    ClassObject* exNoClassDefFoundError;
+    ClassObject* exNoSuchFieldError;
+    ClassObject* exNoSuchFieldException;
+    ClassObject* exNoSuchMethodError;
+    ClassObject* exNullPointerException;
+    ClassObject* exOutOfMemoryError;
+    ClassObject* exRuntimeException;
+    ClassObject* exStackOverflowError;
+    ClassObject* exStaleDexCacheError;    /* in dalvik.system */
+    ClassObject* exStringIndexOutOfBoundsException;
+    ClassObject* exThrowable;
+    ClassObject* exTypeNotPresentException;
+    ClassObject* exUnsatisfiedLinkError;
+    ClassObject* exUnsupportedOperationException;
+    ClassObject* exVerifyError;
+    ClassObject* exVirtualMachineError;
+
+    /* method offsets - Object */
+    int         voffJavaLangObject_equals;
+    int         voffJavaLangObject_hashCode;
+    int         voffJavaLangObject_toString;
+
+    /* field offsets - String */
+    int         offJavaLangString_value;
+    int         offJavaLangString_count;
+    int         offJavaLangString_offset;
+    int         offJavaLangString_hashCode;
+
+    /* field offsets - Thread */
+    int         offJavaLangThread_vmThread;
+    int         offJavaLangThread_group;
+    int         offJavaLangThread_daemon;
+    int         offJavaLangThread_name;
+    int         offJavaLangThread_priority;
+    int         offJavaLangThread_uncaughtHandler;
+    int         offJavaLangThread_contextClassLoader;
+
+    /* method offsets - Thread */
+    int         voffJavaLangThread_run;
+
+    /* field offsets - ThreadGroup */
+    int         offJavaLangThreadGroup_name;
+    int         offJavaLangThreadGroup_parent;
+
+    /* field offsets - VMThread */
+    int         offJavaLangVMThread_thread;
+    int         offJavaLangVMThread_vmData;
+
+    /* method offsets - ThreadGroup */
+    int         voffJavaLangThreadGroup_removeThread;
+
+    /* field offsets - Throwable */
+    int         offJavaLangThrowable_stackState;
+    int         offJavaLangThrowable_cause;
+
+    /* method offsets - ClassLoader */
+    int         voffJavaLangClassLoader_loadClass;
+
+    /* direct method pointers - ClassLoader */
+    Method*     methJavaLangClassLoader_getSystemClassLoader;
+
+    /* field offsets - java.lang.reflect.* */
+    int         offJavaLangReflectConstructor_slot;
+    int         offJavaLangReflectConstructor_declClass;
+    int         offJavaLangReflectField_slot;
+    int         offJavaLangReflectField_declClass;
+    int         offJavaLangReflectMethod_slot;
+    int         offJavaLangReflectMethod_declClass;
+
+    /* field offsets - java.lang.ref.Reference */
+    int         offJavaLangRefReference_referent;
+    int         offJavaLangRefReference_queue;
+    int         offJavaLangRefReference_queueNext;
+    int         offJavaLangRefReference_pendingNext;
+
+    /* field offsets - java.lang.ref.FinalizerReference */
+    int offJavaLangRefFinalizerReference_zombie;
+
+    /* method pointers - java.lang.ref.ReferenceQueue */
+    Method* methJavaLangRefReferenceQueueAdd;
+
+    /* method pointers - java.lang.ref.FinalizerReference */
+    Method* methJavaLangRefFinalizerReferenceAdd;
+
+    /* constructor method pointers; no vtable involved, so use Method* */
+    Method*     methJavaLangStackTraceElement_init;
+    Method*     methJavaLangReflectConstructor_init;
+    Method*     methJavaLangReflectField_init;
+    Method*     methJavaLangReflectMethod_init;
+    Method*     methOrgApacheHarmonyLangAnnotationAnnotationMember_init;
+
+    /* static method pointers - android.lang.annotation.* */
+    Method*
+        methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation;
+
+    /* direct method pointers - java.lang.reflect.Proxy */
+    Method*     methJavaLangReflectProxy_constructorPrototype;
+
+    /* field offsets - java.lang.reflect.Proxy */
+    int         offJavaLangReflectProxy_h;
+
+    /* direct method pointer - java.lang.System.runFinalization */
+    Method*     methJavaLangSystem_runFinalization;
+
+    /* field offsets - java.io.FileDescriptor */
+    int         offJavaIoFileDescriptor_descriptor;
+
+    /* direct method pointers - dalvik.system.NativeStart */
+    Method*     methDalvikSystemNativeStart_main;
+    Method*     methDalvikSystemNativeStart_run;
+
+    /* assorted direct buffer helpers */
+    Method*     methJavaNioDirectByteBuffer_init;
+    int         offJavaNioBuffer_capacity;
+    int         offJavaNioBuffer_effectiveDirectAddress;
+
+    /* direct method pointers - org.apache.harmony.dalvik.ddmc.DdmServer */
+    Method*     methDalvikDdmcServer_dispatch;
+    Method*     methDalvikDdmcServer_broadcast;
+
+    /* field offsets - org.apache.harmony.dalvik.ddmc.Chunk */
+    int         offDalvikDdmcChunk_type;
+    int         offDalvikDdmcChunk_data;
+    int         offDalvikDdmcChunk_offset;
+    int         offDalvikDdmcChunk_length;
+
+    /*
+     * Thread list.  This always has at least one element in it (main),
+     * and main is always the first entry.
+     *
+     * The threadListLock is used for several things, including the thread
+     * start condition variable.  Generally speaking, you must hold the
+     * threadListLock when:
+     *  - adding/removing items from the list
+     *  - waiting on or signaling threadStartCond
+     *  - examining the Thread struct for another thread (this is to avoid
+     *    one thread freeing the Thread struct while another thread is
+     *    perusing it)
+     */
+    Thread*     threadList;
+    pthread_mutex_t threadListLock;
+
+    pthread_cond_t threadStartCond;
+
+    /*
+     * The thread code grabs this before suspending all threads.  There
+     * are a few things that can cause a "suspend all":
+     *  (1) the GC is starting;
+     *  (2) the debugger has sent a "suspend all" request;
+     *  (3) a thread has hit a breakpoint or exception that the debugger
+     *      has marked as a "suspend all" event;
+     *  (4) the SignalCatcher caught a signal that requires suspension.
+     *  (5) (if implemented) the JIT needs to perform a heavyweight
+     *      rearrangement of the translation cache or JitTable.
+     *
+     * Because we use "safe point" self-suspension, it is never safe to
+     * do a blocking "lock" call on this mutex -- if it has been acquired,
+     * somebody is probably trying to put you to sleep.  The leading '_' is
+     * intended as a reminder that this lock is special.
+     */
+    pthread_mutex_t _threadSuspendLock;
+
+    /*
+     * Guards Thread->suspendCount for all threads, and
+     * provides the lock for the condition variable that all suspended threads
+     * sleep on (threadSuspendCountCond).
+     *
+     * This has to be separate from threadListLock because of the way
+     * threads put themselves to sleep.
+     */
+    pthread_mutex_t threadSuspendCountLock;
+
+    /*
+     * Suspended threads sleep on this.  They should sleep on the condition
+     * variable until their "suspend count" is zero.
+     *
+     * Paired with "threadSuspendCountLock".
+     */
+    pthread_cond_t  threadSuspendCountCond;
+
+    /*
+     * Sum of all threads' suspendCount fields. Guarded by
+     * threadSuspendCountLock.
+     */
+    int  sumThreadSuspendCount;
+
+    /*
+     * MUTEX ORDERING: when locking multiple mutexes, always grab them in
+     * this order to avoid deadlock:
+     *
+     *  (1) _threadSuspendLock      (use lockThreadSuspend())
+     *  (2) threadListLock          (use dvmLockThreadList())
+     *  (3) threadSuspendCountLock  (use lockThreadSuspendCount())
+     */
+
+
+    /*
+     * Thread ID bitmap.  We want threads to have small integer IDs so
+     * we can use them in "thin locks".
+     */
+    BitVector*  threadIdMap;
+
+    /*
+     * Manage exit conditions.  The VM exits when all non-daemon threads
+     * have exited.  If the main thread returns early, we need to sleep
+     * on a condition variable.
+     */
+    int         nonDaemonThreadCount;   /* must hold threadListLock to access */
+    pthread_cond_t  vmExitCond;
+
+    /*
+     * The set of DEX files loaded by custom class loaders.
+     */
+    HashTable*  userDexFiles;
+
+    /*
+     * JNI global reference table.
+     */
+    IndirectRefTable jniGlobalRefTable;
+    IndirectRefTable jniWeakGlobalRefTable;
+    pthread_mutex_t jniGlobalRefLock;
+    pthread_mutex_t jniWeakGlobalRefLock;
+
+    /*
+     * JNI pinned object table (used for primitive arrays).
+     */
+    ReferenceTable  jniPinRefTable;
+    pthread_mutex_t jniPinRefLock;
+
+    /*
+     * Native shared library table.
+     */
+    HashTable*  nativeLibs;
+
+    /*
+     * GC heap lock.  Functions like gcMalloc() acquire this before making
+     * any changes to the heap.  It is held throughout garbage collection.
+     */
+    pthread_mutex_t gcHeapLock;
+
+    /*
+     * Condition variable to queue threads waiting to retry an
+     * allocation.  Signaled after a concurrent GC is completed.
+     */
+    pthread_cond_t gcHeapCond;
+
+    /* Opaque pointer representing the heap. */
+    GcHeap*     gcHeap;
+
+    /* The card table base, modified as needed for marking cards. */
+    u1*         biasedCardTableBase;
+
+    /*
+     * Pre-allocated throwables.
+     */
+    Object*     outOfMemoryObj;
+    Object*     internalErrorObj;
+    Object*     noClassDefFoundErrorObj;
+
+    /* Monitor list, so we can free them */
+    /*volatile*/ Monitor* monitorList;
+
+    /* Monitor for Thread.sleep() implementation */
+    Monitor*    threadSleepMon;
+
+    /* set when we create a second heap inside the zygote */
+    bool        newZygoteHeapAllocated;
+
+    /*
+     * TLS keys.
+     */
+    pthread_key_t pthreadKeySelf;       /* Thread*, for dvmThreadSelf */
+
+    /*
+     * Cache results of "A instanceof B".
+     */
+    AtomicCache* instanceofCache;
+
+    /* inline substitution table, used during optimization */
+    InlineSub*          inlineSubs;
+
+    /*
+     * Bootstrap class loader linear allocator.
+     */
+    LinearAllocHdr* pBootLoaderAlloc;
+
+    /*
+     * Compute some stats on loaded classes.
+     */
+    int         numLoadedClasses;
+    int         numDeclaredMethods;
+    int         numDeclaredInstFields;
+    int         numDeclaredStaticFields;
+
+    /* when using a native debugger, set this to suppress watchdog timers */
+    bool        nativeDebuggerActive;
+
+    /*
+     * JDWP debugger support.
+     *
+     * Note: Each thread will normally determine whether the debugger is active
+     * for it by referring to its subMode flags.  "debuggerActive" here should be
+     * seen as "debugger is making requests of 1 or more threads".
+     */
+    bool        debuggerConnected;      /* debugger or DDMS is connected */
+    bool        debuggerActive;         /* debugger is making requests */
+    JdwpState*  jdwpState;
+
+    /*
+     * Registry of objects known to the debugger.
+     */
+    HashTable*  dbgRegistry;
+
+    /*
+     * Debugger breakpoint table.
+     */
+    BreakpointSet*  breakpointSet;
+
+    /*
+     * Single-step control struct.  We currently only allow one thread to
+     * be single-stepping at a time, which is all that really makes sense,
+     * but it's possible we may need to expand this to be per-thread.
+     */
+    StepControl stepControl;
+
+    /*
+     * DDM features embedded in the VM.
+     */
+    bool        ddmThreadNotification;
+
+    /*
+     * Zygote (partially-started process) support
+     */
+    bool        zygote;
+
+    /*
+     * Used for tracking allocations that we report to DDMS.  When the feature
+     * is enabled (through a DDMS request) the "allocRecords" pointer becomes
+     * non-NULL.
+     */
+    pthread_mutex_t allocTrackerLock;
+    AllocRecord*    allocRecords;
+    int             allocRecordHead;        /* most-recently-added entry */
+    int             allocRecordCount;       /* #of valid entries */
+    int             allocRecordMax;         /* Number of allocated entries. */
+
+    /*
+     * When a profiler is enabled, this is incremented.  Distinct profilers
+     * include "dmtrace" method tracing, emulator method tracing, and
+     * possibly instruction counting.
+     *
+     * The purpose of this is to have a single value that shows whether any
+     * profiling is going on.  Individual thread will normally check their
+     * thread-private subMode flags to take any profiling action.
+     */
+    volatile int activeProfilers;
+
+    /*
+     * State for method-trace profiling.
+     */
+    MethodTraceState methodTrace;
+    Method*     methodTraceGcMethod;
+    Method*     methodTraceClassPrepMethod;
+
+    /*
+     * State for emulator tracing.
+     */
+    void*       emulatorTracePage;
+    int         emulatorTraceEnableCount;
+
+    /*
+     * Global state for memory allocation profiling.
+     */
+    AllocProfState allocProf;
+
+    /*
+     * Pointers to the original methods for things that have been inlined.
+     * This makes it easy for us to output method entry/exit records for
+     * the method calls we're not actually making.  (Used by method
+     * profiling.)
+     */
+    Method**    inlinedMethods;
+
+    /*
+     * Dalvik instruction counts (kNumPackedOpcodes entries).
+     */
+    int*        executedInstrCounts;
+    int         instructionCountEnableCount;
+
+    /*
+     * Signal catcher thread (for SIGQUIT).
+     */
+    pthread_t   signalCatcherHandle;
+    bool        haltSignalCatcher;
+
+    /*
+     * Stdout/stderr conversion thread.
+     */
+    bool            haltStdioConverter;
+    bool            stdioConverterReady;
+    pthread_t       stdioConverterHandle;
+    pthread_mutex_t stdioConverterLock;
+    pthread_cond_t  stdioConverterCond;
+    int             stdoutPipe[2];
+    int             stderrPipe[2];
+
+    /*
+     * pid of the system_server process. We track it so that when system server
+     * crashes the Zygote process will be killed and restarted.
+     */
+    pid_t systemServerPid;
+
+    int kernelGroupScheduling;
+
+//#define COUNT_PRECISE_METHODS
+#ifdef COUNT_PRECISE_METHODS
+    PointerSet* preciseMethods;
+#endif
+
+    /* some RegisterMap statistics, useful during development */
+    void*       registerMapStats;
+
+#ifdef VERIFIER_STATS
+    VerifierStats verifierStats;
+#endif
+
+    /* String pointed here will be deposited on the stack frame of dvmAbort */
+    const char *lastMessage;
+};
+
+extern struct DvmGlobals gDvm;
+
+#if defined(WITH_JIT)
+
+#define DEFAULT_CODE_CACHE_SIZE 0xffffffff
+
+/* Trace profiling modes.  Ordering matters - off states before on states */
+enum TraceProfilingModes {
+    kTraceProfilingDisabled = 0,      // Not profiling
+    kTraceProfilingPeriodicOff = 1,   // Periodic profiling, off phase
+    kTraceProfilingContinuous = 2,    // Always profiling
+    kTraceProfilingPeriodicOn = 3     // Periodic profiling, on phase
+};
+
+/*
+ * Exiting the compiled code w/o chaining will incur overhead to look up the
+ * target in the code cache which is extra work only when JIT is enabled. So
+ * we want to monitor it closely to make sure we don't have performance bugs.
+ */
+enum NoChainExits {
+    kInlineCacheMiss = 0,
+    kCallsiteInterpreted,
+    kSwitchOverflow,
+    kHeavyweightMonitor,
+    kNoChainExitLast,
+};
+
+/*
+ * JIT-specific global state
+ */
+struct DvmJitGlobals {
+    /*
+     * Guards writes to Dalvik PC (dPC), translated code address (codeAddr) and
+     * chain fields within the JIT hash table.  Note carefully the access
+     * mechanism.
+     * Only writes are guarded, and the guarded fields must be updated in a
+     * specific order using atomic operations.  Further, once a field is
+     * written it cannot be changed without halting all threads.
+     *
+     * The write order is:
+     *    1) codeAddr
+     *    2) dPC
+     *    3) chain [if necessary]
+     *
+     * This mutex also guards both read and write of curJitTableEntries.
+     */
+    pthread_mutex_t tableLock;
+
+    /* The JIT hash table.  Note that for access speed, copies of this pointer
+     * are stored in each thread. */
+    struct JitEntry *pJitEntryTable;
+
+    /* Array of compilation trigger threshold counters */
+    unsigned char *pProfTable;
+
+    /* Trace profiling counters */
+    struct JitTraceProfCounters *pJitTraceProfCounters;
+
+    /* Copy of pProfTable used for temporarily disabling the Jit */
+    unsigned char *pProfTableCopy;
+
+    /* Size of JIT hash table in entries.  Must be a power of 2 */
+    unsigned int jitTableSize;
+
+    /* Mask used in hash function for JitTable.  Should be jitTableSize-1 */
+    unsigned int jitTableMask;
+
+    /* How many entries in the JitEntryTable are in use */
+    unsigned int jitTableEntriesUsed;
+
+    /* Max bytes allocated for the code cache.  Rough rule of thumb: 1K per 1M of system RAM */
+    unsigned int codeCacheSize;
+
+    /* Trigger for trace selection */
+    unsigned short threshold;
+
+    /* JIT Compiler Control */
+    bool               haltCompilerThread;
+    bool               blockingMode;
+    bool               methodTraceSupport;
+    bool               genSuspendPoll;
+    Thread*            compilerThread;
+    pthread_t          compilerHandle;
+    pthread_mutex_t    compilerLock;
+    pthread_mutex_t    compilerICPatchLock;
+    pthread_cond_t     compilerQueueActivity;
+    pthread_cond_t     compilerQueueEmpty;
+    volatile int       compilerQueueLength;
+    int                compilerHighWater;
+    int                compilerWorkEnqueueIndex;
+    int                compilerWorkDequeueIndex;
+    int                compilerICPatchIndex;
+
+    /* JIT internal stats */
+    int                compilerMaxQueued;
+    int                translationChains;
+
+    /* Compiled code cache */
+    void* codeCache;
+
+    /*
+     * This is used to store the base address of an in-flight compilation whose
+     * class object pointers have been calculated to populate literal pool.
+     * Once the compiler thread has changed its status to VM_WAIT, we cannot
+     * guarantee whether GC has happened before the code address has been
+     * installed to the JIT table. Because of that, this field can only
+     * been cleared/overwritten by the compiler thread if it is in the
+     * THREAD_RUNNING state or in a safe point.
+     */
+    void *inflightBaseAddr;
+
+    /* Translation cache version (protected by compilerLock */
+    int cacheVersion;
+
+    /* Bytes used by the code templates */
+    unsigned int templateSize;
+
+    /* Bytes already used in the code cache */
+    unsigned int codeCacheByteUsed;
+
+    /* Number of installed compilations in the cache */
+    unsigned int numCompilations;
+
+    /* Flag to indicate that the code cache is full */
+    bool codeCacheFull;
+
+    /* Page size  - 1 */
+    unsigned int pageSizeMask;
+
+    /* Lock to change the protection type of the code cache */
+    pthread_mutex_t    codeCacheProtectionLock;
+
+    /* Number of times that the code cache has been reset */
+    int numCodeCacheReset;
+
+    /* Number of times that the code cache reset request has been delayed */
+    int numCodeCacheResetDelayed;
+
+    /* true/false: compile/reject opcodes specified in the -Xjitop list */
+    bool includeSelectedOp;
+
+    /* true/false: compile/reject methods specified in the -Xjitmethod list */
+    bool includeSelectedMethod;
+
+    /* true/false: compile/reject traces with offset specified in the -Xjitoffset list */
+    bool includeSelectedOffset;
+
+    /* Disable JIT for selected opcodes - one bit for each opcode */
+    char opList[(kNumPackedOpcodes+7)/8];
+
+    /* Disable JIT for selected methods */
+    HashTable *methodTable;
+
+    /* Disable JIT for selected classes */
+    HashTable *classTable;
+
+    /* Disable JIT for selected offsets */
+    unsigned int pcTable[COMPILER_PC_OFFSET_SIZE];
+    int num_entries_pcTable;
+
+    /* Flag to dump all compiled code */
+    bool printMe;
+
+    /* Flag to dump compiled binary code in bytes */
+    bool printBinary;
+
+    /* Per-process debug flag toggled when receiving a SIGUSR2 */
+    bool receivedSIGUSR2;
+
+    /* Trace profiling mode */
+    TraceProfilingModes profileMode;
+
+    /* Periodic trace profiling countdown timer */
+    int profileCountdown;
+
+    /* Vector to disable selected optimizations */
+    int disableOpt;
+
+    /* Table to track the overall and trace statistics of hot methods */
+    HashTable*  methodStatsTable;
+
+    /* Filter method compilation blacklist with call-graph information */
+    bool checkCallGraph;
+
+    /* New translation chain has been set up */
+    volatile bool hasNewChain;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Spin when error is detected, volatile so GDB can reset it */
+    volatile bool selfVerificationSpin;
+#endif
+
+    /* Framework or stand-alone? */
+    bool runningInAndroidFramework;
+
+    /* Framework callback happened? */
+    bool alreadyEnabledViaFramework;
+
+    /* Framework requests to disable the JIT for good */
+    bool disableJit;
+
+#if defined(SIGNATURE_BREAKPOINT)
+    /* Signature breakpoint */
+    u4 signatureBreakpointSize;         // # of words
+    u4 *signatureBreakpoint;            // Signature content
+#endif
+
+#if defined(WITH_JIT_TUNING)
+    /* Performance tuning counters */
+    int                addrLookupsFound;
+    int                addrLookupsNotFound;
+    int                noChainExit[kNoChainExitLast];
+    int                normalExit;
+    int                puntExit;
+    int                invokeMonomorphic;
+    int                invokePolymorphic;
+    int                invokeNative;
+    int                invokeMonoGetterInlined;
+    int                invokeMonoSetterInlined;
+    int                invokePolyGetterInlined;
+    int                invokePolySetterInlined;
+    int                returnOp;
+    int                icPatchInit;
+    int                icPatchLockFree;
+    int                icPatchQueued;
+    int                icPatchRejected;
+    int                icPatchDropped;
+    int                codeCachePatches;
+    int                numCompilerThreadBlockGC;
+    u8                 jitTime;
+    u8                 compilerThreadBlockGCStart;
+    u8                 compilerThreadBlockGCTime;
+    u8                 maxCompilerThreadBlockGCTime;
+#endif
+
+#if defined(ARCH_IA32)
+    JitOptLevel        optLevel;
+#endif
+
+    /* Place arrays at the end to ease the display in gdb sessions */
+
+    /* Work order queue for compilations */
+    CompilerWorkOrder compilerWorkQueue[COMPILER_WORK_QUEUE_SIZE];
+
+    /* Work order queue for predicted chain patching */
+    ICPatchWorkOrder compilerICPatchQueue[COMPILER_IC_PATCH_QUEUE_SIZE];
+};
+
+extern struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+extern int gDvmICHitCount;
+#endif
+
+#endif
+
+struct DvmJniGlobals {
+    bool useCheckJni;
+    bool warnOnly;
+    bool forceCopy;
+
+    // Provide backwards compatibility for pre-ICS apps on ICS.
+    bool workAroundAppJniBugs;
+
+    // Debugging help for third-party developers. Similar to -Xjnitrace.
+    bool logThirdPartyJni;
+
+    // We only support a single JavaVM per process.
+    JavaVM*     jniVm;
+};
+
+extern struct DvmJniGlobals gDvmJni;
+
+#endif  // DALVIK_GLOBALS_H_
diff --git a/vm/Hash.cpp b/vm/Hash.cpp
new file mode 100644
index 0000000..cd4530a
--- /dev/null
+++ b/vm/Hash.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Hash table.  The dominant calls are add and lookup, with removals
+ * happening very infrequently.  We use probing, and don't worry much
+ * about tombstone removal.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/* table load factor, i.e. how full can it get before we resize */
+//#define LOAD_NUMER  3       // 75%
+//#define LOAD_DENOM  4
+#define LOAD_NUMER  5       // 62.5%
+#define LOAD_DENOM  8
+//#define LOAD_NUMER  1       // 50%
+//#define LOAD_DENOM  2
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.
+ */
+size_t dvmHashSize(size_t size) {
+    return (size * LOAD_DENOM) / LOAD_NUMER +1;
+}
+
+
+/*
+ * Create and initialize a hash table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc)
+{
+    HashTable* pHashTable;
+
+    assert(initialSize > 0);
+
+    pHashTable = (HashTable*) malloc(sizeof(*pHashTable));
+    if (pHashTable == NULL)
+        return NULL;
+
+    dvmInitMutex(&pHashTable->lock);
+
+    pHashTable->tableSize = dexRoundUpPower2(initialSize);
+    pHashTable->numEntries = pHashTable->numDeadEntries = 0;
+    pHashTable->freeFunc = freeFunc;
+    pHashTable->pEntries =
+        (HashEntry*) calloc(pHashTable->tableSize, sizeof(HashEntry));
+    if (pHashTable->pEntries == NULL) {
+        free(pHashTable);
+        return NULL;
+    }
+
+    return pHashTable;
+}
+
+/*
+ * Clear out all entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable)
+{
+    HashEntry* pEnt;
+    int i;
+
+    pEnt = pHashTable->pEntries;
+    for (i = 0; i < pHashTable->tableSize; i++, pEnt++) {
+        if (pEnt->data == HASH_TOMBSTONE) {
+            // nuke entry
+            pEnt->data = NULL;
+        } else if (pEnt->data != NULL) {
+            // call free func then nuke entry
+            if (pHashTable->freeFunc != NULL)
+                (*pHashTable->freeFunc)(pEnt->data);
+            pEnt->data = NULL;
+        }
+    }
+
+    pHashTable->numEntries = 0;
+    pHashTable->numDeadEntries = 0;
+}
+
+/*
+ * Free the table.
+ */
+void dvmHashTableFree(HashTable* pHashTable)
+{
+    if (pHashTable == NULL)
+        return;
+    dvmHashTableClear(pHashTable);
+    free(pHashTable->pEntries);
+    free(pHashTable);
+}
+
+#ifndef NDEBUG
+/*
+ * Count up the number of tombstone entries in the hash table.
+ */
+static int countTombStones(HashTable* pHashTable)
+{
+    int i, count;
+
+    for (count = i = 0; i < pHashTable->tableSize; i++) {
+        if (pHashTable->pEntries[i].data == HASH_TOMBSTONE)
+            count++;
+    }
+    return count;
+}
+#endif
+
+/*
+ * Resize a hash table.  We do this when adding an entry increased the
+ * size of the table beyond its comfy limit.
+ *
+ * This essentially requires re-inserting all elements into the new storage.
+ *
+ * If multiple threads can access the hash table, the table's lock should
+ * have been grabbed before issuing the "lookup+add" call that led to the
+ * resize, so we don't have a synchronization problem here.
+ */
+static bool resizeHash(HashTable* pHashTable, int newSize)
+{
+    HashEntry* pNewEntries;
+    int i;
+
+    assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
+    //ALOGI("before: dead=%d", pHashTable->numDeadEntries);
+
+    pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashEntry));
+    if (pNewEntries == NULL)
+        return false;
+
+    for (i = 0; i < pHashTable->tableSize; i++) {
+        void* data = pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE) {
+            int hashValue = pHashTable->pEntries[i].hashValue;
+            int newIdx;
+
+            /* probe for new spot, wrapping around */
+            newIdx = hashValue & (newSize-1);
+            while (pNewEntries[newIdx].data != NULL)
+                newIdx = (newIdx + 1) & (newSize-1);
+
+            pNewEntries[newIdx].hashValue = hashValue;
+            pNewEntries[newIdx].data = data;
+        }
+    }
+
+    free(pHashTable->pEntries);
+    pHashTable->pEntries = pNewEntries;
+    pHashTable->tableSize = newSize;
+    pHashTable->numDeadEntries = 0;
+
+    assert(countTombStones(pHashTable) == 0);
+    return true;
+}
+
+/*
+ * Look up an entry.
+ *
+ * We probe on collisions, wrapping around the table.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    void* result = NULL;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            //ALOGD("+++ match on entry %d", pEntry - pHashTable->pEntries);
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //ALOGI("+++ look probing %d...", pEntry - pHashTable->pEntries);
+    }
+
+    if (pEntry->data == NULL) {
+        if (doAdd) {
+            pEntry->hashValue = itemHash;
+            pEntry->data = item;
+            pHashTable->numEntries++;
+
+            /*
+             * We've added an entry.  See if this brings us too close to full.
+             */
+            if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
+                > pHashTable->tableSize * LOAD_NUMER)
+            {
+                if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
+                    /* don't really have a way to indicate failure */
+                    ALOGE("Dalvik hash resize failure");
+                    dvmAbort();
+                }
+                /* note "pEntry" is now invalid */
+            } else {
+                //ALOGW("okay %d/%d/%d",
+                //    pHashTable->numEntries, pHashTable->tableSize,
+                //    (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
+            }
+
+            /* full table is bad -- search for nonexistent never halts */
+            assert(pHashTable->numEntries < pHashTable->tableSize);
+            result = item;
+        } else {
+            assert(result == NULL);
+        }
+    } else {
+        result = pEntry->data;
+    }
+
+    return result;
+}
+
+/*
+ * Remove an entry from the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 itemHash, void* item)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+
+    assert(pHashTable->tableSize > 0);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data == item) {
+            //ALOGI("+++ stepping on entry %d", pEntry - pHashTable->pEntries);
+            pEntry->data = HASH_TOMBSTONE;
+            pHashTable->numEntries--;
+            pHashTable->numDeadEntries++;
+            return true;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //ALOGI("+++ del probing %d...", pEntry - pHashTable->pEntries);
+    }
+
+    return false;
+}
+
+/*
+ * Scan every entry in the hash table and evaluate it with the specified
+ * indirect function call. If the function returns 1, remove the entry from
+ * the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ *
+ * Returning values other than 0 or 1 will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func)
+{
+    int i, val, tableSize;
+
+    tableSize = pHashTable->tableSize;
+
+    for (i = 0; i < tableSize; i++) {
+        HashEntry* pEnt = &pHashTable->pEntries[i];
+
+        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+            val = (*func)(pEnt->data);
+            if (val == 1) {
+                pEnt->data = HASH_TOMBSTONE;
+                pHashTable->numEntries--;
+                pHashTable->numDeadEntries++;
+            }
+            else if (val != 0) {
+                return val;
+            }
+        }
+    }
+    return 0;
+}
+
+
+/*
+ * Execute a function on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg)
+{
+    int i, val, tableSize;
+
+    tableSize = pHashTable->tableSize;
+
+    for (i = 0; i < tableSize; i++) {
+        HashEntry* pEnt = &pHashTable->pEntries[i];
+
+        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+            val = (*func)(pEnt->data, arg);
+            if (val != 0)
+                return val;
+        }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Look up an entry, counting the number of times we have to probe.
+ *
+ * Returns -1 if the entry wasn't found.
+ */
+static int countProbes(HashTable* pHashTable, u4 itemHash, const void* item,
+    HashCompareFunc cmpFunc)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    int count = 0;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        count++;
+    }
+    if (pEntry->data == NULL)
+        return -1;
+
+    return count;
+}
+
+/*
+ * Evaluate the amount of probing required for the specified hash table.
+ *
+ * We do this by running through all entries in the hash table, computing
+ * the hash value and then doing a lookup.
+ *
+ * The caller should lock the table before calling here.
+ */
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc)
+{
+    int numEntries, minProbe, maxProbe, totalProbe;
+    HashIter iter;
+
+    numEntries = maxProbe = totalProbe = 0;
+    minProbe = 65536*32767;
+
+    for (dvmHashIterBegin(pHashTable, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        const void* data = (const void*)dvmHashIterData(&iter);
+        int count;
+
+        count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
+
+        numEntries++;
+
+        if (count < minProbe)
+            minProbe = count;
+        if (count > maxProbe)
+            maxProbe = count;
+        totalProbe += count;
+    }
+
+    ALOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f",
+        minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
+        (float) totalProbe / (float) numEntries);
+}
diff --git a/vm/Hash.h b/vm/Hash.h
new file mode 100644
index 0000000..38f710f
--- /dev/null
+++ b/vm/Hash.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * General purpose hash table, used for finding classes, methods, etc.
+ *
+ * When the number of elements reaches a certain percentage of the table's
+ * capacity, the table will be resized.
+ */
+#ifndef DALVIK_HASH_H_
+#define DALVIK_HASH_H_
+
+/* compute the hash of an item with a specific type */
+typedef u4 (*HashCompute)(const void* item);
+
+/*
+ * Compare a hash entry with a "loose" item after their hash values match.
+ * Returns { <0, 0, >0 } depending on ordering of items (same semantics
+ * as strcmp()).
+ */
+typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem);
+
+/*
+ * This function will be used to free entries in the table.  This can be
+ * NULL if no free is required, free(), or a custom function.
+ */
+typedef void (*HashFreeFunc)(void* ptr);
+
+/*
+ * Used by dvmHashForeach().
+ */
+typedef int (*HashForeachFunc)(void* data, void* arg);
+
+/*
+ * Used by dvmHashForeachRemove().
+ */
+typedef int (*HashForeachRemoveFunc)(void* data);
+
+/*
+ * One entry in the hash table.  "data" values are expected to be (or have
+ * the same characteristics as) valid pointers.  In particular, a NULL
+ * value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates
+ * a no-longer-used slot that must be stepped over during probing.
+ *
+ * Attempting to add a NULL or tombstone value is an error.
+ *
+ * When an entry is released, we will call (HashFreeFunc)(entry->data).
+ */
+struct HashEntry {
+    u4 hashValue;
+    void* data;
+};
+
+#define HASH_TOMBSTONE ((void*) 0xcbcacccd)     // invalid ptr value
+
+/*
+ * Expandable hash table.
+ *
+ * This structure should be considered opaque.
+ */
+struct HashTable {
+    int         tableSize;          /* must be power of 2 */
+    int         numEntries;         /* current #of "live" entries */
+    int         numDeadEntries;     /* current #of tombstone entries */
+    HashEntry*  pEntries;           /* array on heap */
+    HashFreeFunc freeFunc;
+    pthread_mutex_t lock;
+};
+
+/*
+ * Create and initialize a HashTable structure, using "initialSize" as
+ * a basis for the initial capacity of the table.  (The actual initial
+ * table size may be adjusted upward.)  If you know exactly how many
+ * elements the table will hold, pass the result from dvmHashSize() in.)
+ *
+ * Returns "false" if unable to allocate the table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc);
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.  Use
+ * this when you know ahead of time how many elements the table will hold.
+ * Pass this value into dvmHashTableCreate() to ensure that you can add
+ * all elements without needing to reallocate the table.
+ */
+size_t dvmHashSize(size_t size);
+
+/*
+ * Clear out a hash table, freeing the contents of any used entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable);
+
+/*
+ * Free a hash table.  Performs a "clear" first.
+ */
+void dvmHashTableFree(HashTable* pHashTable);
+
+/*
+ * Exclusive access.  Important when adding items to a table, or when
+ * doing any operations on a table that could be added to by another thread.
+ */
+INLINE void dvmHashTableLock(HashTable* pHashTable) {
+    dvmLockMutex(&pHashTable->lock);
+}
+INLINE void dvmHashTableUnlock(HashTable* pHashTable) {
+    dvmUnlockMutex(&pHashTable->lock);
+}
+
+/*
+ * Get #of entries in hash table.
+ */
+INLINE int dvmHashTableNumEntries(HashTable* pHashTable) {
+    return pHashTable->numEntries;
+}
+
+/*
+ * Get total size of hash table (for memory usage calculations).
+ */
+INLINE int dvmHashTableMemUsage(HashTable* pHashTable) {
+    return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry);
+}
+
+/*
+ * Look up an entry in the table, possibly adding it if it's not there.
+ *
+ * If "item" is not found, and "doAdd" is false, NULL is returned.
+ * Otherwise, a pointer to the found or added item is returned.  (You can
+ * tell the difference by seeing if return value == item.)
+ *
+ * An "add" operation may cause the entire table to be reallocated.  Don't
+ * forget to lock the table before calling this.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd);
+
+/*
+ * Remove an item from the hash table, given its "data" pointer.  Does not
+ * invoke the "free" function; just detaches it from the table.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 hash, void* item);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns 1 detach the entry from the hash table. Does not invoke
+ * the "free" function.
+ *
+ * Returning values other than 0 or 1 from "func" will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func);
+
+/*
+ * An alternative to dvmHashForeach(), using an iterator.
+ *
+ * Use like this:
+ *   HashIter iter;
+ *   for (dvmHashIterBegin(hashTable, &iter); !dvmHashIterDone(&iter);
+ *       dvmHashIterNext(&iter))
+ *   {
+ *       MyData* data = (MyData*)dvmHashIterData(&iter);
+ *   }
+ */
+struct HashIter {
+    void*       data;
+    HashTable*  pHashTable;
+    int         idx;
+};
+INLINE void dvmHashIterNext(HashIter* pIter) {
+    int i = pIter->idx +1;
+    int lim = pIter->pHashTable->tableSize;
+    for ( ; i < lim; i++) {
+        void* data = pIter->pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE)
+            break;
+    }
+    pIter->idx = i;
+}
+INLINE void dvmHashIterBegin(HashTable* pHashTable, HashIter* pIter) {
+    pIter->pHashTable = pHashTable;
+    pIter->idx = -1;
+    dvmHashIterNext(pIter);
+}
+INLINE bool dvmHashIterDone(HashIter* pIter) {
+    return (pIter->idx >= pIter->pHashTable->tableSize);
+}
+INLINE void* dvmHashIterData(HashIter* pIter) {
+    assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize);
+    return pIter->pHashTable->pEntries[pIter->idx].data;
+}
+
+
+/*
+ * Evaluate hash table performance by examining the number of times we
+ * have to probe for an entry.
+ *
+ * The caller should lock the table beforehand.
+ */
+typedef u4 (*HashCalcFunc)(const void* item);
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc);
+
+#endif  // DALVIK_HASH_H_
diff --git a/vm/IndirectRefTable.cpp b/vm/IndirectRefTable.cpp
new file mode 100644
index 0000000..69afccd
--- /dev/null
+++ b/vm/IndirectRefTable.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Indirect reference table management.
+ */
+#include "Dalvik.h"
+
+static void abortMaybe() {
+    // If CheckJNI is on, it'll give a more detailed error before aborting.
+    // Otherwise, we want to abort rather than hand back a bad reference.
+    if (!gDvmJni.useCheckJni) {
+        dvmAbort();
+    }
+}
+
+bool IndirectRefTable::init(size_t initialCount,
+        size_t maxCount, IndirectRefKind desiredKind)
+{
+    assert(initialCount > 0);
+    assert(initialCount <= maxCount);
+    assert(desiredKind != kIndirectKindInvalid);
+
+    table_ = (IndirectRefSlot*) malloc(initialCount * sizeof(IndirectRefSlot));
+    if (table_ == NULL) {
+        return false;
+    }
+    memset(table_, 0xd1, initialCount * sizeof(IndirectRefSlot));
+
+    segmentState.all = IRT_FIRST_SEGMENT;
+    alloc_entries_ = initialCount;
+    max_entries_ = maxCount;
+    kind_ = desiredKind;
+
+    return true;
+}
+
+/*
+ * Clears out the contents of a IndirectRefTable, freeing allocated storage.
+ */
+void IndirectRefTable::destroy()
+{
+    free(table_);
+    table_ = NULL;
+    alloc_entries_ = max_entries_ = -1;
+}
+
+IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    size_t topIndex = segmentState.parts.topIndex;
+
+    assert(obj != NULL);
+    assert(dvmIsHeapAddress(obj));
+    assert(table_ != NULL);
+    assert(alloc_entries_ <= max_entries_);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    /*
+     * We know there's enough room in the table.  Now we just need to find
+     * the right spot.  If there's a hole, find it and fill it; otherwise,
+     * add to the end of the list.
+     */
+    IndirectRef result;
+    IndirectRefSlot* slot;
+    int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
+    if (numHoles > 0) {
+        assert(topIndex > 1);
+        /* find the first hole; likely to be near the end of the list,
+         * we know the item at the topIndex is not a hole */
+        slot = &table_[topIndex - 1];
+        assert(slot->obj != NULL);
+        while ((--slot)->obj != NULL) {
+            assert(slot >= table_ + prevState.parts.topIndex);
+        }
+        segmentState.parts.numHoles--;
+    } else {
+        /* add to the end, grow if needed */
+        if (topIndex == alloc_entries_) {
+            /* reached end of allocated space; did we hit buffer max? */
+            if (topIndex == max_entries_) {
+                ALOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)",
+                        indirectRefKindToString(kind_), max_entries_);
+                return NULL;
+            }
+
+            size_t newSize = alloc_entries_ * 2;
+            if (newSize > max_entries_) {
+                newSize = max_entries_;
+            }
+            assert(newSize > alloc_entries_);
+
+            IndirectRefSlot* newTable =
+                    (IndirectRefSlot*) realloc(table_, newSize * sizeof(IndirectRefSlot));
+            if (table_ == NULL) {
+                ALOGE("JNI ERROR (app bug): unable to expand %s reference table "
+                        "(from %d to %d, max=%d)",
+                        indirectRefKindToString(kind_),
+                        alloc_entries_, newSize, max_entries_);
+                return NULL;
+            }
+
+            memset(newTable + alloc_entries_, 0xd1,
+                   (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
+
+            alloc_entries_ = newSize;
+            table_ = newTable;
+        }
+        slot = &table_[topIndex++];
+        segmentState.parts.topIndex = topIndex;
+    }
+
+    slot->obj = obj;
+    slot->serial = nextSerial(slot->serial);
+    result = toIndirectRef(slot - table_, slot->serial, kind_);
+
+    assert(result != NULL);
+    return result;
+}
+
+/*
+ * Get the referent of an indirect ref from the table.
+ *
+ * Returns kInvalidIndirectRefObject if iref is invalid.
+ */
+Object* IndirectRefTable::get(IndirectRef iref) const {
+    IndirectRefKind kind = indirectRefKind(iref);
+    if (kind != kind_) {
+        if (iref == NULL) {
+            ALOGW("Attempt to look up NULL %s reference", indirectRefKindToString(kind_));
+            return kInvalidIndirectRefObject;
+        }
+        if (kind == kIndirectKindInvalid) {
+            ALOGE("JNI ERROR (app bug): invalid %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            abortMaybe();
+            return kInvalidIndirectRefObject;
+        }
+        // References of the requested kind cannot appear within this table.
+        return kInvalidIndirectRefObject;
+    }
+
+    u4 topIndex = segmentState.parts.topIndex;
+    u4 index = extractIndex(iref);
+    if (index >= topIndex) {
+        /* bad -- stale reference? */
+        ALOGE("JNI ERROR (app bug): accessed stale %s reference %p (index %d in a table of size %d)",
+                indirectRefKindToString(kind_), iref, index, topIndex);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    Object* obj = table_[index].obj;
+    if (obj == NULL) {
+        ALOGI("JNI ERROR (app bug): accessed deleted %s reference %p",
+                indirectRefKindToString(kind_), iref);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    u4 serial = extractSerial(iref);
+    if (serial != table_[index].serial) {
+        ALOGE("JNI ERROR (app bug): attempt to use stale %s reference %p",
+                indirectRefKindToString(kind_), iref);
+        abortMaybe();
+        return kInvalidIndirectRefObject;
+    }
+
+    return obj;
+}
+
+static int findObject(const Object* obj, int bottomIndex, int topIndex,
+        const IndirectRefSlot* table) {
+    for (int i = bottomIndex; i < topIndex; ++i) {
+        if (table[i].obj == obj) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+bool IndirectRefTable::contains(const Object* obj) const {
+    return findObject(obj, 0, segmentState.parts.topIndex, table_) >= 0;
+}
+
+/*
+ * Remove "obj" from "pRef".  We extract the table offset bits from "iref"
+ * and zap the corresponding entry, leaving a hole if it's not at the top.
+ *
+ * If the entry is not between the current top index and the bottom index
+ * specified by the cookie, we don't remove anything.  This is the behavior
+ * required by JNI's DeleteLocalRef function.
+ *
+ * Note this is NOT called when a local frame is popped.  This is only used
+ * for explicit single removals.
+ *
+ * Returns "false" if nothing was removed.
+ */
+bool IndirectRefTable::remove(u4 cookie, IndirectRef iref)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    u4 topIndex = segmentState.parts.topIndex;
+    u4 bottomIndex = prevState.parts.topIndex;
+
+    assert(table_ != NULL);
+    assert(alloc_entries_ <= max_entries_);
+    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    IndirectRefKind kind = indirectRefKind(iref);
+    u4 index;
+    if (kind == kind_) {
+        index = extractIndex(iref);
+        if (index < bottomIndex) {
+            /* wrong segment */
+            ALOGV("Attempt to remove index outside index area (%ud vs %ud-%ud)",
+                    index, bottomIndex, topIndex);
+            return false;
+        }
+        if (index >= topIndex) {
+            /* bad -- stale reference? */
+            ALOGD("Attempt to remove invalid index %ud (bottom=%ud top=%ud)",
+                    index, bottomIndex, topIndex);
+            return false;
+        }
+        if (table_[index].obj == NULL) {
+            ALOGD("Attempt to remove cleared %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            return false;
+        }
+        u4 serial = extractSerial(iref);
+        if (table_[index].serial != serial) {
+            ALOGD("Attempt to remove stale %s reference %p",
+                    indirectRefKindToString(kind_), iref);
+            return false;
+        }
+    } else if (kind == kIndirectKindInvalid && gDvmJni.workAroundAppJniBugs) {
+        // reference looks like a pointer, scan the table to find the index
+        int i = findObject(reinterpret_cast<Object*>(iref), bottomIndex, topIndex, table_);
+        if (i < 0) {
+            ALOGW("trying to work around app JNI bugs, but didn't find %p in table!", iref);
+            return false;
+        }
+        index = i;
+    } else {
+        // References of the requested kind cannot appear within this table.
+        return false;
+    }
+
+    if (index == topIndex - 1) {
+        // Top-most entry.  Scan up and consume holes.
+        int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
+        if (numHoles != 0) {
+            while (--topIndex > bottomIndex && numHoles != 0) {
+                ALOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p",
+                    topIndex-1, cookie, table_[topIndex-1].obj);
+                if (table_[topIndex-1].obj != NULL) {
+                    break;
+                }
+                ALOGV("+++ ate hole at %d", topIndex-1);
+                numHoles--;
+            }
+            segmentState.parts.numHoles = numHoles + prevState.parts.numHoles;
+            segmentState.parts.topIndex = topIndex;
+        } else {
+            segmentState.parts.topIndex = topIndex-1;
+            ALOGV("+++ ate last entry %d", topIndex-1);
+        }
+    } else {
+        /*
+         * Not the top-most entry.  This creates a hole.  We NULL out the
+         * entry to prevent somebody from deleting it twice and screwing up
+         * the hole count.
+         */
+        table_[index].obj = NULL;
+        segmentState.parts.numHoles++;
+        ALOGV("+++ left hole at %d, holes=%d", index, segmentState.parts.numHoles);
+    }
+
+    return true;
+}
+
+const char* indirectRefKindToString(IndirectRefKind kind)
+{
+    switch (kind) {
+    case kIndirectKindInvalid:      return "invalid";
+    case kIndirectKindLocal:        return "local";
+    case kIndirectKindGlobal:       return "global";
+    case kIndirectKindWeakGlobal:   return "weak global";
+    default:                        return "UNKNOWN";
+    }
+}
+
+void IndirectRefTable::dump(const char* descr) const
+{
+    size_t count = capacity();
+    Object** copy = new Object*[count];
+    for (size_t i = 0; i < count; i++) {
+        copy[i] = table_[i].obj;
+    }
+    dvmDumpReferenceTableContents(copy, count, descr);
+    delete[] copy;
+}
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
new file mode 100644
index 0000000..9172ada
--- /dev/null
+++ b/vm/IndirectRefTable.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_INDIRECTREFTABLE_H_
+#define DALVIK_INDIRECTREFTABLE_H_
+
+/*
+ * Maintain a table of indirect references.  Used for local/global JNI
+ * references.
+ *
+ * The table contains object references that are part of the GC root set.
+ * When an object is added we return an IndirectRef that is not a valid
+ * pointer but can be used to find the original value in O(1) time.
+ * Conversions to and from indirect refs are performed on JNI method calls
+ * in and out of the VM, so they need to be very fast.
+ *
+ * To be efficient for JNI local variable storage, we need to provide
+ * operations that allow us to operate on segments of the table, where
+ * segments are pushed and popped as if on a stack.  For example, deletion
+ * of an entry should only succeed if it appears in the current segment,
+ * and we want to be able to strip off the current segment quickly when
+ * a method returns.  Additions to the table must be made in the current
+ * segment even if space is available in an earlier area.
+ *
+ * A new segment is created when we call into native code from interpreted
+ * code, or when we handle the JNI PushLocalFrame function.
+ *
+ * The GC must be able to scan the entire table quickly.
+ *
+ * In summary, these must be very fast:
+ *  - adding or removing a segment
+ *  - adding references to a new segment
+ *  - converting an indirect reference back to an Object
+ * These can be a little slower, but must still be pretty quick:
+ *  - adding references to a "mature" segment
+ *  - removing individual references
+ *  - scanning the entire table straight through
+ *
+ * If there's more than one segment, we don't guarantee that the table
+ * will fill completely before we fail due to lack of space.  We do ensure
+ * that the current segment will pack tightly, which should satisfy JNI
+ * requirements (e.g. EnsureLocalCapacity).
+ *
+ * To make everything fit nicely in 32-bit integers, the maximum size of
+ * the table is capped at 64K.
+ *
+ * None of the table functions are synchronized.
+ */
+
+/*
+ * Indirect reference definition.  This must be interchangeable with JNI's
+ * jobject, and it's convenient to let null be null, so we use void*.
+ *
+ * We need a 16-bit table index and a 2-bit reference type (global, local,
+ * weak global).  Real object pointers will have zeroes in the low 2 or 3
+ * bits (4- or 8-byte alignment), so it's useful to put the ref type
+ * in the low bits and reserve zero as an invalid value.
+ *
+ * The remaining 14 bits can be used to detect stale indirect references.
+ * For example, if objects don't move, we can use a hash of the original
+ * Object* to make sure the entry hasn't been re-used.  (If the Object*
+ * we find there doesn't match because of heap movement, we could do a
+ * secondary check on the preserved hash value; this implies that creating
+ * a global/local ref queries the hash value and forces it to be saved.)
+ *
+ * A more rigorous approach would be to put a serial number in the extra
+ * bits, and keep a copy of the serial number in a parallel table.  This is
+ * easier when objects can move, but requires 2x the memory and additional
+ * memory accesses on add/get.  It will catch additional problems, e.g.:
+ * create iref1 for obj, delete iref1, create iref2 for same obj, lookup
+ * iref1.  A pattern based on object bits will miss this.
+ *
+ * For now, we use a serial number.
+ */
+typedef void* IndirectRef;
+
+/* magic failure value; must not pass dvmIsHeapAddress() */
+#define kInvalidIndirectRefObject reinterpret_cast<Object*>(0xdead4321)
+
+#define kClearedJniWeakGlobal reinterpret_cast<Object*>(0xdead1234)
+
+/*
+ * Indirect reference kind, used as the two low bits of IndirectRef.
+ *
+ * For convenience these match up with enum jobjectRefType from jni.h.
+ */
+enum IndirectRefKind {
+    kIndirectKindInvalid    = 0,
+    kIndirectKindLocal      = 1,
+    kIndirectKindGlobal     = 2,
+    kIndirectKindWeakGlobal = 3
+};
+const char* indirectRefKindToString(IndirectRefKind kind);
+
+/*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind indirectRefKind(IndirectRef iref)
+{
+    return (IndirectRefKind)((u4) iref & 0x03);
+}
+
+/*
+ * Information we store for each slot in the reference table.
+ */
+struct IndirectRefSlot {
+    Object* obj;        /* object pointer itself, NULL if the slot is unused */
+    u4      serial;     /* slot serial number */
+};
+
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT   0
+
+/*
+ * Table definition.
+ *
+ * For the global reference table, the expected common operations are
+ * adding a new entry and removing a recently-added entry (usually the
+ * most-recently-added entry).  For JNI local references, the common
+ * operations are adding a new entry and removing an entire table segment.
+ *
+ * If "alloc_entries_" is not equal to "max_entries_", the table may expand
+ * when entries are added, which means the memory may move.  If you want
+ * to keep pointers into "table" rather than offsets, you must use a
+ * fixed-size table.
+ *
+ * If we delete entries from the middle of the list, we will be left with
+ * "holes".  We track the number of holes so that, when adding new elements,
+ * we can quickly decide to do a trivial append or go slot-hunting.
+ *
+ * When the top-most entry is removed, any holes immediately below it are
+ * also removed.  Thus, deletion of an entry may reduce "topIndex" by more
+ * than one.
+ *
+ * To get the desired behavior for JNI locals, we need to know the bottom
+ * and top of the current "segment".  The top is managed internally, and
+ * the bottom is passed in as a function argument (the VM keeps it in a
+ * slot in the interpreted stack frame).  When we call a native method or
+ * push a local frame, the current top index gets pushed on, and serves
+ * as the new bottom.  When we pop a frame off, the value from the stack
+ * becomes the new top index, and the value stored in the previous frame
+ * becomes the new bottom.
+ *
+ * To avoid having to re-scan the table after a pop, we want to push the
+ * number of holes in the table onto the stack.  Because of our 64K-entry
+ * cap, we can combine the two into a single unsigned 32-bit value.
+ * Instead of a "bottom" argument we take a "cookie", which includes the
+ * bottom index and the count of holes below the bottom.
+ *
+ * We need to minimize method call/return overhead.  If we store the
+ * "cookie" externally, on the interpreted call stack, the VM can handle
+ * pushes and pops with a single 4-byte load and store.  (We could also
+ * store it internally in a public structure, but the local JNI refs are
+ * logically tied to interpreted stack frames anyway.)
+ *
+ * Common alternative implementation: make IndirectRef a pointer to the
+ * actual reference slot.  Instead of getting a table and doing a lookup,
+ * the lookup can be done instantly.  Operations like determining the
+ * type and deleting the reference are more expensive because the table
+ * must be hunted for (i.e. you have to do a pointer comparison to see
+ * which table it's in), you can't move the table when expanding it (so
+ * realloc() is out), and tricks like serial number checking to detect
+ * stale references aren't possible (though we may be able to get similar
+ * benefits with other approaches).
+ *
+ * TODO: consider a "lastDeleteIndex" for quick hole-filling when an
+ * add immediately follows a delete; must invalidate after segment pop
+ * (which could increase the cost/complexity of method call/return).
+ * Might be worth only using it for JNI globals.
+ *
+ * TODO: may want completely different add/remove algorithms for global
+ * and local refs to improve performance.  A large circular buffer might
+ * reduce the amortized cost of adding global references.
+ *
+ * TODO: if we can guarantee that the underlying storage doesn't move,
+ * e.g. by using oversized mmap regions to handle expanding tables, we may
+ * be able to avoid having to synchronize lookups.  Might make sense to
+ * add a "synchronized lookup" call that takes the mutex as an argument,
+ * and either locks or doesn't lock based on internal details.
+ */
+union IRTSegmentState {
+    u4          all;
+    struct {
+        u4      topIndex:16;            /* index of first unused entry */
+        u4      numHoles:16;            /* #of holes in entire table */
+    } parts;
+};
+
+class iref_iterator {
+public:
+    explicit iref_iterator(IndirectRefSlot* table, size_t i, size_t capacity) :
+            table_(table), i_(i), capacity_(capacity) {
+        skipNullsAndTombstones();
+    }
+
+    iref_iterator& operator++() {
+        ++i_;
+        skipNullsAndTombstones();
+        return *this;
+    }
+
+    Object** operator*() {
+        return &table_[i_].obj;
+    }
+
+    bool equals(const iref_iterator& rhs) const {
+        return (i_ == rhs.i_ && table_ == rhs.table_);
+    }
+
+private:
+    void skipNullsAndTombstones() {
+        // We skip NULLs and tombstones. Clients don't want to see implementation details.
+        while (i_ < capacity_ && (table_[i_].obj == NULL
+                || table_[i_].obj == kClearedJniWeakGlobal)) {
+            ++i_;
+        }
+    }
+
+    IndirectRefSlot* table_;
+    size_t i_;
+    size_t capacity_;
+};
+
+bool inline operator!=(const iref_iterator& lhs, const iref_iterator& rhs) {
+    return !lhs.equals(rhs);
+}
+
+struct IndirectRefTable {
+public:
+    typedef iref_iterator iterator;
+
+    /* semi-public - read/write by interpreter in native call handler */
+    IRTSegmentState segmentState;
+
+    /*
+     * private:
+     *
+     * TODO: we can't make these private as long as the interpreter
+     * uses offsetof, since private member data makes us non-POD.
+     */
+    /* bottom of the stack */
+    IndirectRefSlot* table_;
+    /* bit mask, ORed into all irefs */
+    IndirectRefKind kind_;
+    /* #of entries we have space for */
+    size_t          alloc_entries_;
+    /* max #of entries allowed */
+    size_t          max_entries_;
+
+    // TODO: want hole-filling stats (#of holes filled, total entries scanned)
+    //       for performance evaluation.
+
+    /*
+     * Add a new entry.  "obj" must be a valid non-NULL object reference
+     * (though it's okay if it's not fully-formed, e.g. the result from
+     * dvmMalloc doesn't have obj->clazz set).
+     *
+     * Returns NULL if the table is full (max entries reached, or alloc
+     * failed during expansion).
+     */
+    IndirectRef add(u4 cookie, Object* obj);
+
+    /*
+     * Given an IndirectRef in the table, return the Object it refers to.
+     *
+     * Returns kInvalidIndirectRefObject if iref is invalid.
+     */
+    Object* get(IndirectRef iref) const;
+
+    /*
+     * Returns true if the table contains a reference to this object.
+     */
+    bool contains(const Object* obj) const;
+
+    /*
+     * Remove an existing entry.
+     *
+     * If the entry is not between the current top index and the bottom index
+     * specified by the cookie, we don't remove anything.  This is the behavior
+     * required by JNI's DeleteLocalRef function.
+     *
+     * Returns "false" if nothing was removed.
+     */
+    bool remove(u4 cookie, IndirectRef iref);
+
+    /*
+     * Initialize an IndirectRefTable.
+     *
+     * If "initialCount" != "maxCount", the table will expand as required.
+     *
+     * "kind" should be Local or Global.  The Global table may also hold
+     * WeakGlobal refs.
+     *
+     * Returns "false" if table allocation fails.
+     */
+    bool init(size_t initialCount, size_t maxCount, IndirectRefKind kind);
+
+    /*
+     * Clear out the contents, freeing allocated storage.
+     *
+     * You must call dvmInitReferenceTable() before you can re-use this table.
+     *
+     * TODO: this should be a destructor.
+     */
+    void destroy();
+
+    /*
+     * Dump the contents of a reference table to the log file.
+     *
+     * The caller should lock any external sync before calling.
+     *
+     * TODO: we should name the table in a constructor and remove
+     * the argument here.
+     */
+    void dump(const char* descr) const;
+
+    /*
+     * Return the #of entries in the entire table.  This includes holes, and
+     * so may be larger than the actual number of "live" entries.
+     */
+    size_t capacity() const {
+        return segmentState.parts.topIndex;
+    }
+
+    iterator begin() {
+        return iterator(table_, 0, capacity());
+    }
+
+    iterator end() {
+        return iterator(table_, capacity(), capacity());
+    }
+
+private:
+    static inline u4 extractIndex(IndirectRef iref) {
+        u4 uref = (u4) iref;
+        return (uref >> 2) & 0xffff;
+    }
+
+    static inline u4 extractSerial(IndirectRef iref) {
+        u4 uref = (u4) iref;
+        return uref >> 20;
+    }
+
+    static inline u4 nextSerial(u4 serial) {
+        return (serial + 1) & 0xfff;
+    }
+
+    static inline IndirectRef toIndirectRef(u4 index, u4 serial, IndirectRefKind kind) {
+        assert(index < 65536);
+        return reinterpret_cast<IndirectRef>((serial << 20) | (index << 2) | kind);
+    }
+};
+
+#endif  // DALVIK_INDIRECTREFTABLE_H_
diff --git a/vm/Init.cpp b/vm/Init.cpp
new file mode 100644
index 0000000..ccd5115
--- /dev/null
+++ b/vm/Init.cpp
@@ -0,0 +1,2166 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik initialization, shutdown, and command-line argument processing.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <linux/fs.h>
+#include <cutils/fs.h>
+#include <unistd.h>
+#ifdef HAVE_ANDROID_OS
+#include <sys/prctl.h>
+#endif
+
+#include "Dalvik.h"
+#include "test/Test.h"
+#include "mterp/Mterp.h"
+#include "Hash.h"
+#include "JniConstants.h"
+
+#if defined(WITH_JIT)
+#include "compiler/codegen/Optimizer.h"
+#endif
+
+#define kMinHeapStartSize   (1*1024*1024)
+#define kMinHeapSize        (2*1024*1024)
+#define kMaxHeapSize        (1*1024*1024*1024)
+
+/*
+ * Register VM-agnostic native methods for system classes.
+ */
+extern int jniRegisterSystemMethods(JNIEnv* env);
+
+/* fwd */
+static bool registerSystemNatives(JNIEnv* pEnv);
+static bool initJdwp();
+static bool initZygote();
+
+
+/* global state */
+struct DvmGlobals gDvm;
+struct DvmJniGlobals gDvmJni;
+
+/* JIT-specific global state */
+#if defined(WITH_JIT)
+struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+/*
+ * Track the number of hits in the inline cache for predicted chaining.
+ * Use an ugly global variable here since it is accessed in assembly code.
+ */
+int gDvmICHitCount;
+#endif
+
+#endif
+
+/*
+ * Show usage.
+ *
+ * We follow the tradition of unhyphenated compound words.
+ */
+static void usage()
+{
+    const char* progName = "dalvikvm";
+    dvmFprintf(stderr, "%s: [options] class [argument ...]\n", progName);
+    dvmFprintf(stderr, "%s: [options] -jar file.jar [argument ...]\n",progName);
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "The following standard options are recognized:\n");
+    dvmFprintf(stderr, "  -classpath classpath\n");
+    dvmFprintf(stderr, "  -Dproperty=value\n");
+    dvmFprintf(stderr, "  -verbose:tag  ('gc', 'jni', or 'class')\n");
+    dvmFprintf(stderr, "  -ea[:<package name>... |:<class name>]\n");
+    dvmFprintf(stderr, "  -da[:<package name>... |:<class name>]\n");
+    dvmFprintf(stderr, "   (-enableassertions, -disableassertions)\n");
+    dvmFprintf(stderr, "  -esa\n");
+    dvmFprintf(stderr, "  -dsa\n");
+    dvmFprintf(stderr,
+                "   (-enablesystemassertions, -disablesystemassertions)\n");
+    dvmFprintf(stderr, "  -showversion\n");
+    dvmFprintf(stderr, "  -help\n");
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "The following extended options are recognized:\n");
+    dvmFprintf(stderr, "  -Xrunjdwp:<options>\n");
+    dvmFprintf(stderr, "  -Xbootclasspath:bootclasspath\n");
+    dvmFprintf(stderr, "  -Xcheck:tag  (e.g. 'jni')\n");
+    dvmFprintf(stderr, "  -XmsN  (min heap, must be multiple of 1K, >= 1MB)\n");
+    dvmFprintf(stderr, "  -XmxN  (max heap, must be multiple of 1K, >= 2MB)\n");
+    dvmFprintf(stderr, "  -XssN  (stack size, >= %dKB, <= %dKB)\n",
+        kMinStackSize / 1024, kMaxStackSize / 1024);
+    dvmFprintf(stderr, "  -Xverify:{none,remote,all}\n");
+    dvmFprintf(stderr, "  -Xrs\n");
+#if defined(WITH_JIT)
+    dvmFprintf(stderr,
+                "  -Xint  (extended to accept ':portable', ':fast' and ':jit')\n");
+#else
+    dvmFprintf(stderr,
+                "  -Xint  (extended to accept ':portable' and ':fast')\n");
+#endif
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "These are unique to Dalvik:\n");
+    dvmFprintf(stderr, "  -Xzygote\n");
+    dvmFprintf(stderr, "  -Xdexopt:{none,verified,all,full}\n");
+    dvmFprintf(stderr, "  -Xnoquithandler\n");
+    dvmFprintf(stderr, "  -Xjniopts:{warnonly,forcecopy}\n");
+    dvmFprintf(stderr, "  -Xjnitrace:substring (eg NativeClass or nativeMethod)\n");
+    dvmFprintf(stderr, "  -Xstacktracefile:<filename>\n");
+    dvmFprintf(stderr, "  -Xgc:[no]precise\n");
+    dvmFprintf(stderr, "  -Xgc:[no]preverify\n");
+    dvmFprintf(stderr, "  -Xgc:[no]postverify\n");
+    dvmFprintf(stderr, "  -Xgc:[no]concurrent\n");
+    dvmFprintf(stderr, "  -Xgc:[no]verifycardtable\n");
+    dvmFprintf(stderr, "  -XX:+DisableExplicitGC\n");
+    dvmFprintf(stderr, "  -X[no]genregmap\n");
+    dvmFprintf(stderr, "  -Xverifyopt:[no]checkmon\n");
+    dvmFprintf(stderr, "  -Xcheckdexsum\n");
+#if defined(WITH_JIT)
+    dvmFprintf(stderr, "  -Xincludeselectedop\n");
+    dvmFprintf(stderr, "  -Xjitop:hexopvalue[-endvalue]"
+                       "[,hexopvalue[-endvalue]]*\n");
+    dvmFprintf(stderr, "  -Xincludeselectedmethod\n");
+    dvmFprintf(stderr, "  -Xjitthreshold:decimalvalue\n");
+    dvmFprintf(stderr, "  -Xjitcodecachesize:decimalvalueofkbytes\n");
+    dvmFprintf(stderr, "  -Xjitblocking\n");
+    dvmFprintf(stderr, "  -Xjitmethod:signature[,signature]* "
+                       "(eg Ljava/lang/String\\;replace)\n");
+    dvmFprintf(stderr, "  -Xjitclass:classname[,classname]*\n");
+    dvmFprintf(stderr, "  -Xjitoffset:offset[,offset]\n");
+    dvmFprintf(stderr, "  -Xjitconfig:filename\n");
+    dvmFprintf(stderr, "  -Xjitcheckcg\n");
+    dvmFprintf(stderr, "  -Xjitverbose\n");
+    dvmFprintf(stderr, "  -Xjitprofile\n");
+    dvmFprintf(stderr, "  -Xjitdisableopt\n");
+    dvmFprintf(stderr, "  -Xjitsuspendpoll\n");
+#endif
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "Configured with:"
+        " debugger"
+        " profiler"
+        " hprof"
+#ifdef WITH_TRACKREF_CHECKS
+        " trackref_checks"
+#endif
+#ifdef WITH_INSTR_CHECKS
+        " instr_checks"
+#endif
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+        " extra_object_validation"
+#endif
+#ifdef WITH_EXTRA_GC_CHECKS
+        " extra_gc_checks"
+#endif
+#if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+        " dalvik_assert"
+#endif
+#ifdef WITH_JNI_STACK_CHECK
+        " jni_stack_check"
+#endif
+#ifdef EASY_GDB
+        " easy_gdb"
+#endif
+#ifdef CHECK_MUTEX
+        " check_mutex"
+#endif
+#if defined(WITH_JIT)
+        " jit(" ARCH_VARIANT ")"
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+        " self_verification"
+#endif
+#if ANDROID_SMP != 0
+        " smp"
+#endif
+    );
+#ifdef DVM_SHOW_EXCEPTION
+    dvmFprintf(stderr, " show_exception=%d", DVM_SHOW_EXCEPTION);
+#endif
+    dvmFprintf(stderr, "\n\n");
+}
+
+/*
+ * Show helpful information on JDWP options.
+ */
+static void showJdwpHelp()
+{
+    dvmFprintf(stderr,
+        "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n");
+    dvmFprintf(stderr,
+        "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+}
+
+/*
+ * Show version and copyright info.
+ */
+static void showVersion()
+{
+    dvmFprintf(stdout, "DalvikVM version %d.%d.%d\n",
+        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    dvmFprintf(stdout,
+        "Copyright (C) 2007 The Android Open Source Project\n\n"
+        "This software is built from source code licensed under the "
+        "Apache License,\n"
+        "Version 2.0 (the \"License\"). You may obtain a copy of the "
+        "License at\n\n"
+        "     http://www.apache.org/licenses/LICENSE-2.0\n\n"
+        "See the associated NOTICE file for this software for further "
+        "details.\n");
+}
+
+/*
+ * Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+ * memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
+ * [gG] gigabytes.
+ *
+ * "s" should point just past the "-Xm?" part of the string.
+ * "min" specifies the lowest acceptable value described by "s".
+ * "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+ * of 1024.
+ *
+ * The spec says the -Xmx and -Xms options must be multiples of 1024.  It
+ * doesn't say anything about -Xss.
+ *
+ * Returns 0 (a useless size) if "s" is malformed or specifies a low or
+ * non-evenly-divisible value.
+ */
+static size_t parseMemOption(const char* s, size_t div)
+{
+    /* strtoul accepts a leading [+-], which we don't want,
+     * so make sure our string starts with a decimal digit.
+     */
+    if (isdigit(*s)) {
+        const char* s2;
+        size_t val;
+
+        val = strtoul(s, (char* *)&s2, 10);
+        if (s2 != s) {
+            /* s2 should be pointing just after the number.
+             * If this is the end of the string, the user
+             * has specified a number of bytes.  Otherwise,
+             * there should be exactly one more character
+             * that specifies a multiplier.
+             */
+            if (*s2 != '\0') {
+                char c;
+
+                /* The remainder of the string is either a single multiplier
+                 * character, or nothing to indicate that the value is in
+                 * bytes.
+                 */
+                c = *s2++;
+                if (*s2 == '\0') {
+                    size_t mul;
+
+                    if (c == '\0') {
+                        mul = 1;
+                    } else if (c == 'k' || c == 'K') {
+                        mul = 1024;
+                    } else if (c == 'm' || c == 'M') {
+                        mul = 1024 * 1024;
+                    } else if (c == 'g' || c == 'G') {
+                        mul = 1024 * 1024 * 1024;
+                    } else {
+                        /* Unknown multiplier character.
+                         */
+                        return 0;
+                    }
+
+                    if (val <= SIZE_MAX / mul) {
+                        val *= mul;
+                    } else {
+                        /* Clamp to a multiple of 1024.
+                         */
+                        val = SIZE_MAX & ~(1024-1);
+                    }
+                } else {
+                    /* There's more than one character after the
+                     * numeric part.
+                     */
+                    return 0;
+                }
+            }
+
+            /* The man page says that a -Xm value must be
+             * a multiple of 1024.
+             */
+            if (val % div == 0) {
+                return val;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Handle one of the JDWP name/value pairs.
+ *
+ * JDWP options are:
+ *  help: if specified, show help message and bail
+ *  transport: may be dt_socket or dt_shmem
+ *  address: for dt_socket, "host:port", or just "port" when listening
+ *  server: if "y", wait for debugger to attach; if "n", attach to debugger
+ *  timeout: how long to wait for debugger to connect / listen
+ *
+ * Useful with server=n (these aren't supported yet):
+ *  onthrow=<exception-name>: connect to debugger when exception thrown
+ *  onuncaught=y|n: connect to debugger when uncaught exception thrown
+ *  launch=<command-line>: launch the debugger itself
+ *
+ * The "transport" option is required, as is "address" if server=n.
+ */
+static bool handleJdwpOption(const char* name, const char* value)
+{
+    if (strcmp(name, "transport") == 0) {
+        if (strcmp(value, "dt_socket") == 0) {
+            gDvm.jdwpTransport = kJdwpTransportSocket;
+        } else if (strcmp(value, "dt_android_adb") == 0) {
+            gDvm.jdwpTransport = kJdwpTransportAndroidAdb;
+        } else {
+            ALOGE("JDWP transport '%s' not supported", value);
+            return false;
+        }
+    } else if (strcmp(name, "server") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpServer = false;
+        else if (*value == 'y')
+            gDvm.jdwpServer = true;
+        else {
+            ALOGE("JDWP option 'server' must be 'y' or 'n'");
+            return false;
+        }
+    } else if (strcmp(name, "suspend") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpSuspend = false;
+        else if (*value == 'y')
+            gDvm.jdwpSuspend = true;
+        else {
+            ALOGE("JDWP option 'suspend' must be 'y' or 'n'");
+            return false;
+        }
+    } else if (strcmp(name, "address") == 0) {
+        /* this is either <port> or <host>:<port> */
+        const char* colon = strchr(value, ':');
+        char* end;
+        long port;
+
+        if (colon != NULL) {
+            free(gDvm.jdwpHost);
+            gDvm.jdwpHost = (char*) malloc(colon - value +1);
+            strncpy(gDvm.jdwpHost, value, colon - value +1);
+            gDvm.jdwpHost[colon-value] = '\0';
+            value = colon + 1;
+        }
+        if (*value == '\0') {
+            ALOGE("JDWP address missing port");
+            return false;
+        }
+        port = strtol(value, &end, 10);
+        if (*end != '\0') {
+            ALOGE("JDWP address has junk in port field '%s'", value);
+            return false;
+        }
+        gDvm.jdwpPort = port;
+    } else if (strcmp(name, "launch") == 0 ||
+               strcmp(name, "onthrow") == 0 ||
+               strcmp(name, "oncaught") == 0 ||
+               strcmp(name, "timeout") == 0)
+    {
+        /* valid but unsupported */
+        ALOGI("Ignoring JDWP option '%s'='%s'", name, value);
+    } else {
+        ALOGI("Ignoring unrecognized JDWP option '%s'='%s'", name, value);
+    }
+
+    return true;
+}
+
+/*
+ * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
+ * "transport=dt_socket,address=8000,server=y,suspend=n"
+ */
+static bool parseJdwpOptions(const char* str)
+{
+    char* mangle = strdup(str);
+    char* name = mangle;
+    bool result = false;
+
+    /*
+     * Process all of the name=value pairs.
+     */
+    while (true) {
+        char* value;
+        char* comma;
+
+        value = strchr(name, '=');
+        if (value == NULL) {
+            ALOGE("JDWP opts: garbage at '%s'", name);
+            goto bail;
+        }
+
+        comma = strchr(name, ',');      // use name, not value, for safety
+        if (comma != NULL) {
+            if (comma < value) {
+                ALOGE("JDWP opts: found comma before '=' in '%s'", mangle);
+                goto bail;
+            }
+            *comma = '\0';
+        }
+
+        *value++ = '\0';        // stomp the '='
+
+        if (!handleJdwpOption(name, value))
+            goto bail;
+
+        if (comma == NULL) {
+            /* out of options */
+            break;
+        }
+        name = comma+1;
+    }
+
+    /*
+     * Make sure the combination of arguments makes sense.
+     */
+    if (gDvm.jdwpTransport == kJdwpTransportUnknown) {
+        ALOGE("JDWP opts: must specify transport");
+        goto bail;
+    }
+    if (!gDvm.jdwpServer && (gDvm.jdwpHost == NULL || gDvm.jdwpPort == 0)) {
+        ALOGE("JDWP opts: when server=n, must specify host and port");
+        goto bail;
+    }
+    // transport mandatory
+    // outbound server address
+
+    gDvm.jdwpConfigured = true;
+    result = true;
+
+bail:
+    free(mangle);
+    return result;
+}
+
+/*
+ * Handle one of the four kinds of assertion arguments.
+ *
+ * "pkgOrClass" is the last part of an enable/disable line.  For a package
+ * the arg looks like "-ea:com.google.fubar...", for a class it looks
+ * like "-ea:com.google.fubar.Wahoo".  The string we get starts at the ':'.
+ *
+ * For system assertions (-esa/-dsa), "pkgOrClass" is NULL.
+ *
+ * Multiple instances of these arguments can be specified, e.g. you can
+ * enable assertions for a package and then disable them for one class in
+ * the package.
+ */
+static bool enableAssertions(const char* pkgOrClass, bool enable)
+{
+    AssertionControl* pCtrl = &gDvm.assertionCtrl[gDvm.assertionCtrlCount++];
+    pCtrl->enable = enable;
+
+    if (pkgOrClass == NULL) {
+        /* enable or disable for all system classes */
+        pCtrl->isPackage = false;
+        pCtrl->pkgOrClass = NULL;
+        pCtrl->pkgOrClassLen = 0;
+    } else {
+        if (*pkgOrClass == '\0') {
+            /* global enable/disable for all but system */
+            pCtrl->isPackage = false;
+            pCtrl->pkgOrClass = strdup("");
+            pCtrl->pkgOrClassLen = 0;
+        } else {
+            pCtrl->pkgOrClass = dvmDotToSlash(pkgOrClass+1);    // skip ':'
+            if (pCtrl->pkgOrClass == NULL) {
+                /* can happen if class name includes an illegal '/' */
+                ALOGW("Unable to process assertion arg '%s'", pkgOrClass);
+                return false;
+            }
+
+            int len = strlen(pCtrl->pkgOrClass);
+            if (len >= 3 && strcmp(pCtrl->pkgOrClass + len-3, "///") == 0) {
+                /* mark as package, truncate two of the three slashes */
+                pCtrl->isPackage = true;
+                *(pCtrl->pkgOrClass + len-2) = '\0';
+                pCtrl->pkgOrClassLen = len - 2;
+            } else {
+                /* just a class */
+                pCtrl->isPackage = false;
+                pCtrl->pkgOrClassLen = len;
+            }
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Turn assertions on when requested to do so by the Zygote.
+ *
+ * This is a bit sketchy.  We can't (easily) go back and fiddle with all
+ * of the classes that have already been initialized, so this only
+ * affects classes that have yet to be loaded.  If some or all assertions
+ * have been enabled through some other means, we don't want to mess with
+ * it here, so we do nothing.  Finally, we assume that there's room in
+ * "assertionCtrl" to hold at least one entry; this is guaranteed by the
+ * allocator.
+ *
+ * This must only be called from the main thread during zygote init.
+ */
+void dvmLateEnableAssertions()
+{
+    if (gDvm.assertionCtrl == NULL) {
+        ALOGD("Not late-enabling assertions: no assertionCtrl array");
+        return;
+    } else if (gDvm.assertionCtrlCount != 0) {
+        ALOGD("Not late-enabling assertions: some asserts already configured");
+        return;
+    }
+    ALOGD("Late-enabling assertions");
+
+    /* global enable for all but system */
+    AssertionControl* pCtrl = gDvm.assertionCtrl;
+    pCtrl->pkgOrClass = strdup("");
+    pCtrl->pkgOrClassLen = 0;
+    pCtrl->isPackage = false;
+    pCtrl->enable = true;
+    gDvm.assertionCtrlCount = 1;
+}
+
+
+/*
+ * Release memory associated with the AssertionCtrl array.
+ */
+static void freeAssertionCtrl()
+{
+    int i;
+
+    for (i = 0; i < gDvm.assertionCtrlCount; i++)
+        free(gDvm.assertionCtrl[i].pkgOrClass);
+    free(gDvm.assertionCtrl);
+}
+
+#if defined(WITH_JIT)
+/* Parse -Xjitop to selectively turn on/off certain opcodes for JIT */
+static void processXjitop(const char* opt)
+{
+    if (opt[7] == ':') {
+        const char* startPtr = &opt[8];
+        char* endPtr = NULL;
+
+        do {
+            long startValue, endValue;
+
+            startValue = strtol(startPtr, &endPtr, 16);
+            if (startPtr != endPtr) {
+                /* Just in case value is out of range */
+                startValue %= kNumPackedOpcodes;
+
+                if (*endPtr == '-') {
+                    endValue = strtol(endPtr+1, &endPtr, 16);
+                    endValue %= kNumPackedOpcodes;
+                } else {
+                    endValue = startValue;
+                }
+
+                for (; startValue <= endValue; startValue++) {
+                    ALOGW("Dalvik opcode %x is selected for debugging",
+                         (unsigned int) startValue);
+                    /* Mark the corresponding bit to 1 */
+                    gDvmJit.opList[startValue >> 3] |= 1 << (startValue & 0x7);
+                }
+
+                if (*endPtr == 0) {
+                    break;
+                }
+
+                startPtr = endPtr + 1;
+
+                continue;
+            } else {
+                if (*endPtr != 0) {
+                    dvmFprintf(stderr,
+                        "Warning: Unrecognized opcode value substring "
+                        "%s\n", endPtr);
+                }
+                break;
+            }
+        } while (1);
+    } else {
+        int i;
+        for (i = 0; i < (kNumPackedOpcodes+7)/8; i++) {
+            gDvmJit.opList[i] = 0xff;
+        }
+        dvmFprintf(stderr, "Warning: select all opcodes\n");
+    }
+}
+
+/* Parse -Xjitoffset to selectively turn on/off traces with certain offsets for JIT */
+static void processXjitoffset(const char* opt) {
+    gDvmJit.num_entries_pcTable = 0;
+    char* buf = strdup(opt);
+    char* start, *end;
+    start = buf;
+    int idx = 0;
+    do {
+        end = strchr(start, ',');
+        if (end) {
+            *end = 0;
+        }
+
+        dvmFprintf(stderr, "processXjitoffset start = %s\n", start);
+        char* tmp = strdup(start);
+        gDvmJit.pcTable[idx++] = atoi(tmp);
+        free(tmp);
+        if (idx >= COMPILER_PC_OFFSET_SIZE) {
+            dvmFprintf(stderr, "processXjitoffset: ignore entries beyond %d\n", COMPILER_PC_OFFSET_SIZE);
+            break;
+        }
+        if (end) {
+            start = end + 1;
+        } else {
+            break;
+        }
+    } while (1);
+    gDvmJit.num_entries_pcTable = idx;
+    free(buf);
+}
+
+/* Parse -Xjitmethod to selectively turn on/off certain methods for JIT */
+static void processXjitmethod(const char* opt, bool isMethod) {
+    char* buf = strdup(opt);
+
+    if (isMethod && gDvmJit.methodTable == NULL) {
+        gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+    }
+    if (!isMethod && gDvmJit.classTable == NULL) {
+        gDvmJit.classTable = dvmHashTableCreate(8, NULL);
+    }
+
+    char* start = buf;
+    char* end;
+    /*
+     * Break comma-separated method signatures and enter them into the hash
+     * table individually.
+     */
+    do {
+        int hashValue;
+
+        end = strchr(start, ',');
+        if (end) {
+            *end = 0;
+        }
+
+        hashValue = dvmComputeUtf8Hash(start);
+        dvmHashTableLookup(isMethod ? gDvmJit.methodTable : gDvmJit.classTable,
+                           hashValue, strdup(start), (HashCompareFunc) strcmp, true);
+
+        if (end) {
+            start = end + 1;
+        } else {
+            break;
+        }
+    } while (1);
+    free(buf);
+}
+
+/* The format of jit_config.list:
+   EXCLUDE or INCLUDE
+   CLASS
+   prefix1 ...
+   METHOD
+   prefix 1 ...
+   OFFSET
+   index ... //each pair is a range, if pcOff falls into a range, JIT
+*/
+static int processXjitconfig(const char* opt) {
+   FILE* fp = fopen(opt, "r");
+   if (fp == NULL) {
+       return -1;
+   }
+
+   char fLine[500];
+   bool startClass = false, startMethod = false, startOffset = false;
+   gDvmJit.num_entries_pcTable = 0;
+   int idx = 0;
+
+   while (fgets(fLine, 500, fp) != NULL) {
+       char* curLine = strtok(fLine, " \t\r\n");
+       /* handles keyword CLASS, METHOD, INCLUDE, EXCLUDE */
+       if (!strncmp(curLine, "CLASS", 5)) {
+           startClass = true;
+           startMethod = false;
+           startOffset = false;
+           continue;
+       }
+       if (!strncmp(curLine, "METHOD", 6)) {
+           startMethod = true;
+           startClass = false;
+           startOffset = false;
+           continue;
+       }
+       if (!strncmp(curLine, "OFFSET", 6)) {
+           startOffset = true;
+           startMethod = false;
+           startClass = false;
+           continue;
+       }
+       if (!strncmp(curLine, "EXCLUDE", 7)) {
+          gDvmJit.includeSelectedMethod = false;
+          continue;
+       }
+       if (!strncmp(curLine, "INCLUDE", 7)) {
+          gDvmJit.includeSelectedMethod = true;
+          continue;
+       }
+       if (!startMethod && !startClass && !startOffset) {
+         continue;
+       }
+
+        int hashValue = dvmComputeUtf8Hash(curLine);
+        if (startMethod) {
+            if (gDvmJit.methodTable == NULL) {
+                gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+            }
+            dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               strdup(curLine),
+                               (HashCompareFunc) strcmp, true);
+        } else if (startClass) {
+            if (gDvmJit.classTable == NULL) {
+                gDvmJit.classTable = dvmHashTableCreate(8, NULL);
+            }
+            dvmHashTableLookup(gDvmJit.classTable, hashValue,
+                               strdup(curLine),
+                               (HashCompareFunc) strcmp, true);
+        } else if (startOffset) {
+           int tmpInt = atoi(curLine);
+           gDvmJit.pcTable[idx++] = tmpInt;
+           if (idx >= COMPILER_PC_OFFSET_SIZE) {
+               printf("processXjitoffset: ignore entries beyond %d\n", COMPILER_PC_OFFSET_SIZE);
+               break;
+           }
+        }
+   }
+   gDvmJit.num_entries_pcTable = idx;
+   fclose(fp);
+   return 0;
+}
+#endif
+
+/*
+ * Process an argument vector full of options.  Unlike standard C programs,
+ * argv[0] does not contain the name of the program.
+ *
+ * If "ignoreUnrecognized" is set, we ignore options starting with "-X" or "_"
+ * that we don't recognize.  Otherwise, we return with an error as soon as
+ * we see anything we can't identify.
+ *
+ * Returns 0 on success, -1 on failure, and 1 for the special case of
+ * "-version" where we want to stop without showing an error message.
+ */
+static int processOptions(int argc, const char* const argv[],
+    bool ignoreUnrecognized)
+{
+    int i;
+
+    ALOGV("VM options (%d):", argc);
+    for (i = 0; i < argc; i++)
+        ALOGV("  %d: '%s'", i, argv[i]);
+
+    /*
+     * Over-allocate AssertionControl array for convenience.  If allocated,
+     * the array must be able to hold at least one entry, so that the
+     * zygote-time activation can do its business.
+     */
+    assert(gDvm.assertionCtrl == NULL);
+    if (argc > 0) {
+        gDvm.assertionCtrl =
+            (AssertionControl*) malloc(sizeof(AssertionControl) * argc);
+        if (gDvm.assertionCtrl == NULL)
+            return -1;
+        assert(gDvm.assertionCtrlCount == 0);
+    }
+
+    for (i = 0; i < argc; i++) {
+        if (strcmp(argv[i], "-help") == 0) {
+            /* show usage and stop */
+            usage();
+            exit(0);
+
+        } else if (strcmp(argv[i], "-version") == 0) {
+            /* show version and stop */
+            showVersion();
+            return 1;
+        } else if (strcmp(argv[i], "-showversion") == 0) {
+            /* show version and continue */
+            showVersion();
+
+        } else if (strcmp(argv[i], "-classpath") == 0 ||
+                   strcmp(argv[i], "-cp") == 0)
+        {
+            /* set classpath */
+            if (i == argc-1) {
+                dvmFprintf(stderr, "Missing classpath path list\n");
+                return -1;
+            }
+            free(gDvm.classPathStr); /* in case we have compiled-in default */
+            gDvm.classPathStr = strdup(argv[++i]);
+
+        } else if (strncmp(argv[i], "-Xbootclasspath:",
+                sizeof("-Xbootclasspath:")-1) == 0)
+        {
+            /* set bootclasspath */
+            const char* path = argv[i] + sizeof("-Xbootclasspath:")-1;
+
+            if (*path == '\0') {
+                dvmFprintf(stderr, "Missing bootclasspath path list\n");
+                return -1;
+            }
+            free(gDvm.bootClassPathStr);
+            gDvm.bootClassPathStr = strdup(path);
+
+        } else if (strncmp(argv[i], "-Xbootclasspath/a:",
+                sizeof("-Xbootclasspath/a:")-1) == 0) {
+            const char* appPath = argv[i] + sizeof("-Xbootclasspath/a:")-1;
+
+            if (*(appPath) == '\0') {
+                dvmFprintf(stderr, "Missing appending bootclasspath path list\n");
+                return -1;
+            }
+            char* allPath;
+
+            if (asprintf(&allPath, "%s:%s", gDvm.bootClassPathStr, appPath) < 0) {
+                dvmFprintf(stderr, "Can't append to bootclasspath path list\n");
+                return -1;
+            }
+            free(gDvm.bootClassPathStr);
+            gDvm.bootClassPathStr = allPath;
+
+        } else if (strncmp(argv[i], "-Xbootclasspath/p:",
+                sizeof("-Xbootclasspath/p:")-1) == 0) {
+            const char* prePath = argv[i] + sizeof("-Xbootclasspath/p:")-1;
+
+            if (*(prePath) == '\0') {
+                dvmFprintf(stderr, "Missing prepending bootclasspath path list\n");
+                return -1;
+            }
+            char* allPath;
+
+            if (asprintf(&allPath, "%s:%s", prePath, gDvm.bootClassPathStr) < 0) {
+                dvmFprintf(stderr, "Can't prepend to bootclasspath path list\n");
+                return -1;
+            }
+            free(gDvm.bootClassPathStr);
+            gDvm.bootClassPathStr = allPath;
+
+        } else if (strncmp(argv[i], "-D", 2) == 0) {
+            /* Properties are handled in managed code. We just check syntax. */
+            if (strchr(argv[i], '=') == NULL) {
+                dvmFprintf(stderr, "Bad system property setting: \"%s\"\n",
+                    argv[i]);
+                return -1;
+            }
+            gDvm.properties->push_back(argv[i] + 2);
+
+        } else if (strcmp(argv[i], "-jar") == 0) {
+            // TODO: handle this; name of jar should be in argv[i+1]
+            dvmFprintf(stderr, "-jar not yet handled\n");
+            assert(false);
+
+        } else if (strncmp(argv[i], "-Xms", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
+                    gDvm.heapStartingSize = val;
+                } else {
+                    dvmFprintf(stderr,
+                        "Invalid -Xms '%s', range is %dKB to %dKB\n",
+                        argv[i], kMinHeapStartSize/1024, kMaxHeapSize/1024);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xms option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xmx", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapSize && val <= kMaxHeapSize) {
+                    gDvm.heapMaximumSize = val;
+                } else {
+                    dvmFprintf(stderr,
+                        "Invalid -Xmx '%s', range is %dKB to %dKB\n",
+                        argv[i], kMinHeapSize/1024, kMaxHeapSize/1024);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapGrowthLimit=", 20) == 0) {
+            size_t val = parseMemOption(argv[i] + 20, 1024);
+            if (val != 0) {
+                gDvm.heapGrowthLimit = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapGrowthLimit option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapMinFree=", 16) == 0) {
+            size_t val = parseMemOption(argv[i] + 16, 1024);
+            if (val != 0) {
+                gDvm.heapMinFree = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapMinFree option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-XX:HeapMaxFree=", 16) == 0) {
+            size_t val = parseMemOption(argv[i] + 16, 1024);
+            if (val != 0) {
+                gDvm.heapMaxFree = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapMaxFree option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strcmp(argv[i], "-XX:LowMemoryMode") == 0) {
+          gDvm.lowMemoryMode = true;
+        } else if (strncmp(argv[i], "-XX:HeapTargetUtilization=", 26) == 0) {
+            const char* start = argv[i] + 26;
+            const char* end = start;
+            double val = strtod(start, const_cast<char**>(&end));
+            // Ensure that we have a value, there was no cruft after it and it
+            // satisfies a sensible range.
+            bool sane_val = (start != end) && (end[0] == '\0') &&
+                (val >= 0.1) && (val <= 0.9);
+            if (sane_val) {
+                gDvm.heapTargetUtilization = val;
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:HeapTargetUtilization option '%s'\n", argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xss", 4) == 0) {
+            size_t val = parseMemOption(argv[i]+4, 1);
+            if (val != 0) {
+                if (val >= kMinStackSize && val <= kMaxStackSize) {
+                    gDvm.stackSize = val;
+                    if (val > gDvm.mainThreadStackSize) {
+                        gDvm.mainThreadStackSize = val;
+                    }
+                } else {
+                    dvmFprintf(stderr, "Invalid -Xss '%s', range is %d to %d\n",
+                        argv[i], kMinStackSize, kMaxStackSize);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -Xss option '%s'\n", argv[i]);
+                return -1;
+            }
+
+        } else if (strncmp(argv[i], "-XX:mainThreadStackSize=", strlen("-XX:mainThreadStackSize=")) == 0) {
+            size_t val = parseMemOption(argv[i] + strlen("-XX:mainThreadStackSize="), 1);
+            if (val != 0) {
+                if (val >= kMinStackSize && val <= kMaxStackSize) {
+                    gDvm.mainThreadStackSize = val;
+                } else {
+                    dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize '%s', range is %d to %d\n",
+                               argv[i], kMinStackSize, kMaxStackSize);
+                    return -1;
+                }
+            } else {
+                dvmFprintf(stderr, "Invalid -XX:mainThreadStackSize option '%s'\n", argv[i]);
+                return -1;
+            }
+
+        } else if (strncmp(argv[i], "-XX:+DisableExplicitGC", 22) == 0) {
+            gDvm.disableExplicitGc = true;
+        } else if (strcmp(argv[i], "-verbose") == 0 ||
+            strcmp(argv[i], "-verbose:class") == 0)
+        {
+            // JNI spec says "-verbose:gc,class" is valid, but cmd line
+            // doesn't work that way; may want to support.
+            gDvm.verboseClass = true;
+        } else if (strcmp(argv[i], "-verbose:jni") == 0) {
+            gDvm.verboseJni = true;
+        } else if (strcmp(argv[i], "-verbose:gc") == 0) {
+            gDvm.verboseGc = true;
+        } else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
+            gDvm.verboseShutdown = true;
+
+        } else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
+            enableAssertions(argv[i] + 17, true);
+        } else if (strncmp(argv[i], "-ea", 3) == 0) {
+            enableAssertions(argv[i] + 3, true);
+        } else if (strncmp(argv[i], "-disableassertions", 18) == 0) {
+            enableAssertions(argv[i] + 18, false);
+        } else if (strncmp(argv[i], "-da", 3) == 0) {
+            enableAssertions(argv[i] + 3, false);
+        } else if (strcmp(argv[i], "-enablesystemassertions") == 0 ||
+                   strcmp(argv[i], "-esa") == 0)
+        {
+            enableAssertions(NULL, true);
+        } else if (strcmp(argv[i], "-disablesystemassertions") == 0 ||
+                   strcmp(argv[i], "-dsa") == 0)
+        {
+            enableAssertions(NULL, false);
+
+        } else if (strncmp(argv[i], "-Xcheck:jni", 11) == 0) {
+            /* nothing to do now -- was handled during JNI init */
+
+        } else if (strcmp(argv[i], "-Xdebug") == 0) {
+            /* accept but ignore */
+
+        } else if (strncmp(argv[i], "-Xrunjdwp:", 10) == 0 ||
+            strncmp(argv[i], "-agentlib:jdwp=", 15) == 0)
+        {
+            const char* tail;
+
+            if (argv[i][1] == 'X')
+                tail = argv[i] + 10;
+            else
+                tail = argv[i] + 15;
+
+            if (strncmp(tail, "help", 4) == 0 || !parseJdwpOptions(tail)) {
+                showJdwpHelp();
+                return 1;
+            }
+        } else if (strcmp(argv[i], "-Xrs") == 0) {
+            gDvm.reduceSignals = true;
+        } else if (strcmp(argv[i], "-Xnoquithandler") == 0) {
+            /* disables SIGQUIT handler thread while still blocking SIGQUIT */
+            /* (useful if we don't want thread but system still signals us) */
+            gDvm.noQuitHandler = true;
+        } else if (strcmp(argv[i], "-Xzygote") == 0) {
+            gDvm.zygote = true;
+#if defined(WITH_JIT)
+            gDvmJit.runningInAndroidFramework = true;
+#endif
+        } else if (strncmp(argv[i], "-Xdexopt:", 9) == 0) {
+            if (strcmp(argv[i] + 9, "none") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_NONE;
+            else if (strcmp(argv[i] + 9, "verified") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+            else if (strcmp(argv[i] + 9, "all") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_ALL;
+            else if (strcmp(argv[i] + 9, "full") == 0)
+                gDvm.dexOptMode = OPTIMIZE_MODE_FULL;
+            else {
+                dvmFprintf(stderr, "Unrecognized dexopt option '%s'\n",argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xverify:", 9) == 0) {
+            if (strcmp(argv[i] + 9, "none") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_NONE;
+            else if (strcmp(argv[i] + 9, "remote") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_REMOTE;
+            else if (strcmp(argv[i] + 9, "all") == 0)
+                gDvm.classVerifyMode = VERIFY_MODE_ALL;
+            else {
+                dvmFprintf(stderr, "Unrecognized verify option '%s'\n",argv[i]);
+                return -1;
+            }
+        } else if (strncmp(argv[i], "-Xjnigreflimit:", 15) == 0) {
+            // Ignored for backwards compatibility.
+        } else if (strncmp(argv[i], "-Xjnitrace:", 11) == 0) {
+            gDvm.jniTrace = strdup(argv[i] + 11);
+        } else if (strcmp(argv[i], "-Xlog-stdio") == 0) {
+            gDvm.logStdio = true;
+
+        } else if (strncmp(argv[i], "-Xint", 5) == 0) {
+            if (argv[i][5] == ':') {
+                if (strcmp(argv[i] + 6, "portable") == 0)
+                    gDvm.executionMode = kExecutionModeInterpPortable;
+                else if (strcmp(argv[i] + 6, "fast") == 0)
+                    gDvm.executionMode = kExecutionModeInterpFast;
+#ifdef WITH_JIT
+                else if (strcmp(argv[i] + 6, "jit") == 0)
+                    gDvm.executionMode = kExecutionModeJit;
+#endif
+                else {
+                    dvmFprintf(stderr,
+                        "Warning: Unrecognized interpreter mode %s\n",argv[i]);
+                    /* keep going */
+                }
+            } else {
+                /* disable JIT if it was enabled by default */
+                gDvm.executionMode = kExecutionModeInterpFast;
+            }
+
+        } else if (strncmp(argv[i], "-Xlockprofthreshold:", 20) == 0) {
+            gDvm.lockProfThreshold = atoi(argv[i] + 20);
+
+#ifdef WITH_JIT
+        } else if (strncmp(argv[i], "-Xjitop", 7) == 0) {
+            processXjitop(argv[i]);
+        } else if (strncmp(argv[i], "-Xjitmethod:", 12) == 0) {
+            processXjitmethod(argv[i] + strlen("-Xjitmethod:"), true);
+        } else if (strncmp(argv[i], "-Xjitclass:", 11) == 0) {
+            processXjitmethod(argv[i] + strlen("-Xjitclass:"), false);
+        } else if (strncmp(argv[i], "-Xjitoffset:", 12) == 0) {
+            processXjitoffset(argv[i] + strlen("-Xjitoffset:"));
+        } else if (strncmp(argv[i], "-Xjitconfig:", 12) == 0) {
+            processXjitconfig(argv[i] + strlen("-Xjitconfig:"));
+        } else if (strncmp(argv[i], "-Xjitblocking", 13) == 0) {
+          gDvmJit.blockingMode = true;
+        } else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
+          gDvmJit.threshold = atoi(argv[i] + 15);
+        } else if (strncmp(argv[i], "-Xjitcodecachesize:", 19) == 0) {
+          gDvmJit.codeCacheSize = atoi(argv[i] + 19) * 1024;
+          if (gDvmJit.codeCacheSize == 0) {
+            gDvm.executionMode = kExecutionModeInterpFast;
+          }
+        } else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
+          gDvmJit.includeSelectedOp = true;
+        } else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
+          gDvmJit.includeSelectedMethod = true;
+        } else if (strncmp(argv[i], "-Xjitcheckcg", 12) == 0) {
+          gDvmJit.checkCallGraph = true;
+          /* Need to enable blocking mode due to stack crawling */
+          gDvmJit.blockingMode = true;
+        } else if (strncmp(argv[i], "-Xjitdumpbin", 12) == 0) {
+          gDvmJit.printBinary = true;
+        } else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
+          gDvmJit.printMe = true;
+        } else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
+          gDvmJit.profileMode = kTraceProfilingContinuous;
+        } else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
+          /* Disable selected optimizations */
+          if (argv[i][15] == ':') {
+              sscanf(argv[i] + 16, "%x", &gDvmJit.disableOpt);
+          /* Disable all optimizations */
+          } else {
+              gDvmJit.disableOpt = -1;
+          }
+        } else if (strncmp(argv[i], "-Xjitsuspendpoll", 16) == 0) {
+          gDvmJit.genSuspendPoll = true;
+#endif
+
+        } else if (strncmp(argv[i], "-Xstacktracefile:", 17) == 0) {
+            gDvm.stackTraceFile = strdup(argv[i]+17);
+
+        } else if (strcmp(argv[i], "-Xgenregmap") == 0) {
+            gDvm.generateRegisterMaps = true;
+        } else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
+            gDvm.generateRegisterMaps = false;
+
+        } else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
+            gDvm.monitorVerification = true;
+        } else if (strcmp(argv[i], "Xverifyopt:nocheckmon") == 0) {
+            gDvm.monitorVerification = false;
+
+        } else if (strncmp(argv[i], "-Xgc:", 5) == 0) {
+            if (strcmp(argv[i] + 5, "precise") == 0)
+                gDvm.preciseGc = true;
+            else if (strcmp(argv[i] + 5, "noprecise") == 0)
+                gDvm.preciseGc = false;
+            else if (strcmp(argv[i] + 5, "preverify") == 0)
+                gDvm.preVerify = true;
+            else if (strcmp(argv[i] + 5, "nopreverify") == 0)
+                gDvm.preVerify = false;
+            else if (strcmp(argv[i] + 5, "postverify") == 0)
+                gDvm.postVerify = true;
+            else if (strcmp(argv[i] + 5, "nopostverify") == 0)
+                gDvm.postVerify = false;
+            else if (strcmp(argv[i] + 5, "concurrent") == 0)
+                gDvm.concurrentMarkSweep = true;
+            else if (strcmp(argv[i] + 5, "noconcurrent") == 0)
+                gDvm.concurrentMarkSweep = false;
+            else if (strcmp(argv[i] + 5, "verifycardtable") == 0)
+                gDvm.verifyCardTable = true;
+            else if (strcmp(argv[i] + 5, "noverifycardtable") == 0)
+                gDvm.verifyCardTable = false;
+            else {
+                dvmFprintf(stderr, "Bad value for -Xgc");
+                return -1;
+            }
+            ALOGV("Precise GC configured %s", gDvm.preciseGc ? "ON" : "OFF");
+
+        } else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
+            gDvm.verifyDexChecksum = true;
+
+        } else if (strcmp(argv[i], "-Xprofile:threadcpuclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceThreadCpu;
+        } else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceWall;
+        } else if (strcmp(argv[i], "-Xprofile:dualclock") == 0) {
+            gDvm.profilerClockSource = kProfilerClockSourceDual;
+
+        } else {
+            if (!ignoreUnrecognized) {
+                dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
+                return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Set defaults for fields altered or modified by arguments.
+ *
+ * Globals are initialized to 0 (a/k/a NULL or false).
+ */
+static void setCommandLineDefaults()
+{
+    const char* envStr = getenv("CLASSPATH");
+    if (envStr != NULL) {
+        gDvm.classPathStr = strdup(envStr);
+    } else {
+        gDvm.classPathStr = strdup(".");
+    }
+    envStr = getenv("BOOTCLASSPATH");
+    if (envStr != NULL) {
+        gDvm.bootClassPathStr = strdup(envStr);
+    } else {
+        gDvm.bootClassPathStr = strdup(".");
+    }
+
+    gDvm.properties = new std::vector<std::string>();
+
+    /* Defaults overridden by -Xms and -Xmx.
+     * TODO: base these on a system or application-specific default
+     */
+    gDvm.heapStartingSize = 2 * 1024 * 1024;  // Spec says 16MB; too big for us.
+    gDvm.heapMaximumSize = 16 * 1024 * 1024;  // Spec says 75% physical mem
+    gDvm.heapGrowthLimit = 0;  // 0 means no growth limit
+    gDvm.lowMemoryMode = false;
+    gDvm.stackSize = kDefaultStackSize;
+    gDvm.mainThreadStackSize = kDefaultStackSize;
+    // When the heap is less than the maximum or growth limited size,
+    // fix the free portion of the heap. The utilization is the ratio
+    // of live to free memory, 0.5 implies half the heap is available
+    // to allocate into before a GC occurs. Min free and max free
+    // force the free memory to never be smaller than min free or
+    // larger than max free.
+    gDvm.heapTargetUtilization = 0.5;
+    gDvm.heapMaxFree = 2 * 1024 * 1024;
+    gDvm.heapMinFree = gDvm.heapMaxFree / 4;
+
+    gDvm.concurrentMarkSweep = true;
+
+    /* gDvm.jdwpSuspend = true; */
+
+    /* allowed unless zygote config doesn't allow it */
+    gDvm.jdwpAllowed = true;
+
+    /* default verification and optimization modes */
+    gDvm.classVerifyMode = VERIFY_MODE_ALL;
+    gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+    gDvm.monitorVerification = false;
+    gDvm.generateRegisterMaps = true;
+    gDvm.registerMapMode = kRegisterMapModeTypePrecise;
+
+    /*
+     * Default execution mode.
+     *
+     * This should probably interact with the mterp code somehow, e.g. if
+     * we know we're using the "desktop" build we should probably be
+     * using "portable" rather than "fast".
+     */
+#if defined(WITH_JIT)
+    gDvm.executionMode = kExecutionModeJit;
+    gDvmJit.num_entries_pcTable = 0;
+    gDvmJit.includeSelectedMethod = false;
+    gDvmJit.includeSelectedOffset = false;
+    gDvmJit.methodTable = NULL;
+    gDvmJit.classTable = NULL;
+    gDvmJit.codeCacheSize = DEFAULT_CODE_CACHE_SIZE;
+
+    gDvm.constInit = false;
+    gDvm.commonInit = false;
+#else
+    gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+
+    /*
+     * SMP support is a compile-time define, but we may want to have
+     * dexopt target a differently-configured device.
+     */
+    gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+
+    /*
+     * Default profiler configuration.
+     */
+    gDvm.profilerClockSource = kProfilerClockSourceDual;
+}
+
+
+/*
+ * Handle a SIGBUS, which frequently occurs because somebody replaced an
+ * optimized DEX file out from under us.
+ */
+static void busCatcher(int signum, siginfo_t* info, void* context)
+{
+    void* addr = info->si_addr;
+
+    ALOGE("Caught a SIGBUS (%d), addr=%p", signum, addr);
+
+    /*
+     * If we return at this point the SIGBUS just keeps happening, so we
+     * remove the signal handler and allow it to kill us.  TODO: restore
+     * the original, which points to a debuggerd stub; if we don't then
+     * debuggerd won't be notified.
+     */
+    signal(SIGBUS, SIG_DFL);
+}
+
+/*
+ * Configure signals.  We need to block SIGQUIT so that the signal only
+ * reaches the dump-stack-trace thread.
+ *
+ * This can be disabled with the "-Xrs" flag.
+ */
+static void blockSignals()
+{
+    sigset_t mask;
+    int cc;
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGQUIT);
+    sigaddset(&mask, SIGUSR1);      // used to initiate heap dump
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    sigaddset(&mask, SIGUSR2);      // used to investigate JIT internals
+#endif
+    sigaddset(&mask, SIGPIPE);
+    cc = sigprocmask(SIG_BLOCK, &mask, NULL);
+    assert(cc == 0);
+
+    if (false) {
+        /* TODO: save the old sigaction in a global */
+        struct sigaction sa;
+        memset(&sa, 0, sizeof(sa));
+        sa.sa_sigaction = busCatcher;
+        sa.sa_flags = SA_SIGINFO;
+        cc = sigaction(SIGBUS, &sa, NULL);
+        assert(cc == 0);
+    }
+}
+
+class ScopedShutdown {
+public:
+    ScopedShutdown() : armed_(true) {
+    }
+
+    ~ScopedShutdown() {
+        if (armed_) {
+            dvmShutdown();
+        }
+    }
+
+    void disarm() {
+        armed_ = false;
+    }
+
+private:
+    bool armed_;
+};
+
+/*
+ * VM initialization.  Pass in any options provided on the command line.
+ * Do not pass in the class name or the options for the class.
+ *
+ * Returns 0 on success.
+ */
+std::string dvmStartup(int argc, const char* const argv[],
+        bool ignoreUnrecognized, JNIEnv* pEnv)
+{
+    ScopedShutdown scopedShutdown;
+
+    assert(gDvm.initializing);
+
+    ALOGV("VM init args (%d):", argc);
+    for (int i = 0; i < argc; i++) {
+        ALOGV("  %d: '%s'", i, argv[i]);
+    }
+    setCommandLineDefaults();
+
+    /*
+     * Process the option flags (if any).
+     */
+    int cc = processOptions(argc, argv, ignoreUnrecognized);
+    if (cc != 0) {
+        if (cc < 0) {
+            dvmFprintf(stderr, "\n");
+            usage();
+        }
+        return "syntax error";
+    }
+
+#if WITH_EXTRA_GC_CHECKS > 1
+    /* only "portable" interp has the extra goodies */
+    if (gDvm.executionMode != kExecutionModeInterpPortable) {
+        ALOGI("Switching to 'portable' interpreter for GC checks");
+        gDvm.executionMode = kExecutionModeInterpPortable;
+    }
+#endif
+
+    /* Configure group scheduling capabilities */
+    if (!access("/dev/cpuctl/tasks", F_OK)) {
+        ALOGV("Using kernel group scheduling");
+        gDvm.kernelGroupScheduling = 1;
+    } else {
+        ALOGV("Using kernel scheduler policies");
+    }
+
+    /* configure signal handling */
+    if (!gDvm.reduceSignals)
+        blockSignals();
+
+    /* verify system page size */
+    if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
+        return StringPrintf("expected page size %d, got %d",
+                SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
+    }
+
+    /* mterp setup */
+    ALOGV("Using executionMode %d", gDvm.executionMode);
+    dvmCheckAsmConstants();
+
+    /*
+     * Initialize components.
+     */
+    dvmQuasiAtomicsStartup();
+    if (!dvmAllocTrackerStartup()) {
+        return "dvmAllocTrackerStartup failed";
+    }
+    if (!dvmGcStartup()) {
+        return "dvmGcStartup failed";
+    }
+    if (!dvmThreadStartup()) {
+        return "dvmThreadStartup failed";
+    }
+    if (!dvmInlineNativeStartup()) {
+        return "dvmInlineNativeStartup";
+    }
+    if (!dvmRegisterMapStartup()) {
+        return "dvmRegisterMapStartup failed";
+    }
+    if (!dvmInstanceofStartup()) {
+        return "dvmInstanceofStartup failed";
+    }
+    if (!dvmClassStartup()) {
+        return "dvmClassStartup failed";
+    }
+
+    /*
+     * At this point, the system is guaranteed to be sufficiently
+     * initialized that we can look up classes and class members. This
+     * call populates the gDvm instance with all the class and member
+     * references that the VM wants to use directly.
+     */
+    if (!dvmFindRequiredClassesAndMembers()) {
+        return "dvmFindRequiredClassesAndMembers failed";
+    }
+
+    if (!dvmStringInternStartup()) {
+        return "dvmStringInternStartup failed";
+    }
+    if (!dvmNativeStartup()) {
+        return "dvmNativeStartup failed";
+    }
+    if (!dvmInternalNativeStartup()) {
+        return "dvmInternalNativeStartup failed";
+    }
+    if (!dvmJniStartup()) {
+        return "dvmJniStartup failed";
+    }
+    if (!dvmProfilingStartup()) {
+        return "dvmProfilingStartup failed";
+    }
+
+    /*
+     * Create a table of methods for which we will substitute an "inline"
+     * version for performance.
+     */
+    if (!dvmCreateInlineSubsTable()) {
+        return "dvmCreateInlineSubsTable failed";
+    }
+
+    /*
+     * Miscellaneous class library validation.
+     */
+    if (!dvmValidateBoxClasses()) {
+        return "dvmValidateBoxClasses failed";
+    }
+
+    /*
+     * Do the last bits of Thread struct initialization we need to allow
+     * JNI calls to work.
+     */
+    if (!dvmPrepMainForJni(pEnv)) {
+        return "dvmPrepMainForJni failed";
+    }
+
+    /*
+     * Explicitly initialize java.lang.Class.  This doesn't happen
+     * automatically because it's allocated specially (it's an instance
+     * of itself).  Must happen before registration of system natives,
+     * which make some calls that throw assertions if the classes they
+     * operate on aren't initialized.
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        return "couldn't initialized java.lang.Class";
+    }
+
+    /*
+     * Register the system native methods, which are registered through JNI.
+     */
+    if (!registerSystemNatives(pEnv)) {
+        return "couldn't register system natives";
+    }
+
+    /*
+     * Do some "late" initialization for the memory allocator.  This may
+     * allocate storage and initialize classes.
+     */
+    if (!dvmCreateStockExceptions()) {
+        return "dvmCreateStockExceptions failed";
+    }
+
+    /*
+     * At this point, the VM is in a pretty good state.  Finish prep on
+     * the main thread (specifically, create a java.lang.Thread object to go
+     * along with our Thread struct).  Note we will probably be executing
+     * some interpreted class initializer code in here.
+     */
+    if (!dvmPrepMainThread()) {
+        return "dvmPrepMainThread failed";
+    }
+
+    /*
+     * Make sure we haven't accumulated any tracked references.  The main
+     * thread should be starting with a clean slate.
+     */
+    if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
+    {
+        ALOGW("Warning: tracked references remain post-initialization");
+        dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
+    }
+
+    /* general debugging setup */
+    if (!dvmDebuggerStartup()) {
+        return "dvmDebuggerStartup failed";
+    }
+
+    if (!dvmGcStartupClasses()) {
+        return "dvmGcStartupClasses failed";
+    }
+
+    /*
+     * Init for either zygote mode or non-zygote mode.  The key difference
+     * is that we don't start any additional threads in Zygote mode.
+     */
+    if (gDvm.zygote) {
+        if (!initZygote()) {
+            return "initZygote failed";
+        }
+    } else {
+        if (!dvmInitAfterZygote()) {
+            return "dvmInitAfterZygote failed";
+        }
+    }
+
+
+#ifndef NDEBUG
+    if (!dvmTestHash())
+        ALOGE("dvmTestHash FAILED");
+    if (false /*noisy!*/ && !dvmTestIndirectRefTable())
+        ALOGE("dvmTestIndirectRefTable FAILED");
+#endif
+
+    if (dvmCheckException(dvmThreadSelf())) {
+        dvmLogExceptionStackTrace();
+        return "Exception pending at end of VM initialization";
+    }
+
+    scopedShutdown.disarm();
+    return "";
+}
+
+static void loadJniLibrary(const char* name) {
+    std::string mappedName(StringPrintf(OS_SHARED_LIB_FORMAT_STR, name));
+    char* reason = NULL;
+    if (!dvmLoadNativeCode(mappedName.c_str(), NULL, &reason)) {
+        ALOGE("dvmLoadNativeCode failed for \"%s\": %s", name, reason);
+        dvmAbort();
+    }
+}
+
+/*
+ * Register java.* natives from our class libraries.  We need to do
+ * this after we're ready for JNI registration calls, but before we
+ * do any class initialization.
+ *
+ * If we get this wrong, we will blow up in the ThreadGroup class init if
+ * interpreted code makes any reference to System.  It will likely do this
+ * since it wants to do some java.io.File setup (e.g. for static in/out/err).
+ *
+ * We need to have gDvm.initializing raised here so that JNI FindClass
+ * won't try to use the system/application class loader.
+ */
+static bool registerSystemNatives(JNIEnv* pEnv)
+{
+    // Main thread is always first in list.
+    Thread* self = gDvm.threadList;
+
+    // Must set this before allowing JNI-based method registration.
+    self->status = THREAD_NATIVE;
+
+    // First set up JniConstants, which is used by libcore.
+    JniConstants::init(pEnv);
+
+    // Set up our single JNI method.
+    // TODO: factor this out if we add more.
+    jclass c = pEnv->FindClass("java/lang/Class");
+    if (c == NULL) {
+        dvmAbort();
+    }
+    JNIEXPORT jobject JNICALL Java_java_lang_Class_getDex(JNIEnv* env, jclass javaClass);
+    const JNINativeMethod Java_java_lang_Class[] = {
+        { "getDex", "()Lcom/android/dex/Dex;", (void*) Java_java_lang_Class_getDex },
+    };
+    if (pEnv->RegisterNatives(c, Java_java_lang_Class, 1) != JNI_OK) {
+        dvmAbort();
+    }
+
+    // Most JNI libraries can just use System.loadLibrary, but you can't
+    // if you're the library that implements System.loadLibrary!
+    loadJniLibrary("javacore");
+    loadJniLibrary("nativehelper");
+
+    // Back to run mode.
+    self->status = THREAD_RUNNING;
+
+    return true;
+}
+
+/*
+ * Copied and modified slightly from system/core/toolbox/mount.c
+ */
+static std::string getMountsDevDir(const char *arg)
+{
+    char mount_dev[256];
+    char mount_dir[256];
+    int match;
+
+    FILE *fp = fopen("/proc/self/mounts", "r");
+    if (fp == NULL) {
+        ALOGE("Could not open /proc/self/mounts: %s", strerror(errno));
+        return "";
+    }
+
+    while ((match = fscanf(fp, "%255s %255s %*s %*s %*d %*d\n", mount_dev, mount_dir)) != EOF) {
+        mount_dev[255] = 0;
+        mount_dir[255] = 0;
+        if (match == 2 && (strcmp(arg, mount_dir) == 0)) {
+            fclose(fp);
+            return mount_dev;
+        }
+    }
+
+    fclose(fp);
+    return "";
+}
+
+/*
+ * Do zygote-mode-only initialization.
+ */
+static bool initZygote()
+{
+    /* zygote goes into its own process group */
+    setpgid(0,0);
+
+    // See storage config details at http://source.android.com/tech/storage/
+    // Create private mount namespace shared by all children
+    if (unshare(CLONE_NEWNS) == -1) {
+        SLOGE("Failed to unshare(): %s", strerror(errno));
+        return -1;
+    }
+
+    // Mark rootfs as being a slave so that changes from default
+    // namespace only flow into our children.
+    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
+        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
+        return -1;
+    }
+
+    // Create a staging tmpfs that is shared by our children; they will
+    // bind mount storage into their respective private namespaces, which
+    // are isolated from each other.
+    const char* target_base = getenv("EMULATED_STORAGE_TARGET");
+    if (target_base != NULL) {
+        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
+                "uid=0,gid=1028,mode=0751") == -1) {
+            SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));
+            return -1;
+        }
+    }
+
+    // Mark /system as NOSUID | NODEV
+    const char* android_root = getenv("ANDROID_ROOT");
+
+    if (android_root == NULL) {
+        SLOGE("environment variable ANDROID_ROOT does not exist?!?!");
+        return -1;
+    }
+
+    std::string mountDev(getMountsDevDir(android_root));
+    if (mountDev.empty()) {
+        SLOGE("Unable to find mount point for %s", android_root);
+        return -1;
+    }
+
+    if (mount(mountDev.c_str(), android_root, "none",
+            MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_RDONLY | MS_BIND, NULL) == -1) {
+        SLOGE("Remount of %s failed: %s", android_root, strerror(errno));
+        return -1;
+    }
+
+#ifdef HAVE_ANDROID_OS
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+        // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
+        // EINVAL. Don't die on such kernels.
+        if (errno != EINVAL) {
+            SLOGE("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+            return -1;
+        }
+    }
+#endif
+
+    return true;
+}
+
+/*
+ * Do non-zygote-mode initialization.  This is done during VM init for
+ * standard startup, or after a "zygote fork" when creating a new process.
+ */
+bool dvmInitAfterZygote()
+{
+    u8 startHeap, startQuit, startJdwp;
+    u8 endHeap, endQuit, endJdwp;
+
+    startHeap = dvmGetRelativeTimeUsec();
+
+    /*
+     * Post-zygote heap initialization, including starting
+     * the HeapWorker thread.
+     */
+    if (!dvmGcStartupAfterZygote())
+        return false;
+
+    endHeap = dvmGetRelativeTimeUsec();
+    startQuit = dvmGetRelativeTimeUsec();
+
+    /* start signal catcher thread that dumps stacks on SIGQUIT */
+    if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
+        if (!dvmSignalCatcherStartup())
+            return false;
+    }
+
+    /* start stdout/stderr copier, if requested */
+    if (gDvm.logStdio) {
+        if (!dvmStdioConverterStartup())
+            return false;
+    }
+
+    endQuit = dvmGetRelativeTimeUsec();
+    startJdwp = dvmGetRelativeTimeUsec();
+
+    /*
+     * Start JDWP thread.  If the command-line debugger flags specified
+     * "suspend=y", this will pause the VM.  We probably want this to
+     * come last.
+     */
+    if (!initJdwp()) {
+        ALOGD("JDWP init failed; continuing anyway");
+    }
+
+    endJdwp = dvmGetRelativeTimeUsec();
+
+    ALOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec",
+        (int)(endHeap-startHeap), (int)(endQuit-startQuit),
+        (int)(endJdwp-startJdwp), (int)(endJdwp-startHeap));
+
+#ifdef WITH_JIT
+    if (gDvm.executionMode == kExecutionModeJit) {
+        if (!dvmCompilerStartup())
+            return false;
+    }
+#endif
+
+    return true;
+}
+
+/*
+ * Prepare for a connection to a JDWP-compliant debugger.
+ *
+ * Note this needs to happen fairly late in the startup process, because
+ * we need to have all of the java.* native methods registered (which in
+ * turn requires JNI to be fully prepped).
+ *
+ * There are several ways to initialize:
+ *   server=n
+ *     We immediately try to connect to host:port.  Bail on failure.  On
+ *     success, send VM_START (suspending the VM if "suspend=y").
+ *   server=y suspend=n
+ *     Passively listen for a debugger to connect.  Return immediately.
+ *   server=y suspend=y
+ *     Wait until debugger connects.  Send VM_START ASAP, suspending the
+ *     VM after the message is sent.
+ *
+ * This gets more complicated with a nonzero value for "timeout".
+ */
+static bool initJdwp()
+{
+    assert(!gDvm.zygote);
+
+    /*
+     * Init JDWP if the debugger is enabled.  This may connect out to a
+     * debugger, passively listen for a debugger, or block waiting for a
+     * debugger.
+     */
+    if (gDvm.jdwpAllowed && gDvm.jdwpConfigured) {
+        JdwpStartupParams params;
+
+        if (gDvm.jdwpHost != NULL) {
+            if (strlen(gDvm.jdwpHost) >= sizeof(params.host)-1) {
+                ALOGE("ERROR: hostname too long: '%s'", gDvm.jdwpHost);
+                return false;
+            }
+            strcpy(params.host, gDvm.jdwpHost);
+        } else {
+            params.host[0] = '\0';
+        }
+        params.transport = gDvm.jdwpTransport;
+        params.server = gDvm.jdwpServer;
+        params.suspend = gDvm.jdwpSuspend;
+        params.port = gDvm.jdwpPort;
+
+        gDvm.jdwpState = dvmJdwpStartup(&params);
+        if (gDvm.jdwpState == NULL) {
+            ALOGW("WARNING: debugger thread failed to initialize");
+            /* TODO: ignore? fail? need to mimic "expected" behavior */
+        }
+    }
+
+    /*
+     * If a debugger has already attached, send the "welcome" message.  This
+     * may cause us to suspend all threads.
+     */
+    if (dvmJdwpIsActive(gDvm.jdwpState)) {
+        //dvmChangeStatus(NULL, THREAD_RUNNING);
+        if (!dvmJdwpPostVMStart(gDvm.jdwpState, gDvm.jdwpSuspend)) {
+            ALOGW("WARNING: failed to post 'start' message to debugger");
+            /* keep going */
+        }
+        //dvmChangeStatus(NULL, THREAD_NATIVE);
+    }
+
+    return true;
+}
+
+/*
+ * An alternative to JNI_CreateJavaVM/dvmStartup that does the first bit
+ * of initialization and then returns with "initializing" still set.  (Used
+ * by DexOpt command-line utility.)
+ *
+ * Attempting to use JNI or internal natives will fail.  It's best
+ * if no bytecode gets executed, which means no <clinit>, which means
+ * no exception-throwing.  (In practice we need to initialize Class and
+ * Object, and probably some exception classes.)
+ *
+ * Returns 0 on success.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+    DexClassVerifyMode verifyMode, int dexoptFlags)
+{
+    gDvm.initializing = true;
+    gDvm.optimizing = true;
+
+    /* configure signal handling */
+    blockSignals();
+
+    /* set some defaults */
+    setCommandLineDefaults();
+    free(gDvm.bootClassPathStr);
+    gDvm.bootClassPathStr = strdup(bootClassPath);
+
+    /* set opt/verify modes */
+    gDvm.dexOptMode = dexOptMode;
+    gDvm.classVerifyMode = verifyMode;
+    gDvm.generateRegisterMaps = (dexoptFlags & DEXOPT_GEN_REGISTER_MAPS) != 0;
+    if (dexoptFlags & DEXOPT_SMP) {
+        assert((dexoptFlags & DEXOPT_UNIPROCESSOR) == 0);
+        gDvm.dexOptForSmp = true;
+    } else if (dexoptFlags & DEXOPT_UNIPROCESSOR) {
+        gDvm.dexOptForSmp = false;
+    } else {
+        gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+    }
+
+    /*
+     * Initialize the heap, some basic thread control mutexes, and
+     * get the bootclasspath prepped.
+     *
+     * We can't load any classes yet because we may not yet have a source
+     * for things like java.lang.Object and java.lang.Class.
+     */
+    if (!dvmGcStartup())
+        goto fail;
+    if (!dvmThreadStartup())
+        goto fail;
+    if (!dvmInlineNativeStartup())
+        goto fail;
+    if (!dvmRegisterMapStartup())
+        goto fail;
+    if (!dvmInstanceofStartup())
+        goto fail;
+    if (!dvmClassStartup())
+        goto fail;
+
+    /*
+     * We leave gDvm.initializing set to "true" so that, if we're not
+     * able to process the "core" classes, we don't go into a death-spin
+     * trying to throw a "class not found" exception.
+     */
+
+    return 0;
+
+fail:
+    dvmShutdown();
+    return 1;
+}
+
+
+/*
+ * All threads have stopped.  Finish the shutdown procedure.
+ *
+ * We can also be called if startup fails partway through, so be prepared
+ * to deal with partially initialized data.
+ *
+ * Free any storage allocated in gGlobals.
+ *
+ * We can't dlclose() shared libs we've loaded, because it's possible a
+ * thread not associated with the VM is running code in one.
+ *
+ * This is called from the JNI DestroyJavaVM function, which can be
+ * called from any thread.  (In practice, this will usually run in the
+ * same thread that started the VM, a/k/a the main thread, but we don't
+ * want to assume that.)
+ */
+void dvmShutdown()
+{
+    ALOGV("VM shutting down");
+
+    if (CALC_CACHE_STATS)
+        dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+
+    /*
+     * Stop our internal threads.
+     */
+    dvmGcThreadShutdown();
+
+    if (gDvm.jdwpState != NULL)
+        dvmJdwpShutdown(gDvm.jdwpState);
+    free(gDvm.jdwpHost);
+    gDvm.jdwpHost = NULL;
+    free(gDvm.jniTrace);
+    gDvm.jniTrace = NULL;
+    free(gDvm.stackTraceFile);
+    gDvm.stackTraceFile = NULL;
+
+    /* tell signal catcher to shut down if it was started */
+    dvmSignalCatcherShutdown();
+
+    /* shut down stdout/stderr conversion */
+    dvmStdioConverterShutdown();
+
+#ifdef WITH_JIT
+    if (gDvm.executionMode == kExecutionModeJit) {
+        /* shut down the compiler thread */
+        dvmCompilerShutdown();
+    }
+#endif
+
+    /*
+     * Kill any daemon threads that still exist.  Actively-running threads
+     * are likely to crash the process if they continue to execute while
+     * the VM shuts down.
+     */
+    dvmSlayDaemons();
+
+    if (gDvm.verboseShutdown)
+        ALOGD("VM cleaning up");
+
+    dvmDebuggerShutdown();
+    dvmProfilingShutdown();
+    dvmJniShutdown();
+    dvmStringInternShutdown();
+    dvmThreadShutdown();
+    dvmClassShutdown();
+    dvmRegisterMapShutdown();
+    dvmInstanceofShutdown();
+    dvmInlineNativeShutdown();
+    dvmGcShutdown();
+    dvmAllocTrackerShutdown();
+
+    /* these must happen AFTER dvmClassShutdown has walked through class data */
+    dvmNativeShutdown();
+    dvmInternalNativeShutdown();
+
+    dvmFreeInlineSubsTable();
+
+    free(gDvm.bootClassPathStr);
+    free(gDvm.classPathStr);
+    delete gDvm.properties;
+
+    freeAssertionCtrl();
+
+    dvmQuasiAtomicsShutdown();
+
+    /*
+     * We want valgrind to report anything we forget to free as "definitely
+     * lost".  If there's a pointer in the global chunk, it would be reported
+     * as "still reachable".  Erasing the memory fixes this.
+     *
+     * This must be erased to zero if we want to restart the VM within this
+     * process.
+     */
+    memset(&gDvm, 0xcd, sizeof(gDvm));
+}
+
+
+/*
+ * fprintf() wrapper that calls through the JNI-specified vfprintf hook if
+ * one was specified.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+{
+    va_list args;
+    int result;
+
+    va_start(args, format);
+    if (gDvm.vfprintfHook != NULL)
+        result = (*gDvm.vfprintfHook)(fp, format, args);
+    else
+        result = vfprintf(fp, format, args);
+    va_end(args);
+
+    return result;
+}
+
+#ifdef __GLIBC__
+#include <execinfo.h>
+/*
+ * glibc-only stack dump function.  Requires link with "--export-dynamic".
+ *
+ * TODO: move this into libs/cutils and make it work for all platforms.
+ */
+void dvmPrintNativeBackTrace()
+{
+    size_t MAX_STACK_FRAMES = 64;
+    void* stackFrames[MAX_STACK_FRAMES];
+    size_t frameCount = backtrace(stackFrames, MAX_STACK_FRAMES);
+
+    /*
+     * TODO: in practice, we may find that we should use backtrace_symbols_fd
+     * to avoid allocation, rather than use our own custom formatting.
+     */
+    char** strings = backtrace_symbols(stackFrames, frameCount);
+    if (strings == NULL) {
+        ALOGE("backtrace_symbols failed: %s", strerror(errno));
+        return;
+    }
+
+    size_t i;
+    for (i = 0; i < frameCount; ++i) {
+        ALOGW("#%-2d %s", i, strings[i]);
+    }
+    free(strings);
+}
+#else
+void dvmPrintNativeBackTrace() {
+    /* Hopefully, you're on an Android device and debuggerd will do this. */
+}
+#endif
+
+/*
+ * Abort the VM.  We get here on fatal errors.  Try very hard not to use
+ * this; whenever possible, return an error to somebody responsible.
+ */
+void dvmAbort()
+{
+    /*
+     * Leave gDvm.lastMessage on the stack frame which can be decoded in the
+     * tombstone file. This is for situations where we only have tombstone files
+     * but no logs (ie b/5372634).
+     *
+     * For example, in the tombstone file you usually see this:
+     *
+     *   #00  pc 00050ef2  /system/lib/libdvm.so (dvmAbort)
+     *   #01  pc 00077670  /system/lib/libdvm.so (_Z15dvmClassStartupv)
+     *     :
+     *
+     * stack:
+     *     :
+     * #00 beed2658  00000000
+     *     beed265c  7379732f
+     *     beed2660  2f6d6574
+     *     beed2664  6d617266
+     *     beed2668  726f7765
+     *     beed266c  6f632f6b
+     *     beed2670  6a2e6572
+     *     beed2674  00007261
+     *     beed2678  00000000
+     *
+     * The ascii values between beed265c and beed2674 belongs to messageBuffer
+     * and it can be decoded as "/system/framework/core.jar".
+     */
+    const int messageLength = 512;
+    char messageBuffer[messageLength] = {0};
+    int result = 0;
+
+    snprintf(messageBuffer, messageLength, "%s", gDvm.lastMessage);
+
+    /* So that messageBuffer[] looks like useful stuff to the compiler */
+    for (int i = 0; i < messageLength && messageBuffer[i]; i++) {
+        result += messageBuffer[i];
+    }
+
+    ALOGE("VM aborting");
+
+    fflush(NULL);       // flush all open file buffers
+
+    /* JNI-supplied abort hook gets right of first refusal */
+    if (gDvm.abortHook != NULL)
+        (*gDvm.abortHook)();
+
+    /*
+     * On the device, debuggerd will give us a stack trace.
+     * On the host, we have to help ourselves.
+     */
+    dvmPrintNativeBackTrace();
+
+    abort();
+
+    /* notreached */
+}
diff --git a/vm/Init.h b/vm/Init.h
new file mode 100644
index 0000000..1b585fa
--- /dev/null
+++ b/vm/Init.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * VM initialization and shutdown.
+ */
+#ifndef DALVIK_INIT_H_
+#define DALVIK_INIT_H_
+
+/*
+ * Standard VM initialization, usually invoked through JNI.
+ */
+std::string dvmStartup(int argc, const char* const argv[],
+        bool ignoreUnrecognized, JNIEnv* pEnv);
+void dvmShutdown(void);
+bool dvmInitAfterZygote(void);
+
+/*
+ * Enable Java programming language assert statements after the Zygote fork.
+ */
+void dvmLateEnableAssertions(void);
+
+/*
+ * Partial VM initialization; only used as part of "dexopt", which may be
+ * asked to optimize a DEX file holding fundamental classes.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+    DexClassVerifyMode verifyMode, int dexoptFlags);
+
+/*
+ * Look up the set of classes and members used directly by the VM,
+ * storing references to them into the globals instance. See
+ * Globals.h. This function is exposed so that dex optimization may
+ * call it (while avoiding doing other unnecessary VM initialization).
+ *
+ * The function returns a success flag (true == success).
+ */
+bool dvmFindRequiredClassesAndMembers(void);
+
+/*
+ * Look up required members of the class Reference, and set the global
+ * reference to Reference itself too. This needs to be done separately
+ * from dvmFindRequiredClassesAndMembers(), during the course of
+ * linking the class Reference (which is done specially).
+ */
+bool dvmFindReferenceMembers(ClassObject* classReference);
+
+/*
+ * Replacement for fprintf() when we want to send a message to the console.
+ * This defaults to fprintf(), but will use the JNI fprintf callback if
+ * one was provided.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+#endif  // DALVIK_INIT_H_
diff --git a/vm/InitRefs.cpp b/vm/InitRefs.cpp
new file mode 100644
index 0000000..08c28f8
--- /dev/null
+++ b/vm/InitRefs.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*
+ * Code to initialize references to classes and members for use by
+ * lower-level VM facilities
+ */
+
+#include "Dalvik.h"
+
+static bool initClassReference(ClassObject** pClass, const char* name) {
+    ClassObject* result;
+
+    assert(*pClass == NULL);
+
+    if (name[0] == '[') {
+        result = dvmFindArrayClass(name, NULL);
+    } else {
+        result = dvmFindSystemClassNoInit(name);
+    }
+
+    if (result == NULL) {
+        ALOGE("Could not find essential class %s", name);
+        return false;
+    }
+
+    *pClass = result;
+    return true;
+}
+
+static bool initClassReferences() {
+    static struct { ClassObject** ref; const char* name; } classes[] = {
+        /*
+         * Note: The class Class gets special treatment during initial
+         * VM startup, so there is no need to list it here.
+         */
+
+        /* The corest of the core classes */
+        { &gDvm.classJavaLangObject, "Ljava/lang/Object;" },
+        { &gDvm.exThrowable,         "Ljava/lang/Throwable;" },
+
+        /* Slightly less core, but still down there, classes */
+        { &gDvm.classJavaLangClassArray,             "[Ljava/lang/Class;" },
+        { &gDvm.classJavaLangClassLoader,            "Ljava/lang/ClassLoader;" },
+        { &gDvm.classJavaLangObjectArray,            "[Ljava/lang/Object;"},
+        { &gDvm.classJavaLangStackTraceElement,      "Ljava/lang/StackTraceElement;" },
+        { &gDvm.classJavaLangStackTraceElementArray, "[Ljava/lang/StackTraceElement;" },
+        { &gDvm.classJavaLangString,                 "Ljava/lang/String;" },
+        { &gDvm.classJavaLangThread,                 "Ljava/lang/Thread;" },
+        { &gDvm.classJavaLangThreadGroup,            "Ljava/lang/ThreadGroup;" },
+        { &gDvm.classJavaLangVMThread,               "Ljava/lang/VMThread;" },
+
+        /* Arrays of primitive types */
+        { &gDvm.classArrayBoolean, "[Z" },
+        { &gDvm.classArrayByte,    "[B" },
+        { &gDvm.classArrayShort,   "[S" },
+        { &gDvm.classArrayChar,    "[C" },
+        { &gDvm.classArrayInt,     "[I" },
+        { &gDvm.classArrayLong,    "[J" },
+        { &gDvm.classArrayFloat,   "[F" },
+        { &gDvm.classArrayDouble,  "[D" },
+
+        /* Exception classes */
+        { &gDvm.exAbstractMethodError,             "Ljava/lang/AbstractMethodError;" },
+        { &gDvm.exArithmeticException,             "Ljava/lang/ArithmeticException;" },
+        { &gDvm.exArrayIndexOutOfBoundsException,  "Ljava/lang/ArrayIndexOutOfBoundsException;" },
+        { &gDvm.exArrayStoreException,             "Ljava/lang/ArrayStoreException;" },
+        { &gDvm.exClassCastException,              "Ljava/lang/ClassCastException;" },
+        { &gDvm.exClassCircularityError,           "Ljava/lang/ClassCircularityError;" },
+        { &gDvm.exClassNotFoundException,          "Ljava/lang/ClassNotFoundException;" },
+        { &gDvm.exClassFormatError,                "Ljava/lang/ClassFormatError;" },
+        { &gDvm.exError,                           "Ljava/lang/Error;" },
+        { &gDvm.exExceptionInInitializerError,     "Ljava/lang/ExceptionInInitializerError;" },
+        { &gDvm.exFileNotFoundException,           "Ljava/io/FileNotFoundException;" },
+        { &gDvm.exIOException,                     "Ljava/io/IOException;" },
+        { &gDvm.exIllegalAccessError,              "Ljava/lang/IllegalAccessError;" },
+        { &gDvm.exIllegalAccessException,          "Ljava/lang/IllegalAccessException;" },
+        { &gDvm.exIllegalArgumentException,        "Ljava/lang/IllegalArgumentException;" },
+        { &gDvm.exIllegalMonitorStateException,    "Ljava/lang/IllegalMonitorStateException;" },
+        { &gDvm.exIllegalStateException,           "Ljava/lang/IllegalStateException;" },
+        { &gDvm.exIllegalThreadStateException,     "Ljava/lang/IllegalThreadStateException;" },
+        { &gDvm.exIncompatibleClassChangeError,    "Ljava/lang/IncompatibleClassChangeError;" },
+        { &gDvm.exInstantiationError,              "Ljava/lang/InstantiationError;" },
+        { &gDvm.exInstantiationException,          "Ljava/lang/InstantiationException;" },
+        { &gDvm.exInternalError,                   "Ljava/lang/InternalError;" },
+        { &gDvm.exInterruptedException,            "Ljava/lang/InterruptedException;" },
+        { &gDvm.exLinkageError,                    "Ljava/lang/LinkageError;" },
+        { &gDvm.exNegativeArraySizeException,      "Ljava/lang/NegativeArraySizeException;" },
+        { &gDvm.exNoClassDefFoundError,            "Ljava/lang/NoClassDefFoundError;" },
+        { &gDvm.exNoSuchFieldError,                "Ljava/lang/NoSuchFieldError;" },
+        { &gDvm.exNoSuchFieldException,            "Ljava/lang/NoSuchFieldException;" },
+        { &gDvm.exNoSuchMethodError,               "Ljava/lang/NoSuchMethodError;" },
+        { &gDvm.exNullPointerException,            "Ljava/lang/NullPointerException;" },
+        { &gDvm.exOutOfMemoryError,                "Ljava/lang/OutOfMemoryError;" },
+        { &gDvm.exRuntimeException,                "Ljava/lang/RuntimeException;" },
+        { &gDvm.exStackOverflowError,              "Ljava/lang/StackOverflowError;" },
+        { &gDvm.exStaleDexCacheError,              "Ldalvik/system/StaleDexCacheError;" },
+        { &gDvm.exStringIndexOutOfBoundsException, "Ljava/lang/StringIndexOutOfBoundsException;" },
+        { &gDvm.exTypeNotPresentException,         "Ljava/lang/TypeNotPresentException;" },
+        { &gDvm.exUnsatisfiedLinkError,            "Ljava/lang/UnsatisfiedLinkError;" },
+        { &gDvm.exUnsupportedOperationException,   "Ljava/lang/UnsupportedOperationException;" },
+        { &gDvm.exVerifyError,                     "Ljava/lang/VerifyError;" },
+        { &gDvm.exVirtualMachineError,             "Ljava/lang/VirtualMachineError;" },
+
+        /* Other classes */
+        { &gDvm.classJavaLangAnnotationAnnotationArray, "[Ljava/lang/annotation/Annotation;" },
+        { &gDvm.classJavaLangAnnotationAnnotationArrayArray,
+          "[[Ljava/lang/annotation/Annotation;" },
+        { &gDvm.classJavaLangReflectAccessibleObject,   "Ljava/lang/reflect/AccessibleObject;" },
+        { &gDvm.classJavaLangReflectConstructor,        "Ljava/lang/reflect/Constructor;" },
+        { &gDvm.classJavaLangReflectConstructorArray,   "[Ljava/lang/reflect/Constructor;" },
+        { &gDvm.classJavaLangReflectField,              "Ljava/lang/reflect/Field;" },
+        { &gDvm.classJavaLangReflectFieldArray,         "[Ljava/lang/reflect/Field;" },
+        { &gDvm.classJavaLangReflectMethod,             "Ljava/lang/reflect/Method;" },
+        { &gDvm.classJavaLangReflectMethodArray,        "[Ljava/lang/reflect/Method;"},
+        { &gDvm.classJavaLangReflectProxy,              "Ljava/lang/reflect/Proxy;" },
+        { &gDvm.classJavaLangSystem,                    "Ljava/lang/System;" },
+        { &gDvm.classJavaNioDirectByteBuffer,           "Ljava/nio/DirectByteBuffer;" },
+        { &gDvm.classOrgApacheHarmonyDalvikDdmcChunk,   "Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
+        { &gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;" },
+        { &gDvm.classLibcoreReflectAnnotationFactory,     "Llibcore/reflect/AnnotationFactory;" },
+        { &gDvm.classLibcoreReflectAnnotationMember,      "Llibcore/reflect/AnnotationMember;" },
+        { &gDvm.classLibcoreReflectAnnotationMemberArray, "[Llibcore/reflect/AnnotationMember;" },
+
+        { NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; classes[i].ref != NULL; i++) {
+        if (!initClassReference(classes[i].ref, classes[i].name)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initFieldOffset(ClassObject* clazz, int *pOffset,
+        const char* name, const char* type) {
+    int offset = dvmFindFieldOffset(clazz, name, type);
+    if (offset < 0) {
+        ALOGE("Could not find essential field %s.%s of type %s", clazz->descriptor, name, type);
+        return false;
+    }
+
+    *pOffset = offset;
+    return true;
+}
+
+static bool initFieldOffsets() {
+    struct FieldInfo {
+        int* offset;
+        const char* name;
+        const char* type;
+    };
+
+    static struct FieldInfo infoDdmcChunk[] = {
+        { &gDvm.offDalvikDdmcChunk_type,   "type",   "I" },
+        { &gDvm.offDalvikDdmcChunk_data,   "data",   "[B" },
+        { &gDvm.offDalvikDdmcChunk_offset, "offset", "I" },
+        { &gDvm.offDalvikDdmcChunk_length, "length", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoFileDescriptor[] = {
+        { &gDvm.offJavaIoFileDescriptor_descriptor, "descriptor", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoString[] = {
+        { &gDvm.offJavaLangString_value,    "value",    "[C" },
+        { &gDvm.offJavaLangString_count,    "count",    "I" },
+        { &gDvm.offJavaLangString_offset,   "offset",   "I" },
+        { &gDvm.offJavaLangString_hashCode, "hashCode", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThread[] = {
+        { &gDvm.offJavaLangThread_vmThread,           "vmThread",           "Ljava/lang/VMThread;" },
+        { &gDvm.offJavaLangThread_group,              "group",              "Ljava/lang/ThreadGroup;" },
+        { &gDvm.offJavaLangThread_daemon,             "daemon",             "Z" },
+        { &gDvm.offJavaLangThread_name,               "name",               "Ljava/lang/String;" },
+        { &gDvm.offJavaLangThread_priority,           "priority",           "I" },
+        { &gDvm.offJavaLangThread_uncaughtHandler,    "uncaughtHandler",    "Ljava/lang/Thread$UncaughtExceptionHandler;" },
+        { &gDvm.offJavaLangThread_contextClassLoader, "contextClassLoader", "Ljava/lang/ClassLoader;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThreadGroup[] = {
+        { &gDvm.offJavaLangThreadGroup_name,   "name",   "Ljava/lang/String;" },
+        { &gDvm.offJavaLangThreadGroup_parent, "parent", "Ljava/lang/ThreadGroup;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoThrowable[] = {
+        { &gDvm.offJavaLangThrowable_stackState, "stackState", "Ljava/lang/Object;" },
+        { &gDvm.offJavaLangThrowable_cause,      "cause",      "Ljava/lang/Throwable;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoVMThread[] = {
+        { &gDvm.offJavaLangVMThread_thread, "thread", "Ljava/lang/Thread;" },
+        { &gDvm.offJavaLangVMThread_vmData, "vmData", "I" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoFinalizerReference[] = {
+        { &gDvm.offJavaLangRefFinalizerReference_zombie, "zombie", "Ljava/lang/Object;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoConstructor[] = {
+        { &gDvm.offJavaLangReflectConstructor_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectConstructor_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoField[] = {
+        { &gDvm.offJavaLangReflectField_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectField_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoMethod[] = {
+        { &gDvm.offJavaLangReflectMethod_slot,      "slot",           "I" },
+        { &gDvm.offJavaLangReflectMethod_declClass, "declaringClass", "Ljava/lang/Class;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoProxy[] = {
+        { &gDvm.offJavaLangReflectProxy_h, "h", "Ljava/lang/reflect/InvocationHandler;" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct FieldInfo infoBuffer[] = {
+        { &gDvm.offJavaNioBuffer_capacity,               "capacity",               "I" },
+        { &gDvm.offJavaNioBuffer_effectiveDirectAddress, "effectiveDirectAddress", "J" },
+        { NULL, NULL, NULL }
+    };
+
+    static struct { const char* name; const struct FieldInfo* fields; } classes[] = {
+        { "Lorg/apache/harmony/dalvik/ddmc/Chunk;", infoDdmcChunk },
+        { "Ljava/io/FileDescriptor;",               infoFileDescriptor },
+        { "Ljava/lang/String;",                     infoString },
+        { "Ljava/lang/Thread;",                     infoThread },
+        { "Ljava/lang/ThreadGroup;",                infoThreadGroup },
+        { "Ljava/lang/Throwable;",                  infoThrowable },
+        { "Ljava/lang/VMThread;",                   infoVMThread },
+        { "Ljava/lang/ref/FinalizerReference;", infoFinalizerReference },
+        { "Ljava/lang/reflect/Constructor;",        infoConstructor },
+        { "Ljava/lang/reflect/Field;",              infoField },
+        { "Ljava/lang/reflect/Method;",             infoMethod },
+        { "Ljava/lang/reflect/Proxy;",              infoProxy },
+        { "Ljava/nio/Buffer;",                      infoBuffer },
+        { NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; classes[i].name != NULL; i++) {
+        const char* className = classes[i].name;
+        ClassObject* clazz = dvmFindSystemClassNoInit(className);
+        const struct FieldInfo* fields = classes[i].fields;
+
+        if (clazz == NULL) {
+            ALOGE("Could not find essential class %s for field lookup", className);
+            return false;
+        }
+
+        int j;
+        for (j = 0; fields[j].offset != NULL; j++) {
+            if (!initFieldOffset(clazz, fields[j].offset, fields[j].name, fields[j].type)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+static bool initDirectMethodReferenceByClass(Method** pMethod, ClassObject* clazz,
+        const char* name, const char* descriptor) {
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, name, descriptor);
+
+    if (method == NULL) {
+        ALOGE("Could not find essential direct method %s.%s with descriptor %s",
+                clazz->descriptor, name, descriptor);
+        return false;
+    }
+
+    *pMethod = method;
+    return true;
+}
+
+static bool initDirectMethodReference(Method** pMethod, const char* className,
+        const char* name, const char* descriptor) {
+    ClassObject* clazz = dvmFindSystemClassNoInit(className);
+
+    if (clazz == NULL) {
+        ALOGE("Could not find essential class %s for direct method lookup", className);
+        return false;
+    }
+
+    return initDirectMethodReferenceByClass(pMethod, clazz, name, descriptor);
+}
+
+static bool initConstructorReferences() {
+    static struct { Method** method; const char* name; const char* descriptor; } constructors[] = {
+        { &gDvm.methJavaLangStackTraceElement_init, "Ljava/lang/StackTraceElement;",
+          "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V" },
+        { &gDvm.methJavaLangReflectConstructor_init, "Ljava/lang/reflect/Constructor;",
+          "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;II)V" },
+        { &gDvm.methJavaLangReflectField_init, "Ljava/lang/reflect/Field;",
+          "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;II)V" },
+        { &gDvm.methJavaLangReflectMethod_init, "Ljava/lang/reflect/Method;",
+          "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;"
+          "Ljava/lang/String;II)V" },
+        { &gDvm.methJavaNioDirectByteBuffer_init, "Ljava/nio/DirectByteBuffer;",
+          "(JI)V" },
+        { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
+          "Llibcore/reflect/AnnotationMember;",
+          "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V" },
+        { NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; constructors[i].method != NULL; i++) {
+        if (!initDirectMethodReference(constructors[i].method, constructors[i].name,
+                "<init>", constructors[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initDirectMethodReferences() {
+    static struct {
+        Method** method;
+        const char* className;
+        const char* name;
+        const char* descriptor;
+    } methods[] = {
+        { &gDvm.methJavaLangClassLoader_getSystemClassLoader, "Ljava/lang/ClassLoader;",
+          "getSystemClassLoader", "()Ljava/lang/ClassLoader;" },
+        { &gDvm.methJavaLangReflectProxy_constructorPrototype, "Ljava/lang/reflect/Proxy;",
+          "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V" },
+        { &gDvm.methJavaLangSystem_runFinalization, "Ljava/lang/System;",
+          "runFinalization", "()V" },
+
+        { &gDvm.methodTraceGcMethod, "Ldalvik/system/VMDebug;", "startGC", "()V" },
+        { &gDvm.methodTraceClassPrepMethod, "Ldalvik/system/VMDebug;", "startClassPrep", "()V" },
+        { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
+          "Llibcore/reflect/AnnotationFactory;", "createAnnotation",
+          "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)"
+          "Ljava/lang/annotation/Annotation;" },
+        { &gDvm.methDalvikSystemNativeStart_main, "Ldalvik/system/NativeStart;", "main", "([Ljava/lang/String;)V" },
+        { &gDvm.methDalvikSystemNativeStart_run, "Ldalvik/system/NativeStart;", "run", "()V" },
+        { &gDvm.methJavaLangRefFinalizerReferenceAdd,
+          "Ljava/lang/ref/FinalizerReference;", "add", "(Ljava/lang/Object;)V" },
+        { &gDvm.methDalvikDdmcServer_dispatch,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
+        { &gDvm.methDalvikDdmcServer_broadcast,
+          "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "broadcast", "(I)V" },
+        { &gDvm.methJavaLangRefReferenceQueueAdd,
+          "Ljava/lang/ref/ReferenceQueue;", "add", "(Ljava/lang/ref/Reference;)V" },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; methods[i].method != NULL; i++) {
+        if (!initDirectMethodReference(methods[i].method, methods[i].className,
+                methods[i].name, methods[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initVirtualMethodOffset(int* pOffset, const char* className,
+        const char* name, const char* descriptor) {
+    ClassObject* clazz = dvmFindSystemClassNoInit(className);
+
+    if (clazz == NULL) {
+        ALOGE("Could not find essential class %s for virtual method lookup", className);
+        return false;
+    }
+
+    Method* method = dvmFindVirtualMethodByDescriptor(clazz, name, descriptor);
+
+    if (method == NULL) {
+        ALOGE("Could not find essential virtual method %s.%s with descriptor %s",
+                clazz->descriptor, name, descriptor);
+        return false;
+    }
+
+    *pOffset = method->methodIndex;
+    return true;
+}
+
+static bool initVirtualMethodOffsets() {
+    static struct {
+        int* offset;
+        const char* className;
+        const char* name;
+        const char* descriptor;
+    } methods[] = {
+        { &gDvm.voffJavaLangClassLoader_loadClass, "Ljava/lang/ClassLoader;", "loadClass",
+          "(Ljava/lang/String;)Ljava/lang/Class;" },
+        { &gDvm.voffJavaLangObject_equals, "Ljava/lang/Object;", "equals",
+          "(Ljava/lang/Object;)Z" },
+        { &gDvm.voffJavaLangObject_hashCode, "Ljava/lang/Object;", "hashCode", "()I" },
+        { &gDvm.voffJavaLangObject_toString, "Ljava/lang/Object;", "toString",
+          "()Ljava/lang/String;" },
+        { &gDvm.voffJavaLangThread_run, "Ljava/lang/Thread;", "run", "()V" },
+        { &gDvm.voffJavaLangThreadGroup_removeThread, "Ljava/lang/ThreadGroup;",
+          "removeThread", "(Ljava/lang/Thread;)V" },
+        { NULL, NULL, NULL, NULL }
+    };
+
+    int i;
+    for (i = 0; methods[i].offset != NULL; i++) {
+        if (!initVirtualMethodOffset(methods[i].offset, methods[i].className,
+                methods[i].name, methods[i].descriptor)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool initFinalizerReference()
+{
+    gDvm.classJavaLangRefFinalizerReference =
+        dvmFindSystemClass("Ljava/lang/ref/FinalizerReference;");
+    return gDvm.classJavaLangRefFinalizerReference != NULL;
+}
+
+static bool verifyStringOffset(const char* name, int actual, int expected) {
+    if (actual != expected) {
+        ALOGE("InitRefs: String.%s offset = %d; expected %d", name, actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+static bool verifyStringOffsets() {
+    /*
+     * Various parts of the system use predefined constants for the
+     * offsets to a few fields of the class String. This code verifies
+     * that the predefined offsets match what is actually defined by
+     * the class.
+     */
+
+    bool ok = true;
+    ok &= verifyStringOffset("value",    gDvm.offJavaLangString_value,  STRING_FIELDOFF_VALUE);
+    ok &= verifyStringOffset("count",    gDvm.offJavaLangString_count,  STRING_FIELDOFF_COUNT);
+    ok &= verifyStringOffset("offset",   gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
+    ok &= verifyStringOffset("hashCode", gDvm.offJavaLangString_hashCode,
+            STRING_FIELDOFF_HASHCODE);
+
+    return ok;
+}
+
+/* (documented in header) */
+bool dvmFindRequiredClassesAndMembers() {
+    /*
+     * Note: Under normal VM use, this is called by dvmStartup()
+     * in Init.c. For dex optimization, this is called as well, but in
+     * that case, the call is made from DexPrepare.c.
+     */
+
+    return initClassReferences()
+        && initFieldOffsets()
+        && initConstructorReferences()
+        && initDirectMethodReferences()
+        && initVirtualMethodOffsets()
+        && initFinalizerReference()
+        && verifyStringOffsets();
+}
+
+/* (documented in header) */
+bool dvmFindReferenceMembers(ClassObject* classReference) {
+    if (strcmp(classReference->descriptor, "Ljava/lang/ref/Reference;") != 0) {
+        ALOGE("Attempt to set up the wrong class as Reference");
+        return false;
+    }
+    return initFieldOffset(classReference, &gDvm.offJavaLangRefReference_pendingNext,
+                "pendingNext", "Ljava/lang/ref/Reference;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queue,
+                "queue", "Ljava/lang/ref/ReferenceQueue;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queueNext,
+                "queueNext", "Ljava/lang/ref/Reference;")
+        && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_referent,
+                "referent", "Ljava/lang/Object;");
+}
diff --git a/vm/InlineNative.cpp b/vm/InlineNative.cpp
new file mode 100644
index 0000000..5fe3eef
--- /dev/null
+++ b/vm/InlineNative.cpp
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Inlined native functions.  These definitions replace interpreted or
+ * native implementations at runtime; "intrinsic" might be a better word.
+ */
+#include "Dalvik.h"
+
+#include <math.h>
+
+/*
+ * Some notes on "inline" functions.
+ *
+ * These are NOT simply native implementations.  A full method definition
+ * must still be provided.  Depending on the flags passed into the VM
+ * at runtime, the original or inline version may be selected by the
+ * DEX optimizer.
+ *
+ * PLEASE DO NOT use this as the default location for native methods.
+ * The difference between this and an "internal native" static method
+ * call on a 200MHz ARM 9 is roughly 370ns vs. 700ns.  The code here
+ * "secretly replaces" the other method, so you can't avoid having two
+ * implementations.  Since the DEX optimizer mode can't be known ahead
+ * of time, both implementations must be correct and complete.
+ *
+ * The only stuff that really needs to be here are methods that
+ * are high-volume or must be low-overhead, e.g. certain String/Math
+ * methods and some java.util.concurrent.atomic operations.
+ *
+ * Normally, a class is loaded and initialized the first time a static
+ * method is invoked.  This property is NOT preserved here.  If you need
+ * to access a static field in a class, you must ensure initialization
+ * yourself (cheap/easy way is to check the resolved-methods table, and
+ * resolve the method if it hasn't been).
+ *
+ * DO NOT replace "synchronized" methods.  We do not support method
+ * synchronization here.
+ *
+ * DO NOT perform any allocations or do anything that could cause a
+ * garbage collection.  The method arguments are not visible to the GC
+ * and will not be pinned or updated when memory blocks move.  You are
+ * allowed to allocate and throw an exception so long as you only do so
+ * immediately before returning.
+ *
+ * Remember that these functions are executing while the thread is in
+ * the "RUNNING" state, not the "NATIVE" state.  If you perform a blocking
+ * operation you can stall the entire VM if the GC or debugger wants to
+ * suspend the thread.  Since these are arguably native implementations
+ * rather than VM internals, prefer NATIVE to VMWAIT if you want to change
+ * the thread state.
+ *
+ * Always write results to 32-bit or 64-bit fields in "pResult", e.g. do
+ * not write boolean results to pResult->z.  The interpreter expects
+ * 32 or 64 bits to be set.
+ *
+ * Inline op methods return "false" if an exception was thrown, "true" if
+ * everything went well.
+ *
+ * DO NOT provide implementations of methods that can be overridden by a
+ * subclass, as polymorphism does not work correctly.  For safety you should
+ * only provide inline functions for classes/methods declared "final".
+ *
+ * It's best to avoid inlining the overridden version of a method.  For
+ * example, String.hashCode() is inherited from Object.hashCode().  Code
+ * calling String.hashCode() through an Object reference will run the
+ * "slow" version, while calling it through a String reference gets
+ * the inlined version.  It's best to have just one version unless there
+ * are clear performance gains.
+ *
+ * Because the actual method is not called, debugger breakpoints on these
+ * methods will not happen.  (TODO: have the code here find the original
+ * method and call it when the debugger is active.)  Additional steps have
+ * been taken to allow method profiling to produce correct results.
+ */
+
+
+/*
+ * ===========================================================================
+ *      org.apache.harmony.dalvik.NativeTestTarget
+ * ===========================================================================
+ */
+
+/*
+ * public static void emptyInlineMethod
+ *
+ * This exists only for benchmarks.
+ */
+static bool org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod(
+    u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+{
+    // do nothing
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      java.lang.String
+ * ===========================================================================
+ */
+
+/*
+ * public char charAt(int index)
+ */
+bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    int count, offset;
+    ArrayObject* chars;
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    //ALOGI("String.charAt this=0x%08x index=%d", arg0, arg1);
+    count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    if ((s4) arg1 < 0 || (s4) arg1 >= count) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithIndex(count, arg1);
+        return false;
+    } else {
+        offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+        chars = (ArrayObject*)
+            dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+
+        pResult->i = ((const u2*)(void*)chars->contents)[arg1 + offset];
+        return true;
+    }
+}
+
+/*
+ * public int compareTo(String s)
+ */
+bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /*
+     * Null reference check on "this".  Normally this is performed during
+     * the setup of the virtual method call.  We need to do it before
+     * anything else.  While we're at it, check out the other string,
+     * which must also be non-null.
+     */
+    if ((Object*) arg0 == NULL || (Object*) arg1 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    /* quick test for comparison with itself */
+    if (arg0 == arg1) {
+        pResult->i = 0;
+        return true;
+    }
+
+    /*
+     * This would be simpler and faster if we promoted StringObject to
+     * a full representation, lining up the C structure fields with the
+     * actual object fields.
+     */
+    int thisCount, thisOffset, compCount, compOffset;
+    ArrayObject* thisArray;
+    ArrayObject* compArray;
+    const u2* thisChars;
+    const u2* compChars;
+    int minCount, countDiff;
+
+    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+    countDiff = thisCount - compCount;
+    minCount = (countDiff < 0) ? thisCount : compCount;
+    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+    thisArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+    compArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
+    compChars = ((const u2*)(void*)compArray->contents) + compOffset;
+
+    /*
+     * Straightforward implementation, examining 16 bits at a time.  Compare
+     * the characters that overlap, and if they're all the same then return
+     * the difference in lengths.
+     */
+    int i;
+    for (i = 0; i < minCount; i++) {
+        if (thisChars[i] != compChars[i]) {
+            pResult->i = (s4) thisChars[i] - (s4) compChars[i];
+            return true;
+        }
+    }
+
+    pResult->i = countDiff;
+    return true;
+}
+
+/*
+ * public boolean equals(Object anObject)
+ */
+bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /*
+     * Null reference check on "this".
+     */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    /* quick test for comparison with itself */
+    if (arg0 == arg1) {
+        pResult->i = true;
+        return true;
+    }
+
+    /*
+     * See if the other object is also a String.
+     *
+     * str.equals(null) is expected to return false, presumably based on
+     * the results of the instanceof test.
+     */
+    if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) {
+        pResult->i = false;
+        return true;
+    }
+
+    /*
+     * This would be simpler and faster if we promoted StringObject to
+     * a full representation, lining up the C structure fields with the
+     * actual object fields.
+     */
+    int thisCount, thisOffset, compCount, compOffset;
+    ArrayObject* thisArray;
+    ArrayObject* compArray;
+    const u2* thisChars;
+    const u2* compChars;
+
+    /* quick length check */
+    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+    if (thisCount != compCount) {
+        pResult->i = false;
+        return true;
+    }
+
+    /*
+     * You may, at this point, be tempted to pull out the hashCode fields
+     * and compare them.  If both fields have been initialized, and they
+     * are not equal, we can return false immediately.
+     *
+     * However, the hashCode field is often not set.  If it is set,
+     * there's an excellent chance that the String is being used as a key
+     * in a hashed data structure (e.g. HashMap).  That data structure has
+     * already made the comparison and determined that the hashes are equal,
+     * making a check here redundant.
+     *
+     * It's not clear that checking the hashes will be a win in "typical"
+     * use cases.  We err on the side of simplicity and ignore them.
+     */
+
+    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+    thisArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+    compArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
+    compChars = ((const u2*)(void*)compArray->contents) + compOffset;
+
+    /*
+     * Straightforward implementation, examining 16 bits at a time.  The
+     * direction of the loop doesn't matter, and starting at the end may
+     * give us an advantage when comparing certain types of strings (e.g.
+     * class names).
+     *
+     * We want to go forward for benchmarks against __memcmp16 so we get a
+     * meaningful comparison when the strings don't match (could also test
+     * with palindromes).
+     */
+    int i;
+    //for (i = 0; i < thisCount; i++)
+    for (i = thisCount-1; i >= 0; --i)
+    {
+        if (thisChars[i] != compChars[i]) {
+            pResult->i = false;
+            return true;
+        }
+    }
+    pResult->i = true;
+
+    return true;
+}
+
+extern "C" int __memcmp16(const unsigned short* lhs, const unsigned short* rhs, size_t n) {
+    for (size_t i = 0; i < n; i++) {
+        if (*lhs != *rhs) {
+            return *lhs - *rhs;
+        }
+        lhs++;
+        rhs++;
+    }
+    return 0;
+}
+
+/*
+ * public int length()
+ */
+bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //ALOGI("String.length this=0x%08x pResult=%p", arg0, pResult);
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    return true;
+}
+
+/*
+ * public boolean isEmpty()
+ */
+bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //ALOGI("String.isEmpty this=0x%08x pResult=%p", arg0, pResult);
+
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = (dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT) == 0);
+    return true;
+}
+
+/*
+ * Determine the index of the first character matching "ch".  The string
+ * to search is described by "chars", "offset", and "count".
+ *
+ * The character must be <= 0xffff. Supplementary characters are handled in
+ * Java.
+ *
+ * The "start" parameter must be clamped to [0..count].
+ *
+ * Returns -1 if no match is found.
+ */
+static inline int indexOfCommon(Object* strObj, int ch, int start)
+{
+    //if ((ch & 0xffff) != ch)        /* 32-bit code point */
+    //    return -1;
+
+    /* pull out the basic elements */
+    ArrayObject* charArray =
+        (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
+    const u2* chars = (const u2*)(void*)charArray->contents;
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    //ALOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d",
+    //    (u4) strObj, ch, start, offset, count);
+
+    /* factor out the offset */
+    chars += offset;
+
+    if (start < 0)
+        start = 0;
+    else if (start > count)
+        start = count;
+
+#if 0
+    /* 16-bit loop, simple */
+    while (start < count) {
+        if (chars[start] == ch)
+            return start;
+        start++;
+    }
+#else
+    /* 16-bit loop, slightly better on ARM */
+    const u2* ptr = chars + start;
+    const u2* endPtr = chars + count;
+    while (ptr < endPtr) {
+        if (*ptr++ == ch)
+            return (ptr-1) - chars;
+    }
+#endif
+
+    return -1;
+}
+
+/*
+ * public int indexOf(int c, int start)
+ *
+ * Scan forward through the string for a matching character.
+ * The character must be <= 0xffff; this method does not handle supplementary
+ * characters.
+ */
+bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /* null reference check on "this" */
+    if ((Object*) arg0 == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+
+    pResult->i = indexOfCommon((Object*) arg0, arg1, arg2);
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      java.lang.Math
+ * ===========================================================================
+ */
+
+union Convert32 {
+    u4 arg;
+    float ff;
+};
+
+union Convert64 {
+    u4 arg[2];
+    s8 ll;
+    double dd;
+};
+
+/*
+ * public static int abs(int)
+ */
+bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    s4 val = (s4) arg0;
+    pResult->i = (val >= 0) ? val : -val;
+    return true;
+}
+
+/*
+ * public static long abs(long)
+ */
+bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    s8 val = convert.ll;
+    pResult->j = (val >= 0) ? val : -val;
+    return true;
+}
+
+/*
+ * public static float abs(float)
+ */
+bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert32 convert;
+    /* clear the sign bit; assumes a fairly common fp representation */
+    convert.arg = arg0 & 0x7fffffff;
+    pResult->f = convert.ff;
+    return true;
+}
+
+/*
+ * public static double abs(double)
+ */
+bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    /* clear the sign bit in the (endian-dependent) high word */
+    convert.ll &= 0x7fffffffffffffffULL;
+    pResult->d = convert.dd;
+    return true;
+}
+
+/*
+ * public static int min(int)
+ */
+bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    pResult->i = ((s4) arg0 < (s4) arg1) ? arg0 : arg1;
+    return true;
+}
+
+/*
+ * public static int max(int)
+ */
+bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    pResult->i = ((s4) arg0 > (s4) arg1) ? arg0 : arg1;
+    return true;
+}
+
+/*
+ * public static double sqrt(double)
+ *
+ * With ARM VFP enabled, gcc turns this into an fsqrtd instruction, followed
+ * by an fcmpd of the result against itself.  If it doesn't match (i.e.
+ * it's NaN), the libm sqrt() is invoked.
+ */
+bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = sqrt(convert.dd);
+    return true;
+}
+
+/*
+ * public static double cos(double)
+ */
+bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = cos(convert.dd);
+    return true;
+}
+
+/*
+ * public static double sin(double)
+ */
+bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = sin(convert.dd);
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      java.lang.Float
+ * ===========================================================================
+ */
+
+bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert32 convert;
+    convert.arg = arg0;
+    pResult->i = isnanf(convert.ff) ? 0x7fc00000 : arg0;
+    return true;
+}
+
+bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    pResult->i = arg0;
+    return true;
+}
+
+bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert32 convert;
+    convert.arg = arg0;
+    pResult->f = convert.ff;
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      java.lang.Double
+ * ===========================================================================
+ */
+
+bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->j = isnan(convert.dd) ? 0x7ff8000000000000LL : convert.ll;
+    return true;
+}
+
+bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2,
+    u4 arg, JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->j = convert.ll;
+    return true;
+}
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    Convert64 convert;
+    convert.arg[0] = arg0;
+    convert.arg[1] = arg1;
+    pResult->d = convert.dd;
+    return true;
+}
+
+/*
+ * ===========================================================================
+ *      Infrastructure
+ * ===========================================================================
+ */
+
+/*
+ * Table of methods.
+ *
+ * The DEX optimizer uses the class/method/signature string fields to decide
+ * which calls it can trample.  The interpreter just uses the function
+ * pointer field.
+ *
+ * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make
+ * changes to this table.
+ *
+ * NOTE: If present, the JIT will also need to know about changes
+ * to this table.  Update the NativeInlineOps enum in InlineNative.h and
+ * the dispatch code in compiler/codegen/<target>/Codegen.c.
+ */
+const InlineOperation gDvmInlineOpsTable[] = {
+    { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod,
+        "Lorg/apache/harmony/dalvik/NativeTestTarget;",
+        "emptyInlineMethod", "()V" },
+
+    { javaLangString_charAt, "Ljava/lang/String;", "charAt", "(I)C" },
+    { javaLangString_compareTo, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I" },
+    { javaLangString_equals, "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" },
+    { javaLangString_fastIndexOf_II, "Ljava/lang/String;", "fastIndexOf", "(II)I" },
+    { javaLangString_isEmpty, "Ljava/lang/String;", "isEmpty", "()Z" },
+    { javaLangString_length, "Ljava/lang/String;", "length", "()I" },
+
+    { javaLangMath_abs_int, "Ljava/lang/Math;", "abs", "(I)I" },
+    { javaLangMath_abs_long, "Ljava/lang/Math;", "abs", "(J)J" },
+    { javaLangMath_abs_float, "Ljava/lang/Math;", "abs", "(F)F" },
+    { javaLangMath_abs_double, "Ljava/lang/Math;", "abs", "(D)D" },
+    { javaLangMath_min_int, "Ljava/lang/Math;", "min", "(II)I" },
+    { javaLangMath_max_int, "Ljava/lang/Math;", "max", "(II)I" },
+    { javaLangMath_sqrt, "Ljava/lang/Math;", "sqrt", "(D)D" },
+    { javaLangMath_cos, "Ljava/lang/Math;", "cos", "(D)D" },
+    { javaLangMath_sin, "Ljava/lang/Math;", "sin", "(D)D" },
+
+    { javaLangFloat_floatToIntBits, "Ljava/lang/Float;", "floatToIntBits", "(F)I" },
+    { javaLangFloat_floatToRawIntBits, "Ljava/lang/Float;", "floatToRawIntBits", "(F)I" },
+    { javaLangFloat_intBitsToFloat, "Ljava/lang/Float;", "intBitsToFloat", "(I)F" },
+
+    { javaLangDouble_doubleToLongBits, "Ljava/lang/Double;", "doubleToLongBits", "(D)J" },
+    { javaLangDouble_doubleToRawLongBits, "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J" },
+    { javaLangDouble_longBitsToDouble, "Ljava/lang/Double;", "longBitsToDouble", "(J)D" },
+
+    // These are implemented exactly the same in Math and StrictMath,
+    // so we can make the StrictMath calls fast too. Note that this
+    // isn't true in general!
+    { javaLangMath_abs_int, "Ljava/lang/StrictMath;", "abs", "(I)I" },
+    { javaLangMath_abs_long, "Ljava/lang/StrictMath;", "abs", "(J)J" },
+    { javaLangMath_abs_float, "Ljava/lang/StrictMath;", "abs", "(F)F" },
+    { javaLangMath_abs_double, "Ljava/lang/StrictMath;", "abs", "(D)D" },
+    { javaLangMath_min_int, "Ljava/lang/StrictMath;", "min", "(II)I" },
+    { javaLangMath_max_int, "Ljava/lang/StrictMath;", "max", "(II)I" },
+    { javaLangMath_sqrt, "Ljava/lang/StrictMath;", "sqrt", "(D)D" },
+};
+
+/*
+ * Allocate some tables.
+ */
+bool dvmInlineNativeStartup()
+{
+    gDvm.inlinedMethods =
+        (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
+    if (gDvm.inlinedMethods == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free generated tables.
+ */
+void dvmInlineNativeShutdown()
+{
+    free(gDvm.inlinedMethods);
+}
+
+
+/*
+ * Get a pointer to the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable()
+{
+    return gDvmInlineOpsTable;
+}
+
+/*
+ * Get the number of entries in the inlineops table.
+ */
+int dvmGetInlineOpsTableLength()
+{
+    return NELEM(gDvmInlineOpsTable);
+}
+
+Method* dvmFindInlinableMethod(const char* classDescriptor,
+    const char* methodName, const char* methodSignature)
+{
+    /*
+     * Find the class.
+     */
+    ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL);
+    if (clazz == NULL) {
+        ALOGE("dvmFindInlinableMethod: can't find class '%s'",
+            classDescriptor);
+        dvmClearException(dvmThreadSelf());
+        return NULL;
+    }
+
+    /*
+     * Method could be virtual or direct.  Try both.  Don't use
+     * the "hier" versions.
+     */
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName,
+        methodSignature);
+    if (method == NULL) {
+        method = dvmFindVirtualMethodByDescriptor(clazz, methodName,
+            methodSignature);
+    }
+    if (method == NULL) {
+        ALOGE("dvmFindInlinableMethod: can't find method %s.%s %s",
+            clazz->descriptor, methodName, methodSignature);
+        return NULL;
+    }
+
+    /*
+     * Check that the method is appropriate for inlining.
+     */
+    if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+        ALOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s",
+            clazz->descriptor, method->name);
+        return NULL;
+    }
+    if (dvmIsSynchronizedMethod(method) ||
+            dvmIsDeclaredSynchronizedMethod(method)) {
+        ALOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s",
+            clazz->descriptor, method->name);
+        return NULL;
+    }
+
+    return method;
+}
+
+/*
+ * Populate the methods table on first use.  It's possible the class
+ * hasn't been resolved yet, so we need to do the full "calling the
+ * method for the first time" routine.  (It's probably okay to skip
+ * the access checks.)
+ *
+ * Currently assuming that we're only inlining stuff loaded by the
+ * bootstrap class loader.  This is a safe assumption for many reasons.
+ */
+Method* dvmResolveInlineNative(int opIndex)
+{
+    assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable));
+    Method* method = gDvm.inlinedMethods[opIndex];
+    if (method != NULL) {
+        return method;
+    }
+
+    method = dvmFindInlinableMethod(
+        gDvmInlineOpsTable[opIndex].classDescriptor,
+        gDvmInlineOpsTable[opIndex].methodName,
+        gDvmInlineOpsTable[opIndex].methodSignature);
+
+    if (method == NULL) {
+        /* We already reported the error. */
+        return NULL;
+    }
+
+    gDvm.inlinedMethods[opIndex] = method;
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGV("Registered for profile: %s.%s %s",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    return method;
+}
+
+/*
+ * Make an inline call for the "debug" interpreter, used when the debugger
+ * or profiler is active.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex)
+{
+    Method* method = dvmResolveInlineNative(opIndex);
+    if (method == NULL) {
+        return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+            pResult);
+    }
+
+    Thread* self = dvmThreadSelf();
+    TRACE_METHOD_ENTER(self, method);
+    bool result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+        pResult);
+    TRACE_METHOD_EXIT(self, method);
+    return result;
+}
diff --git a/vm/InlineNative.h b/vm/InlineNative.h
new file mode 100644
index 0000000..fe14f8b
--- /dev/null
+++ b/vm/InlineNative.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Inlined native functions.
+ */
+#ifndef DALVIK_INLINENATIVE_H_
+#define DALVIK_INLINENATIVE_H_
+
+/* startup/shutdown */
+bool dvmInlineNativeStartup(void);
+void dvmInlineNativeShutdown(void);
+
+Method* dvmFindInlinableMethod(const char* classDescriptor,
+    const char* methodName, const char* methodSignature);
+
+/*
+ * Basic 4-argument inline operation handler.
+ */
+typedef bool (*InlineOp4Func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult);
+
+/*
+ * Table of inline operations.
+ *
+ * Try to keep this at a power-of-two size, so we don't have to multiply.
+ *
+ * TODO: might be to our advantage to generate a compact jump table on
+ * the heap at runtime (or just declare two static tables, one with full
+ * info and one with just function pointers).  Especially useful if we decide
+ * to support other method call forms, e.g. /range.  We can also just
+ * generate assembly code that knows how many args it needs and has the
+ * target address embedded.
+ */
+struct InlineOperation {
+    InlineOp4Func   func;               /* MUST be first entry */
+    const char*     classDescriptor;
+    const char*     methodName;
+    const char*     methodSignature;
+};
+
+/*
+ * Must be kept in sync w/ gDvmInlineOpsTable in InlineNative.cpp
+ *
+ * You should also add a test to libcore's IntrinsicTest.
+ */
+enum NativeInlineOps {
+    INLINE_EMPTYINLINEMETHOD = 0,
+    INLINE_STRING_CHARAT = 1,
+    INLINE_STRING_COMPARETO = 2,
+    INLINE_STRING_EQUALS = 3,
+    INLINE_STRING_FASTINDEXOF_II = 4,
+    INLINE_STRING_IS_EMPTY = 5,
+    INLINE_STRING_LENGTH = 6,
+    INLINE_MATH_ABS_INT = 7,
+    INLINE_MATH_ABS_LONG = 8,
+    INLINE_MATH_ABS_FLOAT = 9,
+    INLINE_MATH_ABS_DOUBLE = 10,
+    INLINE_MATH_MIN_INT = 11,
+    INLINE_MATH_MAX_INT = 12,
+    INLINE_MATH_SQRT = 13,
+    INLINE_MATH_COS = 14,
+    INLINE_MATH_SIN = 15,
+    INLINE_FLOAT_TO_INT_BITS = 16,
+    INLINE_FLOAT_TO_RAW_INT_BITS = 17,
+    INLINE_INT_BITS_TO_FLOAT = 18,
+    INLINE_DOUBLE_TO_LONG_BITS = 19,
+    INLINE_DOUBLE_TO_RAW_LONG_BITS = 20,
+    INLINE_LONG_BITS_TO_DOUBLE = 21,
+    INLINE_STRICT_MATH_ABS_INT = 22,
+    INLINE_STRICT_MATH_ABS_LONG = 23,
+    INLINE_STRICT_MATH_ABS_FLOAT = 24,
+    INLINE_STRICT_MATH_ABS_DOUBLE = 25,
+    INLINE_STRICT_MATH_MIN_INT = 26,
+    INLINE_STRICT_MATH_MAX_INT = 27,
+    INLINE_STRICT_MATH_SQRT = 28,
+};
+
+/*
+ * Get the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable(void);
+int dvmGetInlineOpsTableLength(void);
+
+/*
+ * The table, exposed so we can access it with C inlines.  Prefer access
+ * through dvmGetInlineOpsTable().
+ */
+extern const InlineOperation gDvmInlineOpsTable[];
+
+/*
+ * Perform the operation specified by "opIndex".
+ *
+ * We want the arguments to appear in the first 4 registers so they can
+ * be passed straight through to the handler function.  Ideally on ARM
+ * they'll go into r0-r3 and stay there.
+ *
+ * Returns "true" if everything went normally, "false" if an exception
+ * was thrown.
+ */
+INLINE bool dvmPerformInlineOp4Std(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex)
+{
+    return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
+}
+
+/*
+ * Like the "std" version, but will emit profiling info.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult, int opIndex);
+
+/*
+ * Return method & populate the table on first use.
+ */
+extern "C" Method* dvmResolveInlineNative(int opIndex);
+
+/*
+ * The actual inline native definitions.
+ */
+bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                              JValue* pResult);
+
+bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                            JValue* pResult);
+
+bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                                   JValue* pResult);
+
+bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                           JValue* pResult);
+
+bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                            JValue* pResult);
+
+bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                             JValue* pResult);
+
+bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                          JValue* pResult);
+
+bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                       JValue* pResult);
+
+bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                      JValue* pResult);
+
+bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+                      JValue* pResult);
+
+bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                  JValue* pResult);
+
+bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                  JValue* pResult);
+
+bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2,
+                                        u4 arg, JValue* pResult);
+
+bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+                                     JValue* pResult);
+
+#endif  // DALVIK_INLINENATIVE_H_
diff --git a/vm/Inlines.cpp b/vm/Inlines.cpp
new file mode 100644
index 0000000..3ab7717
--- /dev/null
+++ b/vm/Inlines.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Generate non-inline copies of inline functions declared in header files.
+ */
+
+#define _DALVIK_GEN_INLINES
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "mterp/c/header.cpp"
+
+#undef LOG_TAG
+#include "jdwp/JdwpPriv.h"
diff --git a/vm/Inlines.h b/vm/Inlines.h
new file mode 100644
index 0000000..f1580e3
--- /dev/null
+++ b/vm/Inlines.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * In gcc, "extern inline" ensures that the copy in the header is never
+ * turned into a separate function.  This prevents us from having multiple
+ * non-inline copies.  However, we still need to provide a non-inline
+ * version in the library for the benefit of applications that include our
+ * headers and are built with optimizations disabled.  Either that, or use
+ * the "always_inline" gcc attribute to ensure that the non-inline version
+ * is never needed.
+ *
+ * (Note C99 has different notions about what the keyword combos mean.)
+ */
+#ifndef _DALVIK_GEN_INLINES             /* only defined by Inlines.c */
+# define INLINE extern __inline__
+#else
+# define INLINE
+#endif
diff --git a/vm/Intern.cpp b/vm/Intern.cpp
new file mode 100644
index 0000000..7754bb3
--- /dev/null
+++ b/vm/Intern.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * String interning.
+ */
+#include "Dalvik.h"
+
+#include <stddef.h>
+
+/*
+ * Prep string interning.
+ */
+bool dvmStringInternStartup()
+{
+    dvmInitMutex(&gDvm.internLock);
+    gDvm.internedStrings = dvmHashTableCreate(256, NULL);
+    if (gDvm.internedStrings == NULL)
+        return false;
+    gDvm.literalStrings = dvmHashTableCreate(256, NULL);
+    if (gDvm.literalStrings == NULL)
+        return false;
+    return true;
+}
+
+/*
+ * Chuck the intern list.
+ *
+ * The contents of the list are StringObjects that live on the GC heap.
+ */
+void dvmStringInternShutdown()
+{
+    if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) {
+        dvmDestroyMutex(&gDvm.internLock);
+    }
+    dvmHashTableFree(gDvm.internedStrings);
+    gDvm.internedStrings = NULL;
+    dvmHashTableFree(gDvm.literalStrings);
+    gDvm.literalStrings = NULL;
+}
+
+static StringObject* lookupString(HashTable* table, u4 key, StringObject* value)
+{
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, false);
+    return (StringObject*)entry;
+}
+
+static StringObject* insertString(HashTable* table, u4 key, StringObject* value)
+{
+    if (dvmIsNonMovingObject(value) == false) {
+        value = (StringObject*)dvmCloneObject(value, ALLOC_NON_MOVING);
+    }
+    void* entry = dvmHashTableLookup(table, key, (void*)value,
+                                     dvmHashcmpStrings, true);
+    return (StringObject*)entry;
+}
+
+static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral)
+{
+    StringObject* found;
+
+    assert(strObj != NULL);
+    u4 key = dvmComputeStringHash(strObj);
+    dvmLockMutex(&gDvm.internLock);
+    if (isLiteral) {
+        /*
+         * Check the literal table for a match.
+         */
+        StringObject* literal = lookupString(gDvm.literalStrings, key, strObj);
+        if (literal != NULL) {
+            /*
+             * A match was found in the literal table, the easy case.
+             */
+            found = literal;
+        } else {
+            /*
+             * There is no match in the literal table, check the
+             * interned string table.
+             */
+            StringObject* interned = lookupString(gDvm.internedStrings, key, strObj);
+            if (interned != NULL) {
+                /*
+                 * A match was found in the interned table.  Move the
+                 * matching string to the literal table.
+                 */
+                dvmHashTableRemove(gDvm.internedStrings, key, interned);
+                found = insertString(gDvm.literalStrings, key, interned);
+                assert(found == interned);
+            } else {
+                /*
+                 * No match in the literal table or the interned
+                 * table.  Insert into the literal table.
+                 */
+                found = insertString(gDvm.literalStrings, key, strObj);
+                assert(found == strObj);
+            }
+        }
+    } else {
+        /*
+         * Check the literal table for a match.
+         */
+        found = lookupString(gDvm.literalStrings, key, strObj);
+        if (found == NULL) {
+            /*
+             * No match was found in the literal table.  Insert into
+             * the intern table if it does not already exist.
+             */
+            found = insertString(gDvm.internedStrings, key, strObj);
+        }
+    }
+    assert(found != NULL);
+    dvmUnlockMutex(&gDvm.internLock);
+    return found;
+}
+
+/*
+ * Find an entry in the interned string table.
+ *
+ * If the string doesn't already exist, the StringObject is added to
+ * the table.  Otherwise, the existing entry is returned.
+ */
+StringObject* dvmLookupInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, false);
+}
+
+/*
+ * Same as dvmLookupInternedString(), but guarantees that the
+ * returned string is a literal.
+ */
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj)
+{
+    return lookupInternedString(strObj, true);
+}
+
+/*
+ * Returns true if the object is a weak interned string.  Any string
+ * interned by the user is weak.
+ */
+bool dvmIsWeakInternedString(StringObject* strObj)
+{
+    assert(strObj != NULL);
+    if (gDvm.internedStrings == NULL) {
+        return false;
+    }
+    dvmLockMutex(&gDvm.internLock);
+    u4 key = dvmComputeStringHash(strObj);
+    StringObject* found = lookupString(gDvm.internedStrings, key, strObj);
+    dvmUnlockMutex(&gDvm.internLock);
+    return found == strObj;
+}
+
+/*
+ * Clear white references from the intern table.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *))
+{
+    /* It's possible for a GC to happen before dvmStringInternStartup()
+     * is called.
+     */
+    if (gDvm.internedStrings != NULL) {
+        dvmLockMutex(&gDvm.internLock);
+        dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject);
+        dvmUnlockMutex(&gDvm.internLock);
+    }
+}
diff --git a/vm/Intern.h b/vm/Intern.h
new file mode 100644
index 0000000..9944e5c
--- /dev/null
+++ b/vm/Intern.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interned strings.
+ */
+#ifndef DALVIK_INTERN_H_
+#define DALVIK_INTERN_H_
+
+bool dvmStringInternStartup(void);
+void dvmStringInternShutdown(void);
+StringObject* dvmLookupInternedString(StringObject* strObj);
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
+bool dvmIsWeakInternedString(StringObject* strObj);
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
+
+#endif  // DALVIK_INTERN_H_
diff --git a/vm/JarFile.cpp b/vm/JarFile.cpp
new file mode 100644
index 0000000..aabb50f
--- /dev/null
+++ b/vm/JarFile.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Access the contents of a Jar file.
+ *
+ * This isn't actually concerned with any of the Jar-like elements; it
+ * just wants a zip archive with "classes.dex" inside.  In Android the
+ * most common example is ".apk".
+ */
+
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "JarFile.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static const char* kDexInJarName = "classes.dex";
+
+/*
+ * Attempt to open a file whose name is similar to <fileName>,
+ * but with the supplied suffix.  E.g.,
+ * openAlternateSuffix("Home.apk", "dex", O_RDONLY) will attempt
+ * to open "Home.dex".  If the open succeeds, a pointer to a
+ * malloc()ed copy of the opened file name will be put in <*pCachedName>.
+ *
+ * <flags> is passed directly to open(). O_CREAT is not supported.
+ */
+static int openAlternateSuffix(const char *fileName, const char *suffix,
+    int flags, char **pCachedName)
+{
+    char *buf, *c;
+    size_t fileNameLen = strlen(fileName);
+    size_t suffixLen = strlen(suffix);
+    size_t bufLen = fileNameLen + suffixLen + 1;
+    int fd = -1;
+
+    buf = (char*)malloc(bufLen);
+    if (buf == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    /* Copy the original filename into the buffer, find
+     * the last dot, and copy the suffix to just after it.
+     */
+    memcpy(buf, fileName, fileNameLen + 1);
+    c = strrchr(buf, '.');
+    if (c == NULL) {
+        errno = ENOENT;
+        goto bail;
+    }
+    memcpy(c + 1, suffix, suffixLen + 1);
+
+    fd = open(buf, flags);
+    if (fd >= 0) {
+        *pCachedName = buf;
+        return fd;
+    }
+    ALOGV("Couldn't open %s: %s", buf, strerror(errno));
+bail:
+    free(buf);
+    return -1;
+}
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ *
+ * Note: This should parallel the logic of dvmJarFileOpen.
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName)
+{
+    ZipArchiveHandle archive;
+    char* cachedName = NULL;
+    int fd;
+    DexCacheStatus result = DEX_CACHE_ERROR;
+    ZipEntry entry;
+
+    /* Always treat elements of the bootclasspath as up-to-date.
+     * The fact that interpreted code is running at all means that this
+     * should be true.
+     */
+    if (dvmClassPathContains(gDvm.bootClassPath, fileName)) {
+        return DEX_CACHE_OK;
+    }
+
+    /* Try to find the dex file inside of the archive.
+     */
+    if (dexZipOpenArchive(fileName, &archive) != 0) {
+        return DEX_CACHE_BAD_ARCHIVE;
+    }
+
+    /* First, look for a ".odex" alongside the jar file.  It will
+     * have the same name/path except for the extension.
+     */
+    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
+    if (fd >= 0) {
+        ALOGV("Using alternate file (odex) for %s ...", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            ALOGE("%s odex has stale dependencies", fileName);
+            free(cachedName);
+            cachedName = NULL;
+            close(fd);
+            fd = -1;
+            goto tryArchive;
+        } else {
+            ALOGV("%s odex has good dependencies", fileName);
+        }
+    } else {
+
+tryArchive:
+        /*
+         * Pre-created .odex absent or stale.  Look inside the jar for a
+         * "classes.dex".
+         */
+        if (dexZipFindEntry(archive, kDexInJarName, &entry) == 0) {
+            bool newFile = false;
+
+            /*
+             * See if there's an up-to-date copy of the optimized dex
+             * in the cache, but don't create one if there isn't.
+             */
+            ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName);
+            cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);
+            if (cachedName == NULL)
+                return DEX_CACHE_BAD_ARCHIVE;
+
+            fd = dvmOpenCachedDexFile(fileName, cachedName,
+                    entry.mod_time, entry.crc32,
+                    /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false);
+            ALOGV("dvmOpenCachedDexFile returned fd %d", fd);
+            if (fd < 0) {
+                result = DEX_CACHE_STALE;
+                goto bail;
+            }
+
+            /* dvmOpenCachedDexFile locks the file as a side-effect.
+             * Unlock and close it.
+             */
+            if (!dvmUnlockCachedDexFile(fd)) {
+                /* uh oh -- this process needs to exit or we'll wedge the system */
+                ALOGE("Unable to unlock DEX file");
+                goto bail;
+            }
+        } else {
+            ALOGI("Zip is good, but no %s inside, and no .odex "
+                    "file in the same directory", kDexInJarName);
+            result = DEX_CACHE_BAD_ARCHIVE;
+            goto bail;
+        }
+    }
+    result = DEX_CACHE_OK;
+
+bail:
+    dexZipCloseArchive(archive);
+    free(cachedName);
+    if (fd >= 0) {
+        close(fd);
+    }
+    return result;
+}
+
+/*
+ * Open a Jar file.  It's okay if it's just a Zip archive without all of
+ * the Jar trimmings, but we do insist on finding "classes.dex" inside
+ * or an appropriately-named ".odex" file alongside.
+ *
+ * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as
+ * being part of a different class loader.
+ *
+ * Note: This should parallel the logic of dvmDexCacheStatus.
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+    JarFile** ppJarFile, bool isBootstrap)
+{
+    /*
+     * TODO: This function has been duplicated and modified to become
+     * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored.
+     */
+
+    ZipArchiveHandle archive;
+    DvmDex* pDvmDex = NULL;
+    char* cachedName = NULL;
+    bool archiveOpen = false;
+    bool locked = false;
+    int fd = -1;
+    int result = -1;
+
+    /* Even if we're not going to look at the archive, we need to
+     * open it so we can stuff it into ppJarFile.
+     */
+    if (dexZipOpenArchive(fileName, &archive) != 0)
+        goto bail;
+    archiveOpen = true;
+
+    /* If we fork/exec into dexopt, don't let it inherit the archive's fd.
+     */
+    dvmSetCloseOnExec(dexZipGetArchiveFd(archive));
+
+    /* First, look for a ".odex" alongside the jar file.  It will
+     * have the same name/path except for the extension.
+     */
+    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
+    if (fd >= 0) {
+        ALOGV("Using alternate file (odex) for %s ...", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            ALOGE("%s odex has stale dependencies", fileName);
+            free(cachedName);
+            cachedName = NULL;
+            close(fd);
+            fd = -1;
+            goto tryArchive;
+        } else {
+            ALOGV("%s odex has good dependencies", fileName);
+            //TODO: make sure that the .odex actually corresponds
+            //      to the classes.dex inside the archive (if present).
+            //      For typical use there will be no classes.dex.
+        }
+    } else {
+        ZipEntry entry;
+
+tryArchive:
+        /*
+         * Pre-created .odex absent or stale.  Look inside the jar for a
+         * "classes.dex".
+         */
+        if (dexZipFindEntry(archive, kDexInJarName, &entry) == 0) {
+            bool newFile = false;
+
+            /*
+             * We've found the one we want.  See if there's an up-to-date copy
+             * in the cache.
+             *
+             * On return, "fd" will be seeked just past the "opt" header.
+             *
+             * If a stale .odex file is present and classes.dex exists in
+             * the archive, this will *not* return an fd pointing to the
+             * .odex file; the fd will point into dalvik-cache like any
+             * other jar.
+             */
+            if (odexOutputName == NULL) {
+                cachedName = dexOptGenerateCacheFileName(fileName,
+                                kDexInJarName);
+                if (cachedName == NULL)
+                    goto bail;
+            } else {
+                cachedName = strdup(odexOutputName);
+            }
+            ALOGV("dvmJarFileOpen: Checking cache for %s (%s)",
+                fileName, cachedName);
+            fd = dvmOpenCachedDexFile(fileName, cachedName,
+                    entry.mod_time,
+                    entry.crc32,
+                    isBootstrap, &newFile, /*createIfMissing=*/true);
+            if (fd < 0) {
+                ALOGI("Unable to open or create cache for %s (%s)",
+                    fileName, cachedName);
+                goto bail;
+            }
+            locked = true;
+
+            /*
+             * If fd points to a new file (because there was no cached version,
+             * or the cached version was stale), generate the optimized DEX.
+             * The file descriptor returned is still locked, and is positioned
+             * just past the optimization header.
+             */
+            if (newFile) {
+                u8 startWhen, extractWhen, endWhen;
+                bool result;
+                off_t dexOffset;
+
+                dexOffset = lseek(fd, 0, SEEK_CUR);
+                result = (dexOffset > 0);
+
+                if (result) {
+                    startWhen = dvmGetRelativeTimeUsec();
+                    result = dexZipExtractEntryToFile(archive, &entry, fd) == 0;
+                    extractWhen = dvmGetRelativeTimeUsec();
+                }
+                if (result) {
+                    result = dvmOptimizeDexFile(fd, dexOffset,
+                                entry.uncompressed_length,
+                                fileName,
+                                entry.mod_time,
+                                entry.crc32,
+                                isBootstrap);
+                }
+
+                if (!result) {
+                    ALOGE("Unable to extract+optimize DEX from '%s'",
+                        fileName);
+                    goto bail;
+                }
+
+                endWhen = dvmGetRelativeTimeUsec();
+                ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms",
+                    fileName,
+                    (int) (extractWhen - startWhen) / 1000,
+                    (int) (endWhen - extractWhen) / 1000);
+            }
+        } else {
+            ALOGI("Zip is good, but no %s inside, and no valid .odex "
+                    "file in the same directory", kDexInJarName);
+            goto bail;
+        }
+    }
+
+    /*
+     * Map the cached version.  This immediately rewinds the fd, so it
+     * doesn't have to be seeked anywhere in particular.
+     */
+    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
+        ALOGI("Unable to map %s in %s", kDexInJarName, fileName);
+        goto bail;
+    }
+
+    if (locked) {
+        /* unlock the fd */
+        if (!dvmUnlockCachedDexFile(fd)) {
+            /* uh oh -- this process needs to exit or we'll wedge the system */
+            ALOGE("Unable to unlock DEX file");
+            goto bail;
+        }
+        locked = false;
+    }
+
+    ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName);
+
+    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
+    (*ppJarFile)->archive = archive;
+    (*ppJarFile)->cacheFileName = cachedName;
+    (*ppJarFile)->pDvmDex = pDvmDex;
+    cachedName = NULL;      // don't free it below
+    result = 0;
+
+bail:
+    /* clean up, closing the open file */
+    if (archiveOpen && result != 0)
+        dexZipCloseArchive(archive);
+    free(cachedName);
+    if (fd >= 0) {
+        if (locked)
+            (void) dvmUnlockCachedDexFile(fd);
+        close(fd);
+    }
+    return result;
+}
+
+/*
+ * Close a Jar file and free the struct.
+ */
+void dvmJarFileFree(JarFile* pJarFile)
+{
+    if (pJarFile == NULL)
+        return;
+
+    dvmDexFileFree(pJarFile->pDvmDex);
+    dexZipCloseArchive(pJarFile->archive);
+    free(pJarFile->cacheFileName);
+    free(pJarFile);
+}
diff --git a/vm/JarFile.h b/vm/JarFile.h
new file mode 100644
index 0000000..b3a8a0f
--- /dev/null
+++ b/vm/JarFile.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Decode jar/apk/zip files.
+ */
+#ifndef DALVIK_JARFILE_H_
+#define DALVIK_JARFILE_H_
+
+#include "libdex/ZipArchive.h"
+
+/*
+ * This represents an open, scanned Jar file.  (It's actually for any Zip
+ * archive that happens to hold a Dex file.)
+ */
+struct JarFile {
+    ZipArchiveHandle archive;
+    //MemMapping  map;
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+};
+
+/*
+ * Open the Zip archive and get a list of the classfile entries.
+ *
+ * On success, returns 0 and sets "*ppJarFile" to a newly-allocated JarFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+    JarFile** ppJarFile, bool isBootstrap);
+
+/*
+ * Free a JarFile structure, along with any associated structures.
+ */
+void dvmJarFileFree(JarFile* pJarFile);
+
+/* pry the DexFile out of a JarFile */
+INLINE DvmDex* dvmGetJarFileDex(JarFile* pJarFile) {
+    return pJarFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetJarFileCacheFileName(JarFile* pJarFile) {
+    return pJarFile->cacheFileName;
+}
+
+enum DexCacheStatus {
+    DEX_CACHE_ERROR = -2,
+    DEX_CACHE_BAD_ARCHIVE = -1,
+    DEX_CACHE_OK = 0,
+    DEX_CACHE_STALE,
+    DEX_CACHE_STALE_ODEX,
+};
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName);
+
+#endif  // DALVIK_JARFILE_H_
diff --git a/vm/Jni.cpp b/vm/Jni.cpp
new file mode 100644
index 0000000..0e77fab
--- /dev/null
+++ b/vm/Jni.cpp
@@ -0,0 +1,3542 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik implementation of JNI interfaces.
+ */
+#include "Dalvik.h"
+#include "JniInternal.h"
+#include "Misc.h"
+#include "ScopedPthreadMutexLock.h"
+#include "UniquePtr.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+/*
+Native methods and interaction with the GC
+
+All JNI methods must start by changing their thread status to
+THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
+returning to native code.  The switch to "running" triggers a thread
+suspension check.
+
+With a rudimentary GC we should be able to skip the status change for
+simple functions, e.g.  IsSameObject, GetJavaVM, GetStringLength, maybe
+even access to fields with primitive types.  Our options are more limited
+with a compacting GC.
+
+For performance reasons we do as little error-checking as possible here.
+For example, we don't check to make sure the correct type of Object is
+passed in when setting a field, and we don't prevent you from storing
+new values in a "final" field.  Such things are best handled in the
+"check" version.  For actions that are common, dangerous, and must be
+checked at runtime, such as array bounds checks, we do the tests here.
+
+
+General notes on local/global reference tracking
+
+JNI provides explicit control over natively-held references that the GC
+needs to know about.  These can be local, in which case they're released
+when the native method returns into the VM, or global, which are held
+until explicitly released.  (There are also weak-global references,
+which have the lifespan and visibility of global references, but the
+object they refer to may be collected.)
+
+The references can be created with explicit JNI NewLocalRef / NewGlobalRef
+calls.  The former is very unusual, the latter is reasonably common
+(e.g. for caching references to class objects).
+
+Local references are most often created as a side-effect of JNI functions.
+For example, the AllocObject/NewObject functions must create local
+references to the objects returned, because nothing else in the GC root
+set has a reference to the new objects.
+
+The most common mode of operation is for a method to create zero or
+more local references and return.  Explicit "local delete" operations
+are expected to be exceedingly rare, except when walking through an
+object array, and the Push/PopLocalFrame calls are expected to be used
+infrequently.  For efficient operation, we want to add new local refs
+with a simple store/increment operation; to avoid infinite growth in
+pathological situations, we need to reclaim the space used by deleted
+entries.
+
+If we just want to maintain a list for the GC root set, we can use an
+expanding append-only array that compacts when objects are deleted.
+In typical situations, e.g. running through an array of objects, we will
+be deleting one of the most recently added entries, so we can minimize
+the number of elements moved (or avoid having to move any).
+
+If we want to conceal the pointer values from native code, which is
+necessary to allow the GC to move JNI-referenced objects around, then we
+have to use a more complicated indirection mechanism.
+
+The spec says, "Local references are only valid in the thread in which
+they are created.  The native code must not pass local references from
+one thread to another."
+
+
+Pinned objects
+
+For some large chunks of data, notably primitive arrays and String data,
+JNI allows the VM to choose whether it wants to pin the array object or
+make a copy.  We currently pin the memory for better execution performance.
+
+TODO: we're using simple root set references to pin primitive array data,
+because they have the property we need (i.e. the pointer we return is
+guaranteed valid until we explicitly release it).  However, if we have a
+compacting GC and don't want to pin all memory held by all global refs,
+we need to treat these differently.
+
+
+Global reference tracking
+
+There should be a small "active" set centered around the most-recently
+added items.
+
+Because it's global, access to it has to be synchronized.  Additions and
+removals require grabbing a mutex.  If the table serves as an indirection
+mechanism (i.e. it's not just a list for the benefit of the garbage
+collector), reference lookups may also require grabbing a mutex.
+
+The JNI spec does not define any sort of limit, so the list must be able
+to expand to a reasonable size.  It may be useful to log significant
+increases in usage to help identify resource leaks.
+
+
+Weak-global reference tracking
+
+[TBD]
+
+
+Local reference tracking
+
+Each Thread/JNIEnv points to an IndirectRefTable.
+
+We implement Push/PopLocalFrame with actual stack frames.  Before a JNI
+frame gets popped, we set "nextEntry" to the "top" pointer of the current
+frame, effectively releasing the references.
+
+The GC will scan all references in the table.
+
+*/
+
+static void ReportJniError() {
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+}
+
+#ifdef WITH_JNI_STACK_CHECK
+# define COMPUTE_STACK_SUM(_self)   computeStackSum(_self);
+# define CHECK_STACK_SUM(_self)     checkStackSum(_self);
+
+/*
+ * Compute a CRC on the entire interpreted stack.
+ *
+ * Would be nice to compute it on "self" as well, but there are parts of
+ * the Thread that can be altered by other threads (e.g. prev/next pointers).
+ */
+static void computeStackSum(Thread* self) {
+    const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    u4 crc = dvmInitCrc32();
+    self->stackCrc = 0;
+    crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+    self->stackCrc = crc;
+}
+
+/*
+ * Compute a CRC on the entire interpreted stack, and compare it to what
+ * we previously computed.
+ *
+ * We can execute JNI directly from native code without calling in from
+ * interpreted code during VM initialization and immediately after JNI
+ * thread attachment.  Another opportunity exists during JNI_OnLoad.  Rather
+ * than catching these cases we just ignore them here, which is marginally
+ * less accurate but reduces the amount of code we have to touch with #ifdefs.
+ */
+static void checkStackSum(Thread* self) {
+    const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    u4 stackCrc = self->stackCrc;
+    self->stackCrc = 0;
+    u4 crc = dvmInitCrc32();
+    crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+    if (crc != stackCrc) {
+        const Method* meth = dvmGetCurrentJNIMethod();
+        if (dvmComputeExactFrameDepth(self->interpSave.curFrame) == 1) {
+            ALOGD("JNI: bad stack CRC (0x%08x) -- okay during init", stackCrc);
+        } else if (strcmp(meth->name, "nativeLoad") == 0 &&
+                (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) {
+            ALOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad", stackCrc);
+        } else {
+            ALOGW("JNI: bad stack CRC (%08x vs %08x)", crc, stackCrc);
+            ReportJniError();
+        }
+    }
+    self->stackCrc = (u4) -1;       /* make logic errors more noticeable */
+}
+
+#else
+# define COMPUTE_STACK_SUM(_self)   ((void)0)
+# define CHECK_STACK_SUM(_self)     ((void)0)
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Entry/exit processing for all JNI calls.
+ *
+ * We skip the (curiously expensive) thread-local storage lookup on our Thread*.
+ * If the caller has passed the wrong JNIEnv in, we're going to be accessing unsynchronized
+ * structures from more than one thread, and things are going to fail
+ * in bizarre ways.  This is only sensible if the native code has been
+ * fully exercised with CheckJNI enabled.
+ */
+class ScopedJniThreadState {
+public:
+    explicit ScopedJniThreadState(JNIEnv* env) {
+        mSelf = ((JNIEnvExt*) env)->self;
+
+        if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+            // When emulating direct pointers with indirect references, it's critical
+            // that we use the correct per-thread indirect reference table.
+            Thread* self = gDvmJni.workAroundAppJniBugs ? dvmThreadSelf() : mSelf;
+            if (self != mSelf) {
+                ALOGE("JNI ERROR: env->self != thread-self (%p vs. %p); auto-correcting", mSelf, self);
+                mSelf = self;
+            }
+        }
+
+        CHECK_STACK_SUM(mSelf);
+        dvmChangeStatus(mSelf, THREAD_RUNNING);
+    }
+
+    ~ScopedJniThreadState() {
+        dvmChangeStatus(mSelf, THREAD_NATIVE);
+        COMPUTE_STACK_SUM(mSelf);
+    }
+
+    inline Thread* self() {
+        return mSelf;
+    }
+
+private:
+    Thread* mSelf;
+
+    // Disallow copy and assignment.
+    ScopedJniThreadState(const ScopedJniThreadState&);
+    void operator=(const ScopedJniThreadState&);
+};
+
+#define kGlobalRefsTableInitialSize 512
+#define kGlobalRefsTableMaxSize     51200       /* arbitrary, must be < 64K */
+
+#define kWeakGlobalRefsTableInitialSize 16
+
+#define kPinTableInitialSize        16
+#define kPinTableMaxSize            1024
+#define kPinComplainThreshold       10
+
+bool dvmJniStartup() {
+    if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
+                                 kGlobalRefsTableMaxSize,
+                                 kIndirectKindGlobal)) {
+        return false;
+    }
+    if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize,
+                                 kGlobalRefsTableMaxSize,
+                                 kIndirectKindWeakGlobal)) {
+        return false;
+    }
+
+    dvmInitMutex(&gDvm.jniGlobalRefLock);
+    dvmInitMutex(&gDvm.jniWeakGlobalRefLock);
+
+    if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) {
+        return false;
+    }
+
+    dvmInitMutex(&gDvm.jniPinRefLock);
+
+    return true;
+}
+
+void dvmJniShutdown() {
+    gDvm.jniGlobalRefTable.destroy();
+    gDvm.jniWeakGlobalRefTable.destroy();
+    dvmClearReferenceTable(&gDvm.jniPinRefTable);
+}
+
+bool dvmIsBadJniVersion(int version) {
+  // We don't support JNI_VERSION_1_1. These are the only other valid versions.
+  return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
+}
+
+/*
+ * Find the JNIEnv associated with the current thread.
+ *
+ * Currently stored in the Thread struct.  Could also just drop this into
+ * thread-local storage.
+ */
+JNIEnvExt* dvmGetJNIEnvForThread() {
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        return NULL;
+    }
+    return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
+}
+
+/*
+ * Convert an indirect reference to an Object reference.  The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL, or is a weak global reference whose reference has
+ * been cleared, this returns NULL.  If jobj is an invalid indirect
+ * reference, kInvalidIndirectRefObject is returned.
+ *
+ * Note "env" may be NULL when decoding global references.
+ */
+Object* dvmDecodeIndirectRef(Thread* self, jobject jobj) {
+    if (jobj == NULL) {
+        return NULL;
+    }
+
+    switch (indirectRefKind(jobj)) {
+    case kIndirectKindLocal:
+        {
+            Object* result = self->jniLocalRefTable.get(jobj);
+            if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindGlobal:
+        {
+            // TODO: find a way to avoid the mutex activity here
+            IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+            ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+            Object* result = pRefTable->get(jobj);
+            if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindWeakGlobal:
+        {
+            // TODO: find a way to avoid the mutex activity here
+            IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable;
+            ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+            Object* result = pRefTable->get(jobj);
+            if (result == kClearedJniWeakGlobal) {
+                result = NULL;
+            } else if (UNLIKELY(result == NULL)) {
+                ALOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj);
+                ReportJniError();
+            }
+            return result;
+        }
+    case kIndirectKindInvalid:
+    default:
+        if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+            // Assume an invalid local reference is actually a direct pointer.
+            return reinterpret_cast<Object*>(jobj);
+        }
+        ALOGW("Invalid indirect reference %p in decodeIndirectRef", jobj);
+        ReportJniError();
+        return kInvalidIndirectRefObject;
+    }
+}
+
+static void AddLocalReferenceFailure(IndirectRefTable* pRefTable) {
+    pRefTable->dump("JNI local");
+    ALOGE("Failed adding to JNI local ref table (has %zd entries)", pRefTable->capacity());
+    ReportJniError(); // spec says call FatalError; this is equivalent
+}
+
+/*
+ * Add a local reference for an object to the current stack frame.  When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects.  We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ */
+static inline jobject addLocalReference(Thread* self, Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    IndirectRefTable* pRefTable = &self->jniLocalRefTable;
+    void* curFrame = self->interpSave.curFrame;
+    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+    jobject jobj = (jobject) pRefTable->add(cookie, obj);
+    if (UNLIKELY(jobj == NULL)) {
+        AddLocalReferenceFailure(pRefTable);
+    }
+
+    if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
+        // Hand out direct pointers to support broken old apps.
+        return reinterpret_cast<jobject>(obj);
+    }
+    return jobj;
+}
+
+/*
+ * Ensure that at least "capacity" references can be held in the local
+ * refs table of the current thread.
+ */
+static bool ensureLocalCapacity(Thread* self, int capacity) {
+    int numEntries = self->jniLocalRefTable.capacity();
+    // TODO: this isn't quite right, since "numEntries" includes holes
+    return ((kJniLocalRefMax - numEntries) >= capacity);
+}
+
+/*
+ * Explicitly delete a reference from the local list.
+ */
+static void deleteLocalReference(Thread* self, jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    IndirectRefTable* pRefTable = &self->jniLocalRefTable;
+    void* curFrame = self->interpSave.curFrame;
+    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+    if (!pRefTable->remove(cookie, jobj)) {
+        /*
+         * Attempting to delete a local reference that is not in the
+         * topmost local reference frame is a no-op.  DeleteLocalRef returns
+         * void and doesn't throw any exceptions, but we should probably
+         * complain about it so the user will notice that things aren't
+         * going quite the way they expect.
+         */
+        ALOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry", jobj);
+    }
+}
+
+/*
+ * Add a global reference for an object.
+ *
+ * We may add the same object more than once.  Add/remove calls are paired,
+ * so it needs to appear on the list multiple times.
+ */
+static jobject addGlobalReference(Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    //ALOGI("adding obj=%p", obj);
+    //dvmDumpThread(dvmThreadSelf(), false);
+
+    if (false && dvmIsClassObject((Object*)obj)) {
+        ClassObject* clazz = (ClassObject*) obj;
+        ALOGI("-------");
+        ALOGI("Adding global ref on class %s", clazz->descriptor);
+        dvmDumpThread(dvmThreadSelf(), false);
+    }
+    if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
+        StringObject* strObj = (StringObject*) obj;
+        char* str = dvmCreateCstrFromString(strObj);
+        if (strcmp(str, "sync-response") == 0) {
+            ALOGI("-------");
+            ALOGI("Adding global ref on string '%s'", str);
+            dvmDumpThread(dvmThreadSelf(), false);
+            //dvmAbort();
+        }
+        free(str);
+    }
+    if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
+        ArrayObject* arrayObj = (ArrayObject*) obj;
+        if (arrayObj->length == 8192 /*&&
+            dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
+        {
+            ALOGI("Adding global ref on byte array %p (len=%d)",
+                arrayObj, arrayObj->length);
+            dvmDumpThread(dvmThreadSelf(), false);
+        }
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+
+    /*
+     * Throwing an exception on failure is problematic, because JNI code
+     * may not be expecting an exception, and things sort of cascade.  We
+     * want to have a hard limit to catch leaks during debugging, but this
+     * otherwise needs to expand until memory is consumed.  As a practical
+     * matter, if we have many thousands of global references, chances are
+     * we're either leaking global ref table entries or we're going to
+     * run out of space in the GC heap.
+     */
+    jobject jobj = (jobject) gDvm.jniGlobalRefTable.add(IRT_FIRST_SEGMENT, obj);
+    if (jobj == NULL) {
+        gDvm.jniGlobalRefTable.dump("JNI global");
+        ALOGE("Failed adding to JNI global ref table (%zd entries)",
+                gDvm.jniGlobalRefTable.capacity());
+        ReportJniError();
+    }
+
+    LOGVV("GREF add %p  (%s.%s)", obj,
+        dvmGetCurrentJNIMethod()->clazz->descriptor,
+        dvmGetCurrentJNIMethod()->name);
+
+    return jobj;
+}
+
+static jobject addWeakGlobalReference(Object* obj) {
+    if (obj == NULL) {
+        return NULL;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+    IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
+    jobject jobj = (jobject) table->add(IRT_FIRST_SEGMENT, obj);
+    if (jobj == NULL) {
+        gDvm.jniWeakGlobalRefTable.dump("JNI weak global");
+        ALOGE("Failed adding to JNI weak global ref table (%zd entries)", table->capacity());
+        ReportJniError();
+    }
+    return jobj;
+}
+
+static void deleteWeakGlobalReference(jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
+    IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable;
+    if (!table->remove(IRT_FIRST_SEGMENT, jobj)) {
+        ALOGW("JNI: DeleteWeakGlobalRef(%p) failed to find entry", jobj);
+    }
+}
+
+/*
+ * Remove a global reference.  In most cases it's the entry most recently
+ * added, which makes this pretty quick.
+ *
+ * Thought: if it's not the most recent entry, just null it out.  When we
+ * fill up, do a compaction pass before we expand the list.
+ */
+static void deleteGlobalReference(jobject jobj) {
+    if (jobj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
+    if (!gDvm.jniGlobalRefTable.remove(IRT_FIRST_SEGMENT, jobj)) {
+        ALOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj);
+        return;
+    }
+}
+
+/*
+ * Objects don't currently move, so we just need to create a reference
+ * that will ensure the array object isn't collected.
+ *
+ * We use a separate reference table, which is part of the GC root set.
+ */
+static void pinPrimitiveArray(ArrayObject* arrayObj) {
+    if (arrayObj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock);
+
+    if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
+        dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+        ALOGE("Failed adding to JNI pinned array ref table (%d entries)",
+           (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+        ReportJniError();
+    }
+
+    /*
+     * The total number of pinned primitive arrays should be pretty small.
+     * A single array should not be pinned more than once or twice; any
+     * more than that is a strong indicator that a Release function is
+     * not being called.
+     */
+    int count = 0;
+    Object** ppObj = gDvm.jniPinRefTable.table;
+    while (ppObj < gDvm.jniPinRefTable.nextEntry) {
+        if (*ppObj++ == (Object*) arrayObj) {
+            count++;
+        }
+    }
+
+    if (count > kPinComplainThreshold) {
+        ALOGW("JNI: pin count on array %p (%s) is now %d",
+              arrayObj, arrayObj->clazz->descriptor, count);
+        /* keep going */
+    }
+}
+
+/*
+ * Un-pin the array object.  If an object was pinned twice, it must be
+ * unpinned twice before it's free to move.
+ */
+static void unpinPrimitiveArray(ArrayObject* arrayObj) {
+    if (arrayObj == NULL) {
+        return;
+    }
+
+    ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock);
+    if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
+            gDvm.jniPinRefTable.table, (Object*) arrayObj))
+    {
+        ALOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)",
+            arrayObj, dvmIsHeapAddress((Object*) arrayObj));
+        return;
+    }
+}
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * We only dump the local refs associated with the current thread.
+ */
+void dvmDumpJniReferenceTables() {
+    Thread* self = dvmThreadSelf();
+    self->jniLocalRefTable.dump("JNI local");
+    gDvm.jniGlobalRefTable.dump("JNI global");
+    dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+}
+
+void dvmDumpJniStats(DebugOutputTarget* target) {
+    dvmPrintDebugMessage(target, "JNI: CheckJNI is %s", gDvmJni.useCheckJni ? "on" : "off");
+    if (gDvmJni.forceCopy) {
+        dvmPrintDebugMessage(target, " (with forcecopy)");
+    }
+    dvmPrintDebugMessage(target, "; workarounds are %s", gDvmJni.workAroundAppJniBugs ? "on" : "off");
+
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    dvmPrintDebugMessage(target, "; pins=%d", dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+    dvmPrintDebugMessage(target, "; globals=%d", gDvm.jniGlobalRefTable.capacity());
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+    dvmLockMutex(&gDvm.jniWeakGlobalRefLock);
+    size_t weaks = gDvm.jniWeakGlobalRefTable.capacity();
+    if (weaks > 0) {
+        dvmPrintDebugMessage(target, " (plus %d weak)", weaks);
+    }
+    dvmUnlockMutex(&gDvm.jniWeakGlobalRefLock);
+
+    dvmPrintDebugMessage(target, "\n\n");
+}
+
+/*
+ * Verify that a reference passed in from native code is one that the
+ * code is allowed to have.
+ *
+ * It's okay for native code to pass us a reference that:
+ *  - was passed in as an argument when invoked by native code (and hence
+ *    is in the JNI local refs table)
+ *  - was returned to it from JNI (and is now in the local refs table)
+ *  - is present in the JNI global refs table
+ *
+ * Used by -Xcheck:jni and GetObjectRefType.
+ */
+jobjectRefType dvmGetJNIRefType(Thread* self, jobject jobj) {
+    /*
+     * IndirectRefKind is currently defined as an exact match of
+     * jobjectRefType, so this is easy.  We have to decode it to determine
+     * if it's a valid reference and not merely valid-looking.
+     */
+    assert(jobj != NULL);
+
+    Object* obj = dvmDecodeIndirectRef(self, jobj);
+    if (obj == reinterpret_cast<Object*>(jobj) && gDvmJni.workAroundAppJniBugs) {
+        // If we're handing out direct pointers, check whether 'jobj' is a direct reference
+        // to a local reference.
+        return self->jniLocalRefTable.contains(obj) ? JNILocalRefType : JNIInvalidRefType;
+    } else if (obj == kInvalidIndirectRefObject) {
+        return JNIInvalidRefType;
+    } else {
+        return (jobjectRefType) indirectRefKind(jobj);
+    }
+}
+
+static void dumpMethods(Method* methods, size_t methodCount, const char* name) {
+    size_t i;
+    for (i = 0; i < methodCount; ++i) {
+        Method* method = &methods[i];
+        if (strcmp(name, method->name) == 0) {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGE("Candidate: %s.%s:%s", method->clazz->descriptor, name, desc);
+            free(desc);
+        }
+    }
+}
+
+static void dumpCandidateMethods(ClassObject* clazz, const char* methodName, const char* signature) {
+    ALOGE("ERROR: couldn't find native method");
+    ALOGE("Requested: %s.%s:%s", clazz->descriptor, methodName, signature);
+    dumpMethods(clazz->virtualMethods, clazz->virtualMethodCount, methodName);
+    dumpMethods(clazz->directMethods, clazz->directMethodCount, methodName);
+}
+
+static void throwNoSuchMethodError(ClassObject* c, const char* name, const char* sig, const char* kind) {
+    std::string msg(StringPrintf("no %s method \"%s.%s%s\"", kind, c->descriptor, name, sig));
+    dvmThrowNoSuchMethodError(msg.c_str());
+}
+
+/*
+ * Register a method that uses JNI calling conventions.
+ */
+static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
+    const char* signature, void* fnPtr)
+{
+    if (fnPtr == NULL) {
+        return false;
+    }
+
+    // If a signature starts with a '!', we take that as a sign that the native code doesn't
+    // need the extra JNI arguments (the JNIEnv* and the jclass).
+    bool fastJni = false;
+    if (*signature == '!') {
+        fastJni = true;
+        ++signature;
+        ALOGV("fast JNI method %s.%s:%s detected", clazz->descriptor, methodName, signature);
+    }
+
+    Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
+    if (method == NULL) {
+        method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
+    }
+    if (method == NULL) {
+        dumpCandidateMethods(clazz, methodName, signature);
+        throwNoSuchMethodError(clazz, methodName, signature, "static or non-static");
+        return false;
+    }
+
+    if (!dvmIsNativeMethod(method)) {
+        ALOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature);
+        throwNoSuchMethodError(clazz, methodName, signature, "native");
+        return false;
+    }
+
+    if (fastJni) {
+        // In this case, we have extra constraints to check...
+        if (dvmIsSynchronizedMethod(method)) {
+            // Synchronization is usually provided by the JNI bridge,
+            // but we won't have one.
+            ALOGE("fast JNI method %s.%s:%s cannot be synchronized",
+                    clazz->descriptor, methodName, signature);
+            return false;
+        }
+        if (!dvmIsStaticMethod(method)) {
+            // There's no real reason for this constraint, but since we won't
+            // be supplying a JNIEnv* or a jobject 'this', you're effectively
+            // static anyway, so it seems clearer to say so.
+            ALOGE("fast JNI method %s.%s:%s cannot be non-static",
+                    clazz->descriptor, methodName, signature);
+            return false;
+        }
+    }
+
+    if (method->nativeFunc != dvmResolveNativeMethod) {
+        /* this is allowed, but unusual */
+        ALOGV("Note: %s.%s:%s was already registered", clazz->descriptor, methodName, signature);
+    }
+
+    method->fastJni = fastJni;
+    dvmUseJNIBridge(method, fnPtr);
+
+    ALOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature);
+    return true;
+}
+
+static const char* builtInPrefixes[] = {
+    "Landroid/",
+    "Lcom/android/",
+    "Lcom/google/android/",
+    "Ldalvik/",
+    "Ljava/",
+    "Ljavax/",
+    "Llibcore/",
+    "Lorg/apache/harmony/",
+};
+
+static bool shouldTrace(Method* method) {
+    const char* className = method->clazz->descriptor;
+    // Return true if the -Xjnitrace setting implies we should trace 'method'.
+    if (gDvm.jniTrace && strstr(className, gDvm.jniTrace)) {
+        return true;
+    }
+    // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+    // like part of Android.
+    if (gDvmJni.logThirdPartyJni) {
+        for (size_t i = 0; i < NELEM(builtInPrefixes); ++i) {
+            if (strstr(className, builtInPrefixes[i]) == className) {
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
+ * to point at the actual function.
+ */
+void dvmUseJNIBridge(Method* method, void* func) {
+    method->shouldTrace = shouldTrace(method);
+
+    // Does the method take any reference arguments?
+    method->noRef = true;
+    const char* cp = method->shorty;
+    while (*++cp != '\0') { // Pre-increment to skip return type.
+        if (*cp == 'L') {
+            method->noRef = false;
+            break;
+        }
+    }
+
+    DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;
+    dvmSetNativeFunc(method, bridge, (const u2*) func);
+}
+
+// TODO: rewrite this to share code with CheckJNI's tracing...
+static void appendValue(char type, const JValue value, char* buf, size_t n, bool appendComma)
+{
+    size_t len = strlen(buf);
+    if (len >= n - 32) { // 32 should be longer than anything we could append.
+        buf[len - 1] = '.';
+        buf[len - 2] = '.';
+        buf[len - 3] = '.';
+        return;
+    }
+    char* p = buf + len;
+    switch (type) {
+    case 'B':
+        if (value.b >= 0 && value.b < 10) {
+            sprintf(p, "%d", value.b);
+        } else {
+            sprintf(p, "%#x (%d)", value.b, value.b);
+        }
+        break;
+    case 'C':
+        if (value.c < 0x7f && value.c >= ' ') {
+            sprintf(p, "U+%x ('%c')", value.c, value.c);
+        } else {
+            sprintf(p, "U+%x", value.c);
+        }
+        break;
+    case 'D':
+        sprintf(p, "%g", value.d);
+        break;
+    case 'F':
+        sprintf(p, "%g", value.f);
+        break;
+    case 'I':
+        sprintf(p, "%d", value.i);
+        break;
+    case 'L':
+        sprintf(p, "%#x", value.i);
+        break;
+    case 'J':
+        sprintf(p, "%lld", value.j);
+        break;
+    case 'S':
+        sprintf(p, "%d", value.s);
+        break;
+    case 'V':
+        strcpy(p, "void");
+        break;
+    case 'Z':
+        strcpy(p, value.z ? "true" : "false");
+        break;
+    default:
+        sprintf(p, "unknown type '%c'", type);
+        break;
+    }
+
+    if (appendComma) {
+        strcat(p, ", ");
+    }
+}
+
+static void logNativeMethodEntry(const Method* method, const u4* args)
+{
+    char thisString[32] = { 0 };
+    const u4* sp = args;
+    if (!dvmIsStaticMethod(method)) {
+        sprintf(thisString, "this=0x%08x ", *sp++);
+    }
+
+    char argsString[128]= { 0 };
+    const char* desc = &method->shorty[1];
+    while (*desc != '\0') {
+        char argType = *desc++;
+        JValue value;
+        if (argType == 'D' || argType == 'J') {
+            value.j = dvmGetArgLong(sp, 0);
+            sp += 2;
+        } else {
+            value.i = *sp++;
+        }
+        appendValue(argType, value, argsString, sizeof(argsString),
+        *desc != '\0');
+    }
+
+    std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    ALOGI("-> %s %s%s %s(%s)", className.c_str(), method->name, signature, thisString, argsString);
+    free(signature);
+}
+
+static void logNativeMethodExit(const Method* method, Thread* self, const JValue returnValue)
+{
+    std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    if (dvmCheckException(self)) {
+        Object* exception = dvmGetException(self);
+        std::string exceptionClassName(dvmHumanReadableDescriptor(exception->clazz->descriptor));
+        ALOGI("<- %s %s%s threw %s", className.c_str(),
+                method->name, signature, exceptionClassName.c_str());
+    } else {
+        char returnValueString[128] = { 0 };
+        char returnType = method->shorty[0];
+        appendValue(returnType, returnValue, returnValueString, sizeof(returnValueString), false);
+        ALOGI("<- %s %s%s returned %s", className.c_str(),
+                method->name, signature, returnValueString);
+    }
+    free(signature);
+}
+
+/*
+ * Get the method currently being executed by examining the interp stack.
+ */
+const Method* dvmGetCurrentJNIMethod() {
+    assert(dvmThreadSelf() != NULL);
+
+    void* fp = dvmThreadSelf()->interpSave.curFrame;
+    const Method* meth = SAVEAREA_FROM_FP(fp)->method;
+
+    assert(meth != NULL);
+    assert(dvmIsNativeMethod(meth));
+    return meth;
+}
+
+/*
+ * Track a JNI MonitorEnter in the current thread.
+ *
+ * The goal is to be able to "implicitly" release all JNI-held monitors
+ * when the thread detaches.
+ *
+ * Monitors may be entered multiple times, so we add a new entry for each
+ * enter call.  It would be more efficient to keep a counter.  At present
+ * there's no real motivation to improve this however.
+ */
+static void trackMonitorEnter(Thread* self, Object* obj) {
+    static const int kInitialSize = 16;
+    ReferenceTable* refTable = &self->jniMonitorRefTable;
+
+    /* init table on first use */
+    if (refTable->table == NULL) {
+        assert(refTable->maxEntries == 0);
+
+        if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
+            ALOGE("Unable to initialize monitor tracking table");
+            ReportJniError();
+        }
+    }
+
+    if (!dvmAddToReferenceTable(refTable, obj)) {
+        /* ran out of memory? could throw exception instead */
+        ALOGE("Unable to add entry to monitor tracking table");
+        ReportJniError();
+    } else {
+        LOGVV("--- added monitor %p", obj);
+    }
+}
+
+/*
+ * Track a JNI MonitorExit in the current thread.
+ */
+static void trackMonitorExit(Thread* self, Object* obj) {
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+
+    if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
+        ALOGE("JNI monitor %p not found in tracking list", obj);
+        /* keep going? */
+    } else {
+        LOGVV("--- removed monitor %p", obj);
+    }
+}
+
+/*
+ * Release all monitors held by the jniMonitorRefTable list.
+ */
+void dvmReleaseJniMonitors(Thread* self) {
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+    Object** top = pRefTable->table;
+
+    if (top == NULL) {
+        return;
+    }
+    Object** ptr = pRefTable->nextEntry;
+    while (--ptr >= top) {
+        if (!dvmUnlockObject(self, *ptr)) {
+            ALOGW("Unable to unlock monitor %p at thread detach", *ptr);
+        } else {
+            LOGVV("--- detach-releasing monitor %p", *ptr);
+        }
+    }
+
+    /* zap it */
+    pRefTable->nextEntry = pRefTable->table;
+}
+
+/*
+ * Determine if the specified class can be instantiated from JNI.  This
+ * is used by AllocObject / NewObject, which are documented as throwing
+ * an exception for abstract and interface classes, and not accepting
+ * array classes.  We also want to reject attempts to create new Class
+ * objects, since only DefineClass should do that.
+ */
+static bool canAllocClass(ClassObject* clazz) {
+    if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {
+        /* JNI spec defines what this throws */
+        dvmThrowInstantiationException(clazz, "abstract class or interface");
+        return false;
+    } else if (dvmIsArrayClass(clazz) || dvmIsTheClassClass(clazz)) {
+        /* spec says "must not" for arrays, ignores Class */
+        dvmThrowInstantiationException(clazz, "wrong JNI function");
+        return false;
+    }
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI call bridge
+ * ===========================================================================
+ */
+
+/*
+ * The functions here form a bridge between interpreted code and JNI native
+ * functions.  The basic task is to convert an array of primitives and
+ * references into C-style function arguments.  This is architecture-specific
+ * and usually requires help from assembly code.
+ *
+ * The bridge takes four arguments: the array of parameters, a place to
+ * store the function result (if any), the method to call, and a pointer
+ * to the current thread.
+ *
+ * These functions aren't called directly from elsewhere in the VM.
+ * A pointer in the Method struct points to one of these, and when a native
+ * method is invoked the interpreter jumps to it.
+ *
+ * (The "internal native" methods are invoked the same way, but instead
+ * of calling through a bridge, the target method is called directly.)
+ *
+ * The "args" array should not be modified, but we do so anyway for
+ * performance reasons.  We know that it points to the "outs" area on
+ * the current method's interpreted stack.  This area is ignored by the
+ * precise GC, because there is no register map for a native method (for
+ * an interpreted method the args would be listed in the argument set).
+ * We know all of the values exist elsewhere on the interpreted stack,
+ * because the method call setup copies them right before making the call,
+ * so we don't have to worry about concealing stuff from the GC.
+ *
+ * If we don't want to modify "args", we either have to create a local
+ * copy and modify it before calling dvmPlatformInvoke, or we have to do
+ * the local reference replacement within dvmPlatformInvoke.  The latter
+ * has some performance advantages, though if we can inline the local
+ * reference adds we may win when there's a lot of reference args (unless
+ * we want to code up some local ref table manipulation in assembly.
+ */
+
+/*
+ * If necessary, convert the value in pResult from a local/global reference
+ * to an object pointer.
+ *
+ * If the returned reference is invalid, kInvalidIndirectRefObject will
+ * be returned in pResult.
+ */
+static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    if (method->shorty[0] == 'L' && !dvmCheckException(self) && pResult->l != NULL) {
+        pResult->l = dvmDecodeIndirectRef(self, (jobject) pResult->l);
+    }
+}
+
+/*
+ * General form, handles all cases.
+ */
+void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) {
+    u4* modArgs = (u4*) args;
+    jclass staticMethodClass = NULL;
+
+    u4 accessFlags = method->accessFlags;
+    bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+
+    //ALOGI("JNI calling %p (%s.%s:%s):", method->insns,
+    //    method->clazz->descriptor, method->name, method->shorty);
+
+    /*
+     * Walk the argument list, creating local references for appropriate
+     * arguments.
+     */
+    int idx = 0;
+    Object* lockObj;
+    if ((accessFlags & ACC_STATIC) != 0) {
+        lockObj = (Object*) method->clazz;
+        /* add the class object we pass in */
+        staticMethodClass = (jclass) addLocalReference(self, (Object*) method->clazz);
+    } else {
+        lockObj = (Object*) args[0];
+        /* add "this" */
+        modArgs[idx++] = (u4) addLocalReference(self, (Object*) modArgs[0]);
+    }
+
+    if (!method->noRef) {
+        const char* shorty = &method->shorty[1];        /* skip return type */
+        while (*shorty != '\0') {
+            switch (*shorty++) {
+            case 'L':
+                //ALOGI("  local %d: 0x%08x", idx, modArgs[idx]);
+                if (modArgs[idx] != 0) {
+                    modArgs[idx] = (u4) addLocalReference(self, (Object*) modArgs[idx]);
+                }
+                break;
+            case 'D':
+            case 'J':
+                idx++;
+                break;
+            default:
+                /* Z B C S I -- do nothing */
+                break;
+            }
+            idx++;
+        }
+    }
+
+    if (UNLIKELY(method->shouldTrace)) {
+        logNativeMethodEntry(method, args);
+    }
+    if (UNLIKELY(isSynchronized)) {
+        dvmLockObject(self, lockObj);
+    }
+
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+    ANDROID_MEMBAR_FULL();      /* guarantee ordering on method->insns */
+    assert(method->insns != NULL);
+
+    JNIEnv* env = self->jniEnv;
+    COMPUTE_STACK_SUM(self);
+    dvmPlatformInvoke(env,
+            (ClassObject*) staticMethodClass,
+            method->jniArgInfo, method->insSize, modArgs, method->shorty,
+            (void*) method->insns, pResult);
+    CHECK_STACK_SUM(self);
+
+    dvmChangeStatus(self, oldStatus);
+
+    convertReferenceResult(env, pResult, method, self);
+
+    if (UNLIKELY(isSynchronized)) {
+        dvmUnlockObject(self, lockObj);
+    }
+    if (UNLIKELY(method->shouldTrace)) {
+        logNativeMethodExit(method, self, *pResult);
+    }
+}
+
+/*
+ * ===========================================================================
+ *      JNI implementation
+ * ===========================================================================
+ */
+
+/*
+ * Return the version of the native method interface.
+ */
+static jint GetVersion(JNIEnv* env) {
+    /*
+     * There is absolutely no need to toggle the mode for correct behavior.
+     * However, it does provide native code with a simple "suspend self
+     * if necessary" call.
+     */
+    ScopedJniThreadState ts(env);
+    return JNI_VERSION_1_6;
+}
+
+/*
+ * Create a new class from a bag of bytes.
+ *
+ * This is not currently supported within Dalvik.
+ */
+static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
+    const jbyte* buf, jsize bufLen)
+{
+    UNUSED_PARAMETER(name);
+    UNUSED_PARAMETER(loader);
+    UNUSED_PARAMETER(buf);
+    UNUSED_PARAMETER(bufLen);
+
+    ScopedJniThreadState ts(env);
+    ALOGW("JNI DefineClass is not supported");
+    return NULL;
+}
+
+/*
+ * Find a class by name.
+ *
+ * We have to use the "no init" version of FindClass here, because we might
+ * be getting the class prior to registering native methods that will be
+ * used in <clinit>.
+ *
+ * We need to get the class loader associated with the current native
+ * method.  If there is no native method, e.g. we're calling this from native
+ * code right after creating the VM, the spec says we need to use the class
+ * loader returned by "ClassLoader.getBaseClassLoader".  There is no such
+ * method, but it's likely they meant ClassLoader.getSystemClassLoader.
+ * We can't get that until after the VM has initialized though.
+ */
+static jclass FindClass(JNIEnv* env, const char* name) {
+    ScopedJniThreadState ts(env);
+
+    const Method* thisMethod = dvmGetCurrentJNIMethod();
+    assert(thisMethod != NULL);
+
+    Object* loader;
+    Object* trackedLoader = NULL;
+    if (ts.self()->classLoaderOverride != NULL) {
+        /* hack for JNI_OnLoad */
+        assert(strcmp(thisMethod->name, "nativeLoad") == 0);
+        loader = ts.self()->classLoaderOverride;
+    } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
+               thisMethod == gDvm.methDalvikSystemNativeStart_run) {
+        /* start point of invocation interface */
+        if (!gDvm.initializing) {
+            loader = trackedLoader = dvmGetSystemClassLoader();
+        } else {
+            loader = NULL;
+        }
+    } else {
+        loader = thisMethod->clazz->classLoader;
+    }
+
+    char* descriptor = dvmNameToDescriptor(name);
+    if (descriptor == NULL) {
+        return NULL;
+    }
+    ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
+    free(descriptor);
+
+    jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
+    dvmReleaseTrackedAlloc(trackedLoader, ts.self());
+    return jclazz;
+}
+
+/*
+ * Return the superclass of a class.
+ */
+static jclass GetSuperclass(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    return (jclass) addLocalReference(ts.self(), (Object*)clazz->super);
+}
+
+/*
+ * Determine whether an object of clazz1 can be safely cast to clazz2.
+ *
+ * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
+ */
+static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz1);
+    ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz2);
+    return dvmInstanceof(clazz1, clazz2);
+}
+
+/*
+ * Given a java.lang.reflect.Method or .Constructor, return a methodID.
+ */
+static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) {
+    ScopedJniThreadState ts(env);
+    Object* method = dvmDecodeIndirectRef(ts.self(), jmethod);
+    return (jmethodID) dvmGetMethodFromReflectObj(method);
+}
+
+/*
+ * Given a java.lang.reflect.Field, return a fieldID.
+ */
+static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) {
+    ScopedJniThreadState ts(env);
+    Object* field = dvmDecodeIndirectRef(ts.self(), jfield);
+    return (jfieldID) dvmGetFieldFromReflectObj(field);
+}
+
+/*
+ * Convert a methodID to a java.lang.reflect.Method or .Constructor.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);
+    Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Convert a fieldID to a java.lang.reflect.Field.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);
+    Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Take this exception and throw it.
+ */
+static jint Throw(JNIEnv* env, jthrowable jobj) {
+    ScopedJniThreadState ts(env);
+    if (jobj != NULL) {
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+        dvmSetException(ts.self(), obj);
+        return JNI_OK;
+    }
+    return JNI_ERR;
+}
+
+/*
+ * Constructs an exception object from the specified class with the message
+ * specified by "message", and throws it.
+ */
+static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    dvmThrowException(clazz, message);
+    // TODO: should return failure if this didn't work (e.g. OOM)
+    return JNI_OK;
+}
+
+/*
+ * If an exception is being thrown, return the exception object.  Otherwise,
+ * return NULL.
+ *
+ * TODO: if there is no pending exception, we should be able to skip the
+ * enter/exit checks.  If we find one, we need to enter and then re-fetch
+ * the exception (in case it got moved by a compacting GC).
+ */
+static jthrowable ExceptionOccurred(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    Object* exception = dvmGetException(ts.self());
+    jthrowable localException = (jthrowable) addLocalReference(ts.self(), exception);
+    if (localException == NULL && exception != NULL) {
+        /*
+         * We were unable to add a new local reference, and threw a new
+         * exception.  We can't return "exception", because it's not a
+         * local reference.  So we have to return NULL, indicating that
+         * there was no exception, even though it's pretty much raining
+         * exceptions in here.
+         */
+        ALOGW("JNI WARNING: addLocal/exception combo");
+    }
+    return localException;
+}
+
+/*
+ * Print an exception and stack trace to stderr.
+ */
+static void ExceptionDescribe(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    Object* exception = dvmGetException(ts.self());
+    if (exception != NULL) {
+        dvmPrintExceptionStackTrace();
+    } else {
+        ALOGI("Odd: ExceptionDescribe called, but no exception pending");
+    }
+}
+
+/*
+ * Clear the exception currently being thrown.
+ *
+ * TODO: we should be able to skip the enter/exit stuff.
+ */
+static void ExceptionClear(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    dvmClearException(ts.self());
+}
+
+/*
+ * Kill the VM.  This function does not return.
+ */
+static void FatalError(JNIEnv* env, const char* msg) {
+    //dvmChangeStatus(NULL, THREAD_RUNNING);
+    ALOGE("JNI posting fatal error: %s", msg);
+    ReportJniError();
+}
+
+/*
+ * Push a new JNI frame on the stack, with a new set of locals.
+ *
+ * The new frame must have the same method pointer.  (If for no other
+ * reason than FindClass needs it to get the appropriate class loader.)
+ */
+static jint PushLocalFrame(JNIEnv* env, jint capacity) {
+    ScopedJniThreadState ts(env);
+    if (!ensureLocalCapacity(ts.self(), capacity) ||
+            !dvmPushLocalFrame(ts.self(), dvmGetCurrentJNIMethod()))
+    {
+        /* yes, OutOfMemoryError, not StackOverflowError */
+        dvmClearException(ts.self());
+        dvmThrowOutOfMemoryError("out of stack in JNI PushLocalFrame");
+        return JNI_ERR;
+    }
+    return JNI_OK;
+}
+
+/*
+ * Pop the local frame off.  If "jresult" is not null, add it as a
+ * local reference on the now-current frame.
+ */
+static jobject PopLocalFrame(JNIEnv* env, jobject jresult) {
+    ScopedJniThreadState ts(env);
+    Object* result = dvmDecodeIndirectRef(ts.self(), jresult);
+    if (!dvmPopLocalFrame(ts.self())) {
+        ALOGW("JNI WARNING: too many PopLocalFrame calls");
+        dvmClearException(ts.self());
+        dvmThrowRuntimeException("too many PopLocalFrame calls");
+    }
+    return addLocalReference(ts.self(), result);
+}
+
+/*
+ * Add a reference to the global list.
+ */
+static jobject NewGlobalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return addGlobalReference(obj);
+}
+
+/*
+ * Delete a reference from the global list.
+ */
+static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) {
+    ScopedJniThreadState ts(env);
+    deleteGlobalReference(jglobalRef);
+}
+
+
+/*
+ * Add a reference to the local list.
+ */
+static jobject NewLocalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return addLocalReference(ts.self(), obj);
+}
+
+/*
+ * Delete a reference from the local list.
+ */
+static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) {
+    ScopedJniThreadState ts(env);
+    deleteLocalReference(ts.self(), jlocalRef);
+}
+
+/*
+ * Ensure that the local references table can hold at least this many
+ * references.
+ */
+static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) {
+    ScopedJniThreadState ts(env);
+    bool okay = ensureLocalCapacity(ts.self(), capacity);
+    if (!okay) {
+        dvmThrowOutOfMemoryError("can't ensure local reference capacity");
+    }
+    return okay ? 0 : -1;
+}
+
+
+/*
+ * Determine whether two Object references refer to the same underlying object.
+ */
+static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) {
+    ScopedJniThreadState ts(env);
+    Object* obj1 = dvmDecodeIndirectRef(ts.self(), jref1);
+    Object* obj2 = dvmDecodeIndirectRef(ts.self(), jref2);
+    return (obj1 == obj2);
+}
+
+/*
+ * Allocate a new object without invoking any constructors.
+ */
+static jobject AllocObject(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    return addLocalReference(ts.self(), newObj);
+}
+
+/*
+ * Allocate a new object and invoke the supplied constructor.
+ */
+static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        va_list args;
+        va_start(args, methodID);
+        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+        va_end(args);
+    }
+    return result;
+}
+
+static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+    }
+    return result;
+}
+
+static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) {
+    ScopedJniThreadState ts(env);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    jobject result = addLocalReference(ts.self(), newObj);
+    if (newObj != NULL) {
+        JValue unused;
+        dvmCallMethodA(ts.self(), (Method*) methodID, newObj, true, &unused, args);
+    }
+    return result;
+}
+
+/*
+ * Returns the class of an object.
+ *
+ * JNI spec says: obj must not be NULL.
+ */
+static jclass GetObjectClass(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+
+    assert(jobj != NULL);
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return (jclass) addLocalReference(ts.self(), (Object*) obj->clazz);
+}
+
+/*
+ * Determine whether "obj" is an instance of "clazz".
+ */
+static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    assert(jclazz != NULL);
+    if (jobj == NULL) {
+        return true;
+    }
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    return dvmInstanceof(obj->clazz, clazz);
+}
+
+/*
+ * Get a method ID for an instance method.
+ *
+ * While Dalvik bytecode has distinct instructions for virtual, super,
+ * static, direct, and interface method invocation, JNI only provides
+ * two functions for acquiring a method ID.  This call handles everything
+ * but static methods.
+ *
+ * JNI defines <init> as an instance method, but Dalvik considers it a
+ * "direct" method, so we have to special-case it here.
+ *
+ * Dalvik also puts all private methods into the "direct" list, so we
+ * really need to just search both lists.
+ */
+static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+    } else if (dvmIsInterfaceClass(clazz)) {
+        Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig);
+        if (meth == NULL) {
+            dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no method with name='%s' signature='%s' in interface %s",
+                name, sig, clazz->descriptor);
+        }
+        return (jmethodID) meth;
+    }
+    Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
+    if (meth == NULL) {
+        /* search private methods and constructors; non-hierarchical */
+        meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
+    }
+    if (meth != NULL && dvmIsStaticMethod(meth)) {
+        IF_ALOGD() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGD("GetMethodID: not returning static method %s.%s %s",
+                    clazz->descriptor, meth->name, desc);
+            free(desc);
+        }
+        meth = NULL;
+    }
+    if (meth == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no method with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    } else {
+        /*
+         * The method's class may not be the same as clazz, but if
+         * it isn't this must be a virtual method and the class must
+         * be a superclass (and, hence, already initialized).
+         */
+        assert(dvmIsClassInitialized(meth->clazz) || dvmIsClassInitializing(meth->clazz));
+    }
+    return (jmethodID) meth;
+}
+
+/*
+ * Get a field ID (instance fields).
+ */
+static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    jfieldID id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
+                "no field with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get the method ID for a static method in a class.
+ */
+static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    Method* meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
+
+    /* make sure it's static, not virtual+private */
+    if (meth != NULL && !dvmIsStaticMethod(meth)) {
+        IF_ALOGD() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGD("GetStaticMethodID: not returning nonstatic method %s.%s %s",
+                    clazz->descriptor, meth->name, desc);
+            free(desc);
+        }
+        meth = NULL;
+    }
+
+    jmethodID id = (jmethodID) meth;
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
+                "no static method with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get a field ID (static fields).
+ */
+static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+
+    jfieldID id = (jfieldID) dvmFindStaticFieldHier(clazz, name, sig);
+    if (id == NULL) {
+        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
+                "no static field with name='%s' signature='%s' in class %s",
+                name, sig, clazz->descriptor);
+    }
+    return id;
+}
+
+/*
+ * Get a static field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref)                       \
+    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz,      \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(sfield)) {                                   \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* obj = dvmGetStaticFieldObjectVolatile(sfield);      \
+                value = (_ctype)(u4)addLocalReference(ts.self(), obj);            \
+            } else {                                                        \
+                value = (_ctype) dvmGetStaticField##_jname##Volatile(sfield);\
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* obj = dvmGetStaticFieldObject(sfield);              \
+                value = (_ctype)(u4)addLocalReference(ts.self(), obj);            \
+            } else {                                                        \
+                value = (_ctype) dvmGetStaticField##_jname(sfield);         \
+            }                                                               \
+        }                                                                   \
+        return value;                                                       \
+    }
+GET_STATIC_TYPE_FIELD(jobject, Object, true);
+GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+GET_STATIC_TYPE_FIELD(jchar, Char, false);
+GET_STATIC_TYPE_FIELD(jshort, Short, false);
+GET_STATIC_TYPE_FIELD(jint, Int, false);
+GET_STATIC_TYPE_FIELD(jlong, Long, false);
+GET_STATIC_TYPE_FIELD(jfloat, Float, false);
+GET_STATIC_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set a static field.
+ */
+#define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)              \
+    static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz,        \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        if (dvmIsVolatileField(sfield)) {                                   \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetStaticFieldObjectVolatile(sfield, valObj);            \
+            } else {                                                        \
+                dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);\
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetStaticFieldObject(sfield, valObj);                    \
+            } else {                                                        \
+                dvmSetStaticField##_jname(sfield, (_ctype2)value);          \
+            }                                                               \
+        }                                                                   \
+    }
+SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, u2, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, s2, Short, false);
+SET_STATIC_TYPE_FIELD(jint, s4, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, s8, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, float, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, double, Double, false);
+
+/*
+ * Get an instance field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_TYPE_FIELD(_ctype, _jname, _isref)                              \
+    static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj,             \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        InstField* field = (InstField*) fieldID;                            \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(field)) {                            \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj =                                            \
+                    dvmGetFieldObjectVolatile(obj, field->byteOffset);      \
+                value = (_ctype)(u4)addLocalReference(ts.self(), valObj);         \
+            } else {                                                        \
+                value = (_ctype)                                            \
+                    dvmGetField##_jname##Volatile(obj, field->byteOffset);  \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
+                value = (_ctype)(u4)addLocalReference(ts.self(), valObj);         \
+            } else {                                                        \
+                value = (_ctype) dvmGetField##_jname(obj, field->byteOffset);\
+            }                                                               \
+        }                                                                   \
+        return value;                                                       \
+    }
+GET_TYPE_FIELD(jobject, Object, true);
+GET_TYPE_FIELD(jboolean, Boolean, false);
+GET_TYPE_FIELD(jbyte, Byte, false);
+GET_TYPE_FIELD(jchar, Char, false);
+GET_TYPE_FIELD(jshort, Short, false);
+GET_TYPE_FIELD(jint, Int, false);
+GET_TYPE_FIELD(jlong, Long, false);
+GET_TYPE_FIELD(jfloat, Float, false);
+GET_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set an instance field.
+ */
+#define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)                     \
+    static void Set##_jname##Field(JNIEnv* env, jobject jobj,               \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); \
+        InstField* field = (InstField*) fieldID;                            \
+        if (dvmIsVolatileField(field)) {                                    \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj);  \
+            } else {                                                        \
+                dvmSetField##_jname##Volatile(obj,                          \
+                    field->byteOffset, (_ctype2)value);                     \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); \
+                dvmSetFieldObject(obj, field->byteOffset, valObj);          \
+            } else {                                                        \
+                dvmSetField##_jname(obj,                                    \
+                    field->byteOffset, (_ctype2)value);                     \
+            }                                                               \
+        }                                                                   \
+    }
+SET_TYPE_FIELD(jobject, Object*, Object, true);
+SET_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_TYPE_FIELD(jchar, u2, Char, false);
+SET_TYPE_FIELD(jshort, s2, Short, false);
+SET_TYPE_FIELD(jint, s4, Int, false);
+SET_TYPE_FIELD(jlong, s8, Long, false);
+SET_TYPE_FIELD(jfloat, float, Float, false);
+SET_TYPE_FIELD(jdouble, double, Double, false);
+
+/*
+ * Make a virtual method call.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type.  If we're
+ * returning an Object, we have to add it to the local references table.
+ */
+#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref)              \
+    static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj,           \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
+CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_VIRTUAL(jchar, Char, 0, result.c, false);
+CALL_VIRTUAL(jshort, Short, 0, result.s, false);
+CALL_VIRTUAL(jint, Int, 0, result.i, false);
+CALL_VIRTUAL(jlong, Long, 0, result.j, false);
+CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_VIRTUAL(void, Void, , , false);
+
+/*
+ * Make a "non-virtual" method call.  We're still calling a virtual method,
+ * but this time we're not doing an indirection through the object's vtable.
+ * The "clazz" parameter defines which implementation of a method we want.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type.
+ */
+#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref)           \
+    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
+        jclass jclazz, jmethodID methodID, ...)                             \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        va_end(args);                                                       \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, va_list args)                    \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);                      \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, jvalue* args)                    \
+    {                                                                       \
+        ScopedJniThreadState ts(env);                                       \
+        Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); \
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_NONVIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
+CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
+CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
+CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
+CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
+CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_NONVIRTUAL(void, Void, , , false);
+
+
+/*
+ * Call a static method.
+ */
+#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
+    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        ScopedJniThreadState ts(env);                                       \
+        JValue result;                                                      \
+        dvmCallMethodA(ts.self(), (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(ts.self()))                        \
+            result.l = (Object*)addLocalReference(ts.self(), result.l);           \
+        return _retok;                                                      \
+    }
+CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true);
+CALL_STATIC(jboolean, Boolean, 0, result.z, false);
+CALL_STATIC(jbyte, Byte, 0, result.b, false);
+CALL_STATIC(jchar, Char, 0, result.c, false);
+CALL_STATIC(jshort, Short, 0, result.s, false);
+CALL_STATIC(jint, Int, 0, result.i, false);
+CALL_STATIC(jlong, Long, 0, result.j, false);
+CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
+CALL_STATIC(jdouble, Double, 0.0, result.d, false);
+CALL_STATIC(void, Void, , , false);
+
+/*
+ * Create a new String from Unicode data.
+ *
+ * If "len" is zero, we will return an empty string even if "unicodeChars"
+ * is NULL.  (The JNI spec is vague here.)
+ */
+static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
+    ScopedJniThreadState ts(env);
+    StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
+    if (jstr == NULL) {
+        return NULL;
+    }
+    dvmReleaseTrackedAlloc((Object*) jstr, NULL);
+    return (jstring) addLocalReference(ts.self(), (Object*) jstr);
+}
+
+/*
+ * Return the length of a String in Unicode character units.
+ */
+static jsize GetStringLength(JNIEnv* env, jstring jstr) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    return strObj->length();
+}
+
+
+/*
+ * Get a string's character data.
+ *
+ * The result is guaranteed to be valid until ReleaseStringChars is
+ * called, which means we have to pin it or return a copy.
+ */
+static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = strObj->chars();
+    if (isCopy != NULL) {
+        *isCopy = JNI_FALSE;
+    }
+    return (jchar*) data;
+}
+
+/*
+ * Release our grip on some characters from a string.
+ */
+static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+    unpinPrimitiveArray(strChars);
+}
+
+/*
+ * Create a new java.lang.String object from chars in modified UTF-8 form.
+ *
+ * The spec doesn't say how to handle a NULL string.  Popular desktop VMs
+ * accept it and return a NULL pointer in response.
+ */
+static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
+    ScopedJniThreadState ts(env);
+    if (bytes == NULL) {
+        return NULL;
+    }
+    /* note newStr could come back NULL on OOM */
+    StringObject* newStr = dvmCreateStringFromCstr(bytes);
+    jstring result = (jstring) addLocalReference(ts.self(), (Object*) newStr);
+    dvmReleaseTrackedAlloc((Object*)newStr, NULL);
+    return result;
+}
+
+/*
+ * Return the length in bytes of the modified UTF-8 form of the string.
+ */
+static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    if (strObj == NULL) {
+        return 0; // Should we throw something or assert?
+    }
+    return strObj->utfLength();
+}
+
+/*
+ * Convert "string" to modified UTF-8 and return a pointer.  The returned
+ * value must be released with ReleaseStringUTFChars.
+ *
+ * According to the JNI reference, "Returns a pointer to a UTF-8 string,
+ * or NULL if the operation fails. Returns NULL if and only if an invocation
+ * of this function has thrown an exception."
+ *
+ * The behavior here currently follows that of other open-source VMs, which
+ * quietly return NULL if "string" is NULL.  We should consider throwing an
+ * NPE.  (The CheckJNI code blows up if you try to pass in a NULL string,
+ * which should catch this sort of thing during development.)  Certain other
+ * VMs will crash with a segmentation fault.
+ */
+static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    if (jstr == NULL) {
+        /* this shouldn't happen; throw NPE? */
+        return NULL;
+    }
+    if (isCopy != NULL) {
+        *isCopy = JNI_TRUE;
+    }
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    char* newStr = dvmCreateCstrFromString(strObj);
+    if (newStr == NULL) {
+        /* assume memory failure */
+        dvmThrowOutOfMemoryError("native heap string alloc failed");
+    }
+    return newStr;
+}
+
+/*
+ * Release a string created by GetStringUTFChars().
+ */
+static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) {
+    ScopedJniThreadState ts(env);
+    free((char*) utf);
+}
+
+/*
+ * Return the capacity of the array.
+ */
+static jsize GetArrayLength(JNIEnv* env, jarray jarr) {
+    ScopedJniThreadState ts(env);
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    return arrObj->length;
+}
+
+/*
+ * Construct a new array that holds objects from class "elementClass".
+ */
+static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
+    jclass jelementClass, jobject jinitialElement)
+{
+    ScopedJniThreadState ts(env);
+
+    if (jelementClass == NULL) {
+        dvmThrowNullPointerException("JNI NewObjectArray elementClass == NULL");
+        return NULL;
+    }
+
+    ClassObject* elemClassObj = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jelementClass);
+    ClassObject* arrayClass = dvmFindArrayClassForElement(elemClassObj);
+    ArrayObject* newObj = dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);
+    if (newObj == NULL) {
+        assert(dvmCheckException(ts.self()));
+        return NULL;
+    }
+    jobjectArray newArray = (jobjectArray) addLocalReference(ts.self(), (Object*) newObj);
+    dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+
+    /*
+     * Initialize the array.
+     */
+    if (jinitialElement != NULL) {
+        Object* initialElement = dvmDecodeIndirectRef(ts.self(), jinitialElement);
+        Object** arrayData = (Object**) (void*) newObj->contents;
+        for (jsize i = 0; i < length; ++i) {
+            arrayData[i] = initialElement;
+        }
+    }
+
+    return newArray;
+}
+
+static bool checkArrayElementBounds(ArrayObject* arrayObj, jsize index) {
+    assert(arrayObj != NULL);
+    if (index < 0 || index >= (int) arrayObj->length) {
+        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, index);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Get one element of an Object array.
+ *
+ * Add the object to the local references table in case the array goes away.
+ */
+static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) {
+    ScopedJniThreadState ts(env);
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    if (!checkArrayElementBounds(arrayObj, index)) {
+        return NULL;
+    }
+
+    Object* value = ((Object**) (void*) arrayObj->contents)[index];
+    return addLocalReference(ts.self(), value);
+}
+
+/*
+ * Set one element of an Object array.
+ */
+static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index, jobject jobj) {
+    ScopedJniThreadState ts(env);
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    if (!checkArrayElementBounds(arrayObj, index)) {
+        return;
+    }
+
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+
+    if (obj != NULL && !dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+      ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+            obj->clazz->descriptor, obj,
+            arrayObj->clazz->descriptor, arrayObj);
+      dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+      return;
+    }
+
+    //ALOGV("JNI: set element %d in array %p to %p", index, array, value);
+
+    dvmSetObjectArrayElement(arrayObj, index, obj);
+}
+
+/*
+ * Create a new array of primitive elements.
+ */
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
+    static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = dvmAllocPrimitiveArray(_typechar, length, ALLOC_DEFAULT); \
+        if (arrayObj == NULL) { \
+            return NULL; \
+        } \
+        _artype result = (_artype) addLocalReference(ts.self(), (Object*) arrayObj); \
+        dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
+        return result; \
+    }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
+NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
+NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
+NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
+NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
+
+/*
+ * Get a pointer to a C array of primitive elements from an array object
+ * of the matching type.
+ *
+ * In a compacting GC, we either need to return a copy of the elements or
+ * "pin" the memory.  Otherwise we run the risk of native code using the
+ * buffer as the destination of e.g. a blocking read() call that wakes up
+ * during a GC.
+ */
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+    static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
+        _ctype##Array jarr, jboolean* isCopy) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        pinPrimitiveArray(arrayObj); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (isCopy != NULL) { \
+            *isCopy = JNI_FALSE; \
+        } \
+        return data; \
+    }
+
+/*
+ * Release the storage locked down by the "get" function.
+ *
+ * The spec says, "'mode' has no effect if 'elems' is not a copy of the
+ * elements in 'array'."  They apparently did not anticipate the need to
+ * un-pin memory.
+ */
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                    \
+    static void Release##_jname##ArrayElements(JNIEnv* env,                 \
+        _ctype##Array jarr, _ctype* elems, jint mode)                       \
+    {                                                                       \
+        UNUSED_PARAMETER(elems);                                            \
+        if (mode != JNI_COMMIT) {                                           \
+            ScopedJniThreadState ts(env);                                   \
+            ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+            unpinPrimitiveArray(arrayObj);                                  \
+        }                                                                   \
+    }
+
+static void throwArrayRegionOutOfBounds(ArrayObject* arrayObj, jsize start,
+    jsize len, const char* arrayIdentifier)
+{
+    dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+        "%s offset=%d length=%d %s.length=%d",
+        arrayObj->clazz->descriptor, start, len, arrayIdentifier,
+        arrayObj->length);
+}
+
+/*
+ * Copy a section of a primitive array to a buffer.
+ */
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Get##_jname##ArrayRegion(JNIEnv* env, \
+        _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            throwArrayRegionOutOfBounds(arrayObj, start, len, "src"); \
+        } else { \
+            memcpy(buf, data + start, len * sizeof(_ctype)); \
+        } \
+    }
+
+/*
+ * Copy a section of a primitive array from a buffer.
+ */
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+    static void Set##_jname##ArrayRegion(JNIEnv* env, \
+        _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
+    { \
+        ScopedJniThreadState ts(env); \
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
+        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            throwArrayRegionOutOfBounds(arrayObj, start, len, "dst"); \
+        } else { \
+            memcpy(data + start, buf, len * sizeof(_ctype)); \
+        } \
+    }
+
+/*
+ * 4-in-1:
+ *  Get<Type>ArrayElements
+ *  Release<Type>ArrayElements
+ *  Get<Type>ArrayRegion
+ *  Set<Type>ArrayRegion
+ */
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname)                           \
+    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                           \
+    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                       \
+    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);                             \
+    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
+
+/*
+ * Register one or more native functions in one class.
+ *
+ * This can be called multiple times on the same method, allowing the
+ * caller to redefine the method implementation at will.
+ */
+static jint RegisterNatives(JNIEnv* env, jclass jclazz,
+    const JNINativeMethod* methods, jint nMethods)
+{
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+
+    if (gDvm.verboseJni) {
+        ALOGI("[Registering JNI native methods for class %s]",
+            clazz->descriptor);
+    }
+
+    for (int i = 0; i < nMethods; i++) {
+        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
+                methods[i].signature, methods[i].fnPtr))
+        {
+            return JNI_ERR;
+        }
+    }
+    return JNI_OK;
+}
+
+/*
+ * Un-register all native methods associated with the class.
+ *
+ * The JNI docs refer to this as a way to reload/relink native libraries,
+ * and say it "should not be used in normal native code".  In particular,
+ * there is no need to do this during shutdown, and you do not need to do
+ * this before redefining a method implementation with RegisterNatives.
+ *
+ * It's chiefly useful for a native "plugin"-style library that wasn't
+ * loaded with System.loadLibrary() (since there's no way to unload those).
+ * For example, the library could upgrade itself by:
+ *
+ *  1. call UnregisterNatives to unbind the old methods
+ *  2. ensure that no code is still executing inside it (somehow)
+ *  3. dlclose() the library
+ *  4. dlopen() the new library
+ *  5. use RegisterNatives to bind the methods from the new library
+ *
+ * The above can work correctly without the UnregisterNatives call, but
+ * creates a window of opportunity in which somebody might try to call a
+ * method that is pointing at unmapped memory, crashing the VM.  In theory
+ * the same guards that prevent dlclose() from unmapping executing code could
+ * prevent that anyway, but with this we can be more thorough and also deal
+ * with methods that only exist in the old or new form of the library (maybe
+ * the lib wants to try the call and catch the UnsatisfiedLinkError).
+ */
+static jint UnregisterNatives(JNIEnv* env, jclass jclazz) {
+    ScopedJniThreadState ts(env);
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);
+    if (gDvm.verboseJni) {
+        ALOGI("[Unregistering JNI native methods for class %s]",
+            clazz->descriptor);
+    }
+    dvmUnregisterJNINativeMethods(clazz);
+    return JNI_OK;
+}
+
+/*
+ * Lock the monitor.
+ *
+ * We have to track all monitor enters and exits, so that we can undo any
+ * outstanding synchronization before the thread exits.
+ */
+static jint MonitorEnter(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    dvmLockObject(ts.self(), obj);
+    trackMonitorEnter(ts.self(), obj);
+    return JNI_OK;
+}
+
+/*
+ * Unlock the monitor.
+ *
+ * Throws an IllegalMonitorStateException if the current thread
+ * doesn't own the monitor.  (dvmUnlockObject() takes care of the throw.)
+ *
+ * According to the 1.6 spec, it's legal to call here with an exception
+ * pending.  If this fails, we'll stomp the original exception.
+ */
+static jint MonitorExit(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    bool success = dvmUnlockObject(ts.self(), obj);
+    if (success) {
+        trackMonitorExit(ts.self(), obj);
+    }
+    return success ? JNI_OK : JNI_ERR;
+}
+
+/*
+ * Return the JavaVM interface associated with the current thread.
+ */
+static jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
+    ScopedJniThreadState ts(env);
+    *vm = gDvmJni.jniVm;
+    return (*vm == NULL) ? JNI_ERR : JNI_OK;
+}
+
+/*
+ * Copies "len" Unicode characters, from offset "start".
+ */
+static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    int strLen = strObj->length();
+    if (((start|len) < 0) || (start + len > strLen)) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
+        return;
+    }
+    memcpy(buf, strObj->chars() + start, len * sizeof(u2));
+}
+
+/*
+ * Translates "len" Unicode characters, from offset "start", into
+ * modified UTF-8 encoding.
+ */
+static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    int strLen = strObj->length();
+    if (((start|len) < 0) || (start + len > strLen)) {
+        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
+        return;
+    }
+    dvmGetStringUtfRegion(strObj, start, len, buf);
+}
+
+/*
+ * Get a raw pointer to array data.
+ *
+ * The caller is expected to call "release" before doing any JNI calls
+ * or blocking I/O operations.
+ *
+ * We need to pin the memory or block GC.
+ */
+static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+    pinPrimitiveArray(arrayObj);
+    void* data = arrayObj->contents;
+    if (UNLIKELY(isCopy != NULL)) {
+        *isCopy = JNI_FALSE;
+    }
+    return data;
+}
+
+/*
+ * Release an array obtained with GetPrimitiveArrayCritical.
+ */
+static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) {
+    if (mode != JNI_COMMIT) {
+        ScopedJniThreadState ts(env);
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);
+        unpinPrimitiveArray(arrayObj);
+    }
+}
+
+/*
+ * Like GetStringChars, but with restricted use.
+ */
+static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = strObj->chars();
+    if (isCopy != NULL) {
+        *isCopy = JNI_FALSE;
+    }
+    return (jchar*) data;
+}
+
+/*
+ * Like ReleaseStringChars, but with restricted use.
+ */
+static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) {
+    ScopedJniThreadState ts(env);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);
+    ArrayObject* strChars = strObj->array();
+    unpinPrimitiveArray(strChars);
+}
+
+/*
+ * Create a new weak global reference.
+ */
+static jweak NewWeakGlobalRef(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    Object *obj = dvmDecodeIndirectRef(ts.self(), jobj);
+    return (jweak) addWeakGlobalReference(obj);
+}
+
+/*
+ * Delete the specified weak global reference.
+ */
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) {
+    ScopedJniThreadState ts(env);
+    deleteWeakGlobalReference(wref);
+}
+
+/*
+ * Quick check for pending exceptions.
+ *
+ * TODO: we should be able to skip the enter/exit macros here.
+ */
+static jboolean ExceptionCheck(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    return dvmCheckException(ts.self());
+}
+
+/*
+ * Returns the type of the object referred to by "obj".  It can be local,
+ * global, or weak global.
+ *
+ * In the current implementation, references can be global and local at
+ * the same time, so while the return value is accurate it may not tell
+ * the whole story.
+ */
+static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) {
+    ScopedJniThreadState ts(env);
+    return dvmGetJNIRefType(ts.self(), jobj);
+}
+
+/*
+ * Allocate and return a new java.nio.ByteBuffer for this block of memory.
+ */
+static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+    ScopedJniThreadState ts(env);
+
+    if (capacity < 0) {
+        ALOGE("JNI ERROR (app bug): negative buffer capacity: %lld", capacity);
+        ReportJniError();
+    }
+    if (address == NULL && capacity != 0) {
+        ALOGE("JNI ERROR (app bug): non-zero capacity for NULL pointer: %lld", capacity);
+        ReportJniError();
+    }
+
+    /* create an instance of java.nio.DirectByteBuffer */
+    ClassObject* bufferClazz = gDvm.classJavaNioDirectByteBuffer;
+    if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) {
+        return NULL;
+    }
+    Object* newObj = dvmAllocObject(bufferClazz, ALLOC_DONT_TRACK);
+    if (newObj == NULL) {
+        return NULL;
+    }
+    /* call the constructor */
+    jobject result = addLocalReference(ts.self(), newObj);
+    JValue unused;
+    dvmCallMethod(ts.self(), gDvm.methJavaNioDirectByteBuffer_init,
+            newObj, &unused, (jlong) address, (jint) capacity);
+    if (dvmGetException(ts.self()) != NULL) {
+        deleteLocalReference(ts.self(), result);
+        return NULL;
+    }
+    return result;
+}
+
+/*
+ * Get the starting address of the buffer for the specified java.nio.Buffer.
+ *
+ * If this is not a "direct" buffer, we return NULL.
+ */
+static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) {
+    ScopedJniThreadState ts(env);
+
+    // All Buffer objects have an effectiveDirectAddress field.
+    Object* bufObj = dvmDecodeIndirectRef(ts.self(), jbuf);
+    return (void*) dvmGetFieldLong(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);
+}
+
+/*
+ * Get the capacity of the buffer for the specified java.nio.Buffer.
+ *
+ * Returns -1 if the object is not a direct buffer.  (We actually skip
+ * this check, since it's expensive to determine, and just return the
+ * capacity regardless.)
+ */
+static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) {
+    ScopedJniThreadState ts(env);
+
+    /*
+     * The capacity is always in the Buffer.capacity field.
+     *
+     * (The "check" version should verify that this is actually a Buffer,
+     * but we're not required to do so here.)
+     */
+    Object* buf = dvmDecodeIndirectRef(ts.self(), jbuf);
+    return dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI invocation functions
+ * ===========================================================================
+ */
+
+/*
+ * Handle AttachCurrentThread{AsDaemon}.
+ *
+ * We need to make sure the VM is actually running.  For example, if we start
+ * up, issue an Attach, and the VM exits almost immediately, by the time the
+ * attaching happens the VM could already be shutting down.
+ *
+ * It's hard to avoid a race condition here because we don't want to hold
+ * a lock across the entire operation.  What we can do is temporarily
+ * increment the thread count to prevent a VM exit.
+ *
+ * This could potentially still have problems if a daemon thread calls here
+ * while the VM is shutting down.  dvmThreadSelf() will work, since it just
+ * uses pthread TLS, but dereferencing "vm" could fail.  Such is life when
+ * you shut down a VM while threads are still running inside it.
+ *
+ * Remember that some code may call this as a way to find the per-thread
+ * JNIEnv pointer.  Don't do excess work for that case.
+ */
+static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool isDaemon) {
+    JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
+
+    /*
+     * Return immediately if we're already one with the VM.
+     */
+    Thread* self = dvmThreadSelf();
+    if (self != NULL) {
+        *p_env = self->jniEnv;
+        return JNI_OK;
+    }
+
+    /*
+     * No threads allowed in zygote mode.
+     */
+    if (gDvm.zygote) {
+        return JNI_ERR;
+    }
+
+    /* increment the count to keep the VM from bailing while we run */
+    dvmLockThreadList(NULL);
+    if (gDvm.nonDaemonThreadCount == 0) {
+        // dead or dying
+        ALOGV("Refusing to attach thread '%s' -- VM is shutting down",
+            (thr_args == NULL) ? "(unknown)" : args->name);
+        dvmUnlockThreadList();
+        return JNI_ERR;
+    }
+    gDvm.nonDaemonThreadCount++;
+    dvmUnlockThreadList();
+
+    /* tweak the JavaVMAttachArgs as needed */
+    JavaVMAttachArgs argsCopy;
+    if (args == NULL) {
+        /* allow the v1.1 calling convention */
+        argsCopy.version = JNI_VERSION_1_2;
+        argsCopy.name = NULL;
+        argsCopy.group = (jobject) dvmGetMainThreadGroup();
+    } else {
+        if (dvmIsBadJniVersion(args->version)) {
+            ALOGE("Bad JNI version passed to %s: %d",
+                  (isDaemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread"),
+                  args->version);
+            return JNI_EVERSION;
+        }
+
+        argsCopy.version = args->version;
+        argsCopy.name = args->name;
+        if (args->group != NULL) {
+            argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group);
+        } else {
+            argsCopy.group = (jobject) dvmGetMainThreadGroup();
+        }
+    }
+
+    bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);
+
+    /* restore the count */
+    dvmLockThreadList(NULL);
+    gDvm.nonDaemonThreadCount--;
+    dvmUnlockThreadList();
+
+    /*
+     * Change the status to indicate that we're out in native code.  This
+     * call is not guarded with state-change macros, so we have to do it
+     * by hand.
+     */
+    if (result) {
+        self = dvmThreadSelf();
+        assert(self != NULL);
+        dvmChangeStatus(self, THREAD_NATIVE);
+        *p_env = self->jniEnv;
+        return JNI_OK;
+    } else {
+        return JNI_ERR;
+    }
+}
+
+/*
+ * Attach the current thread to the VM.  If the thread is already attached,
+ * this is a no-op.
+ */
+static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+    return attachThread(vm, p_env, thr_args, false);
+}
+
+/*
+ * Like AttachCurrentThread, but set the "daemon" flag.
+ */
+static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args)
+{
+    return attachThread(vm, p_env, thr_args, true);
+}
+
+/*
+ * Dissociate the current thread from the VM.
+ */
+static jint DetachCurrentThread(JavaVM* vm) {
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        /* not attached, can't do anything */
+        return JNI_ERR;
+    }
+
+    /* switch to "running" to check for suspension */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /* detach the thread */
+    dvmDetachCurrentThread();
+
+    /* (no need to change status back -- we have no status) */
+    return JNI_OK;
+}
+
+/*
+ * If current thread is attached to VM, return the associated JNIEnv.
+ * Otherwise, stuff NULL in and return JNI_EDETACHED.
+ *
+ * JVMTI overloads this by specifying a magic value for "version", so we
+ * do want to check that here.
+ */
+static jint GetEnv(JavaVM* vm, void** env, jint version) {
+    Thread* self = dvmThreadSelf();
+
+    // GetEnv also accepts JNI_VERSION_1_1, but always returns a JNIEnv*
+    // corresponding to the most current supported JNI version.
+    if (dvmIsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+        ALOGE("Bad JNI version passed to GetEnv: %d", version);
+        return JNI_EVERSION;
+    }
+
+    if (self == NULL) {
+        *env = NULL;
+    } else {
+        /* TODO: status change is probably unnecessary */
+        dvmChangeStatus(self, THREAD_RUNNING);
+        *env = (void*) dvmGetThreadJNIEnv(self);
+        dvmChangeStatus(self, THREAD_NATIVE);
+    }
+    return (*env != NULL) ? JNI_OK : JNI_EDETACHED;
+}
+
+/*
+ * Destroy the VM.  This may be called from any thread.
+ *
+ * If the current thread is attached, wait until the current thread is
+ * the only non-daemon user-level thread.  If the current thread is not
+ * attached, we attach it and do the processing as usual.  (If the attach
+ * fails, it's probably because all the non-daemon threads have already
+ * exited and the VM doesn't want to let us back in.)
+ *
+ * TODO: we don't really deal with the situation where more than one thread
+ * has called here.  One thread wins, the other stays trapped waiting on
+ * the condition variable forever.  Not sure this situation is interesting
+ * in real life.
+ */
+static jint DestroyJavaVM(JavaVM* vm) {
+    JavaVMExt* ext = (JavaVMExt*) vm;
+    if (ext == NULL) {
+        return JNI_ERR;
+    }
+
+    if (gDvm.verboseShutdown) {
+        ALOGD("DestroyJavaVM waiting for non-daemon threads to exit");
+    }
+
+    /*
+     * Sleep on a condition variable until it's okay to exit.
+     */
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        JNIEnv* tmpEnv;
+        if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
+            ALOGV("Unable to reattach main for Destroy; assuming VM is shutting down (count=%d)",
+                gDvm.nonDaemonThreadCount);
+            goto shutdown;
+        } else {
+            ALOGV("Attached to wait for shutdown in Destroy");
+        }
+    }
+    dvmChangeStatus(self, THREAD_VMWAIT);
+
+    dvmLockThreadList(self);
+    gDvm.nonDaemonThreadCount--;    // remove current thread from count
+
+    while (gDvm.nonDaemonThreadCount > 0) {
+        pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
+    }
+
+    dvmUnlockThreadList();
+    self = NULL;
+
+shutdown:
+    // TODO: call System.exit() to run any registered shutdown hooks
+    // (this may not return -- figure out how this should work)
+
+    if (gDvm.verboseShutdown) {
+        ALOGD("DestroyJavaVM shutting VM down");
+    }
+    dvmShutdown();
+
+    // TODO - free resources associated with JNI-attached daemon threads
+    free(ext->envList);
+    free(ext);
+
+    return JNI_OK;
+}
+
+
+/*
+ * ===========================================================================
+ *      Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gNativeInterface = {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+
+    GetVersion,
+
+    DefineClass,
+    FindClass,
+
+    FromReflectedMethod,
+    FromReflectedField,
+    ToReflectedMethod,
+
+    GetSuperclass,
+    IsAssignableFrom,
+
+    ToReflectedField,
+
+    Throw,
+    ThrowNew,
+    ExceptionOccurred,
+    ExceptionDescribe,
+    ExceptionClear,
+    FatalError,
+
+    PushLocalFrame,
+    PopLocalFrame,
+
+    NewGlobalRef,
+    DeleteGlobalRef,
+    DeleteLocalRef,
+    IsSameObject,
+    NewLocalRef,
+    EnsureLocalCapacity,
+
+    AllocObject,
+    NewObject,
+    NewObjectV,
+    NewObjectA,
+
+    GetObjectClass,
+    IsInstanceOf,
+
+    GetMethodID,
+
+    CallObjectMethod,
+    CallObjectMethodV,
+    CallObjectMethodA,
+    CallBooleanMethod,
+    CallBooleanMethodV,
+    CallBooleanMethodA,
+    CallByteMethod,
+    CallByteMethodV,
+    CallByteMethodA,
+    CallCharMethod,
+    CallCharMethodV,
+    CallCharMethodA,
+    CallShortMethod,
+    CallShortMethodV,
+    CallShortMethodA,
+    CallIntMethod,
+    CallIntMethodV,
+    CallIntMethodA,
+    CallLongMethod,
+    CallLongMethodV,
+    CallLongMethodA,
+    CallFloatMethod,
+    CallFloatMethodV,
+    CallFloatMethodA,
+    CallDoubleMethod,
+    CallDoubleMethodV,
+    CallDoubleMethodA,
+    CallVoidMethod,
+    CallVoidMethodV,
+    CallVoidMethodA,
+
+    CallNonvirtualObjectMethod,
+    CallNonvirtualObjectMethodV,
+    CallNonvirtualObjectMethodA,
+    CallNonvirtualBooleanMethod,
+    CallNonvirtualBooleanMethodV,
+    CallNonvirtualBooleanMethodA,
+    CallNonvirtualByteMethod,
+    CallNonvirtualByteMethodV,
+    CallNonvirtualByteMethodA,
+    CallNonvirtualCharMethod,
+    CallNonvirtualCharMethodV,
+    CallNonvirtualCharMethodA,
+    CallNonvirtualShortMethod,
+    CallNonvirtualShortMethodV,
+    CallNonvirtualShortMethodA,
+    CallNonvirtualIntMethod,
+    CallNonvirtualIntMethodV,
+    CallNonvirtualIntMethodA,
+    CallNonvirtualLongMethod,
+    CallNonvirtualLongMethodV,
+    CallNonvirtualLongMethodA,
+    CallNonvirtualFloatMethod,
+    CallNonvirtualFloatMethodV,
+    CallNonvirtualFloatMethodA,
+    CallNonvirtualDoubleMethod,
+    CallNonvirtualDoubleMethodV,
+    CallNonvirtualDoubleMethodA,
+    CallNonvirtualVoidMethod,
+    CallNonvirtualVoidMethodV,
+    CallNonvirtualVoidMethodA,
+
+    GetFieldID,
+
+    GetObjectField,
+    GetBooleanField,
+    GetByteField,
+    GetCharField,
+    GetShortField,
+    GetIntField,
+    GetLongField,
+    GetFloatField,
+    GetDoubleField,
+    SetObjectField,
+    SetBooleanField,
+    SetByteField,
+    SetCharField,
+    SetShortField,
+    SetIntField,
+    SetLongField,
+    SetFloatField,
+    SetDoubleField,
+
+    GetStaticMethodID,
+
+    CallStaticObjectMethod,
+    CallStaticObjectMethodV,
+    CallStaticObjectMethodA,
+    CallStaticBooleanMethod,
+    CallStaticBooleanMethodV,
+    CallStaticBooleanMethodA,
+    CallStaticByteMethod,
+    CallStaticByteMethodV,
+    CallStaticByteMethodA,
+    CallStaticCharMethod,
+    CallStaticCharMethodV,
+    CallStaticCharMethodA,
+    CallStaticShortMethod,
+    CallStaticShortMethodV,
+    CallStaticShortMethodA,
+    CallStaticIntMethod,
+    CallStaticIntMethodV,
+    CallStaticIntMethodA,
+    CallStaticLongMethod,
+    CallStaticLongMethodV,
+    CallStaticLongMethodA,
+    CallStaticFloatMethod,
+    CallStaticFloatMethodV,
+    CallStaticFloatMethodA,
+    CallStaticDoubleMethod,
+    CallStaticDoubleMethodV,
+    CallStaticDoubleMethodA,
+    CallStaticVoidMethod,
+    CallStaticVoidMethodV,
+    CallStaticVoidMethodA,
+
+    GetStaticFieldID,
+
+    GetStaticObjectField,
+    GetStaticBooleanField,
+    GetStaticByteField,
+    GetStaticCharField,
+    GetStaticShortField,
+    GetStaticIntField,
+    GetStaticLongField,
+    GetStaticFloatField,
+    GetStaticDoubleField,
+
+    SetStaticObjectField,
+    SetStaticBooleanField,
+    SetStaticByteField,
+    SetStaticCharField,
+    SetStaticShortField,
+    SetStaticIntField,
+    SetStaticLongField,
+    SetStaticFloatField,
+    SetStaticDoubleField,
+
+    NewString,
+
+    GetStringLength,
+    GetStringChars,
+    ReleaseStringChars,
+
+    NewStringUTF,
+    GetStringUTFLength,
+    GetStringUTFChars,
+    ReleaseStringUTFChars,
+
+    GetArrayLength,
+    NewObjectArray,
+    GetObjectArrayElement,
+    SetObjectArrayElement,
+
+    NewBooleanArray,
+    NewByteArray,
+    NewCharArray,
+    NewShortArray,
+    NewIntArray,
+    NewLongArray,
+    NewFloatArray,
+    NewDoubleArray,
+
+    GetBooleanArrayElements,
+    GetByteArrayElements,
+    GetCharArrayElements,
+    GetShortArrayElements,
+    GetIntArrayElements,
+    GetLongArrayElements,
+    GetFloatArrayElements,
+    GetDoubleArrayElements,
+
+    ReleaseBooleanArrayElements,
+    ReleaseByteArrayElements,
+    ReleaseCharArrayElements,
+    ReleaseShortArrayElements,
+    ReleaseIntArrayElements,
+    ReleaseLongArrayElements,
+    ReleaseFloatArrayElements,
+    ReleaseDoubleArrayElements,
+
+    GetBooleanArrayRegion,
+    GetByteArrayRegion,
+    GetCharArrayRegion,
+    GetShortArrayRegion,
+    GetIntArrayRegion,
+    GetLongArrayRegion,
+    GetFloatArrayRegion,
+    GetDoubleArrayRegion,
+    SetBooleanArrayRegion,
+    SetByteArrayRegion,
+    SetCharArrayRegion,
+    SetShortArrayRegion,
+    SetIntArrayRegion,
+    SetLongArrayRegion,
+    SetFloatArrayRegion,
+    SetDoubleArrayRegion,
+
+    RegisterNatives,
+    UnregisterNatives,
+
+    MonitorEnter,
+    MonitorExit,
+
+    GetJavaVM,
+
+    GetStringRegion,
+    GetStringUTFRegion,
+
+    GetPrimitiveArrayCritical,
+    ReleasePrimitiveArrayCritical,
+
+    GetStringCritical,
+    ReleaseStringCritical,
+
+    NewWeakGlobalRef,
+    DeleteWeakGlobalRef,
+
+    ExceptionCheck,
+
+    NewDirectByteBuffer,
+    GetDirectBufferAddress,
+    GetDirectBufferCapacity,
+
+    GetObjectRefType
+};
+
+static const struct JNIInvokeInterface gInvokeInterface = {
+    NULL,
+    NULL,
+    NULL,
+
+    DestroyJavaVM,
+    AttachCurrentThread,
+    DetachCurrentThread,
+
+    GetEnv,
+
+    AttachCurrentThreadAsDaemon,
+};
+
+/*
+ * ===========================================================================
+ *      VM/Env creation
+ * ===========================================================================
+ */
+
+/*
+ * Create a new JNIEnv struct and add it to the VM's list.
+ *
+ * "self" will be NULL for the main thread, since the VM hasn't started
+ * yet; the value will be filled in later.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self) {
+    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
+
+    //if (self != NULL)
+    //    ALOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
+
+    assert(vm != NULL);
+
+    JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
+    newEnv->funcTable = &gNativeInterface;
+    if (self != NULL) {
+        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
+        assert(newEnv->envThreadId != 0);
+    } else {
+        /* make it obvious if we fail to initialize these later */
+        newEnv->envThreadId = 0x77777775;
+        newEnv->self = (Thread*) 0x77777779;
+    }
+    if (gDvmJni.useCheckJni) {
+        dvmUseCheckedJniEnv(newEnv);
+    }
+
+    ScopedPthreadMutexLock lock(&vm->envListLock);
+
+    /* insert at head of list */
+    newEnv->next = vm->envList;
+    assert(newEnv->prev == NULL);
+    if (vm->envList == NULL) {
+        // rare, but possible
+        vm->envList = newEnv;
+    } else {
+        vm->envList->prev = newEnv;
+    }
+    vm->envList = newEnv;
+
+    //if (self != NULL)
+    //    ALOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
+    return (JNIEnv*) newEnv;
+}
+
+/*
+ * Remove a JNIEnv struct from the list and free it.
+ */
+void dvmDestroyJNIEnv(JNIEnv* env) {
+    if (env == NULL) {
+        return;
+    }
+
+    //ALOGI("Ent DestroyJNIEnv: threadid=%d %p", self->threadId, self);
+
+    JNIEnvExt* extEnv = (JNIEnvExt*) env;
+    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
+
+    ScopedPthreadMutexLock lock(&vm->envListLock);
+
+    if (extEnv == vm->envList) {
+        assert(extEnv->prev == NULL);
+        vm->envList = extEnv->next;
+    } else {
+        assert(extEnv->prev != NULL);
+        extEnv->prev->next = extEnv->next;
+    }
+    if (extEnv->next != NULL) {
+        extEnv->next->prev = extEnv->prev;
+    }
+
+    free(env);
+    //ALOGI("Xit DestroyJNIEnv: threadid=%d %p", self->threadId, self);
+}
+
+/*
+ * Enable "checked JNI" after the VM has partially started.  This must
+ * only be called in "zygote" mode, when we have one thread running.
+ *
+ * This doesn't attempt to rewrite the JNI call bridge associated with
+ * native methods, so we won't get those checks for any methods that have
+ * already been resolved.
+ */
+void dvmLateEnableCheckedJni() {
+    JNIEnvExt* extEnv = dvmGetJNIEnvForThread();
+    if (extEnv == NULL) {
+        ALOGE("dvmLateEnableCheckedJni: thread has no JNIEnv");
+        return;
+    }
+    JavaVMExt* extVm = (JavaVMExt*) gDvmJni.jniVm;
+    assert(extVm != NULL);
+
+    if (!gDvmJni.useCheckJni) {
+        ALOGD("Late-enabling CheckJNI");
+        dvmUseCheckedJniVm(extVm);
+        dvmUseCheckedJniEnv(extEnv);
+    } else {
+        ALOGD("Not late-enabling CheckJNI (already on)");
+    }
+}
+
+/*
+ * Not supported.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
+    return JNI_ERR;
+}
+
+/*
+ * Return a buffer full of created VMs.
+ *
+ * We always have zero or one.
+ */
+jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) {
+    if (gDvmJni.jniVm != NULL) {
+        *nVMs = 1;
+        if (bufLen > 0) {
+            *vmBuf++ = gDvmJni.jniVm;
+        }
+    } else {
+        *nVMs = 0;
+    }
+    return JNI_OK;
+}
+
+/*
+ * Create a new VM instance.
+ *
+ * The current thread becomes the main VM thread.  We return immediately,
+ * which effectively means the caller is executing in a native method.
+ */
+jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+    const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
+    if (dvmIsBadJniVersion(args->version)) {
+        ALOGE("Bad JNI version passed to CreateJavaVM: %d", args->version);
+        return JNI_EVERSION;
+    }
+
+    // TODO: don't allow creation of multiple VMs -- one per customer for now
+
+    /* zero globals; not strictly necessary the first time a VM is started */
+    memset(&gDvm, 0, sizeof(gDvm));
+
+    /*
+     * Set up structures for JNIEnv and VM.
+     */
+    JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
+    pVM->funcTable = &gInvokeInterface;
+    pVM->envList = NULL;
+    dvmInitMutex(&pVM->envListLock);
+
+    UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
+    memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
+
+    /*
+     * Convert JNI args to argv.
+     *
+     * We have to pull out vfprintf/exit/abort, because they use the
+     * "extraInfo" field to pass function pointer "hooks" in.  We also
+     * look for the -Xcheck:jni stuff here.
+     */
+    int argc = 0;
+    for (int i = 0; i < args->nOptions; i++) {
+        const char* optStr = args->options[i].optionString;
+        if (optStr == NULL) {
+            dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
+            return JNI_ERR;
+        } else if (strcmp(optStr, "vfprintf") == 0) {
+            gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "exit") == 0) {
+            gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
+        } else if (strcmp(optStr, "abort") == 0) {
+            gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "sensitiveThread") == 0) {
+            gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
+        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
+            gDvmJni.useCheckJni = true;
+        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
+            char* jniOpts = strdup(optStr + 10);
+            size_t jniOptCount = 1;
+            for (char* p = jniOpts; *p != 0; ++p) {
+                if (*p == ',') {
+                    ++jniOptCount;
+                    *p = 0;
+                }
+            }
+            char* jniOpt = jniOpts;
+            for (size_t i = 0; i < jniOptCount; ++i) {
+                if (strcmp(jniOpt, "warnonly") == 0) {
+                    gDvmJni.warnOnly = true;
+                } else if (strcmp(jniOpt, "forcecopy") == 0) {
+                    gDvmJni.forceCopy = true;
+                } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
+                    gDvmJni.logThirdPartyJni = true;
+                } else {
+                    dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
+                            jniOpt);
+                    free(pVM);
+                    free(jniOpts);
+                    return JNI_ERR;
+                }
+                jniOpt += strlen(jniOpt) + 1;
+            }
+            free(jniOpts);
+        } else {
+            /* regular option */
+            argv[argc++] = optStr;
+        }
+    }
+
+    if (gDvmJni.useCheckJni) {
+        dvmUseCheckedJniVm(pVM);
+    }
+
+    if (gDvmJni.jniVm != NULL) {
+        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
+        free(pVM);
+        return JNI_ERR;
+    }
+    gDvmJni.jniVm = (JavaVM*) pVM;
+
+    /*
+     * Create a JNIEnv for the main thread.  We need to have something set up
+     * here because some of the class initialization we do when starting
+     * up the VM will call into native code.
+     */
+    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
+
+    /* Initialize VM. */
+    gDvm.initializing = true;
+    std::string status =
+            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
+    gDvm.initializing = false;
+
+    if (!status.empty()) {
+        free(pEnv);
+        free(pVM);
+        ALOGW("CreateJavaVM failed: %s", status.c_str());
+        return JNI_ERR;
+    }
+
+    /*
+     * Success!  Return stuff to caller.
+     */
+    dvmChangeStatus(NULL, THREAD_NATIVE);
+    *p_env = (JNIEnv*) pEnv;
+    *p_vm = (JavaVM*) pVM;
+    ALOGV("CreateJavaVM succeeded");
+    return JNI_OK;
+}
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
new file mode 100644
index 0000000..094a640
--- /dev/null
+++ b/vm/JniInternal.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * JNI innards, common to the regular and "checked" interfaces.
+ */
+#ifndef DALVIK_JNIINTERNAL_H_
+#define DALVIK_JNIINTERNAL_H_
+
+#include "jni.h"
+
+/* system init/shutdown */
+bool dvmJniStartup(void);
+void dvmJniShutdown(void);
+
+bool dvmIsBadJniVersion(int version);
+
+/*
+ * Our data structures for JNIEnv and JavaVM.
+ *
+ * Native code thinks it has a pointer to a pointer.  We know better.
+ */
+struct JavaVMExt;
+
+struct JNIEnvExt {
+    const struct JNINativeInterface* funcTable;     /* must be first */
+
+    const struct JNINativeInterface* baseFuncTable;
+
+    u4      envThreadId;
+    Thread* self;
+
+    /* if nonzero, we are in a "critical" JNI call */
+    int     critical;
+
+    struct JNIEnvExt* prev;
+    struct JNIEnvExt* next;
+};
+
+struct JavaVMExt {
+    const struct JNIInvokeInterface* funcTable;     /* must be first */
+
+    const struct JNIInvokeInterface* baseFuncTable;
+
+    /* head of list of JNIEnvs associated with this VM */
+    JNIEnvExt*      envList;
+    pthread_mutex_t envListLock;
+};
+
+/*
+ * Native function return type; used by dvmPlatformInvoke().
+ *
+ * This is part of Method.jniArgInfo, and must fit in 3 bits.
+ * Note: Assembly code in arch/<arch>/Call<arch>.S relies on
+ * the enum values defined here.
+ */
+enum DalvikJniReturnType {
+    DALVIK_JNI_RETURN_VOID = 0,     /* must be zero */
+    DALVIK_JNI_RETURN_FLOAT = 1,
+    DALVIK_JNI_RETURN_DOUBLE = 2,
+    DALVIK_JNI_RETURN_S8 = 3,
+    DALVIK_JNI_RETURN_S4 = 4,
+    DALVIK_JNI_RETURN_S2 = 5,
+    DALVIK_JNI_RETURN_U2 = 6,
+    DALVIK_JNI_RETURN_S1 = 7
+};
+
+#define DALVIK_JNI_NO_ARG_INFO  0x80000000
+#define DALVIK_JNI_RETURN_MASK  0x70000000
+#define DALVIK_JNI_RETURN_SHIFT 28
+#define DALVIK_JNI_COUNT_MASK   0x0f000000
+#define DALVIK_JNI_COUNT_SHIFT  24
+
+
+/*
+ * Pop the JNI local stack when we return from a native method.  "saveArea"
+ * points to the StackSaveArea for the method we're leaving.
+ *
+ * (This may be implemented directly in assembly in mterp, so changes here
+ * may only affect the portable interpreter.)
+ */
+INLINE void dvmPopJniLocals(Thread* self, StackSaveArea* saveArea)
+{
+    self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie;
+}
+
+/*
+ * Set the envThreadId field.
+ */
+INLINE void dvmSetJniEnvThreadId(JNIEnv* pEnv, Thread* self)
+{
+    ((JNIEnvExt*)pEnv)->envThreadId = self->threadId;
+    ((JNIEnvExt*)pEnv)->self = self;
+}
+
+void dvmCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+
+/*
+ * Configure "method" to use the JNI bridge to call "func".
+ */
+void dvmUseJNIBridge(Method* method, void* func);
+
+
+/*
+ * Enable the "checked" versions.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv);
+void dvmUseCheckedJniVm(JavaVMExt* pVm);
+void dvmLateEnableCheckedJni(void);
+
+/*
+ * Decode a local, global, or weak-global reference.
+ */
+Object* dvmDecodeIndirectRef(Thread* self, jobject jobj);
+
+/*
+ * Verify that a reference passed in from native code is valid.  Returns
+ * an indication of local/global/invalid.
+ */
+jobjectRefType dvmGetJNIRefType(Thread* self, jobject jobj);
+
+/*
+ * Get the last method called on the interp stack.  This is the method
+ * "responsible" for calling into JNI.
+ */
+const Method* dvmGetCurrentJNIMethod(void);
+
+/*
+ * Create/destroy a JNIEnv for the current thread.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self);
+void dvmDestroyJNIEnv(JNIEnv* env);
+
+/*
+ * Find the JNIEnv associated with the current thread.
+ */
+JNIEnvExt* dvmGetJNIEnvForThread(void);
+
+/*
+ * Release all MonitorEnter-acquired locks that are still held.  Called at
+ * DetachCurrentThread time.
+ */
+void dvmReleaseJniMonitors(Thread* self);
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * The local ref tables associated with other threads are not included.
+ */
+void dvmDumpJniReferenceTables(void);
+
+// Dumps JNI statistics in response to SIGQUIT.
+struct DebugOutputTarget;
+void dvmDumpJniStats(DebugOutputTarget* target);
+
+#endif  // DALVIK_JNIINTERNAL_H_
diff --git a/vm/LinearAlloc.cpp b/vm/LinearAlloc.cpp
new file mode 100644
index 0000000..359893f
--- /dev/null
+++ b/vm/LinearAlloc.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Linear memory allocation, tied to class loaders.
+ */
+#include "Dalvik.h"
+
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
+
+//#define DISABLE_LINEAR_ALLOC
+
+// Use ashmem to name the LinearAlloc section
+#define USE_ASHMEM 1
+
+#ifdef USE_ASHMEM
+#include <cutils/ashmem.h>
+#endif /* USE_ASHMEM */
+
+/*
+Overview
+
+This is intended to be a simple, fast allocator for "write-once" storage.
+The expectation is that this will hold small allocations that don't change,
+such as parts of classes (vtables, fields, methods, interfaces).  Because
+the lifetime of these items is tied to classes, which in turn are tied
+to class loaders, we associate the storage with a ClassLoader object.
+
+[ We don't yet support class unloading, and our ClassLoader implementation
+is in flux, so for now we just have a single global region and the
+"classLoader" argument is ignored. ]
+
+By storing the data here, rather than on the system heap, we reduce heap
+clutter, speed class loading, reduce the memory footprint (reduced heap
+structure overhead), and most importantly we increase the number of pages
+that remain shared between processes launched in "Zygote mode".
+
+The 4 bytes preceding each block contain the block length.  This allows us
+to support "free" and "realloc" calls in a limited way.  We don't free
+storage once it has been allocated, but in some circumstances it could be
+useful to erase storage to garbage values after a "free" or "realloc".
+(Bad idea if we're trying to share pages.)  We need to align to 8-byte
+boundaries for some architectures, so we have a 50-50 chance of getting
+this for free in a given block.
+
+A NULL value for the "classLoader" argument refers to the bootstrap class
+loader, which is never unloaded (until the VM shuts down).
+
+Because the memory is not expected to be updated, we can use mprotect to
+guard the pages on debug builds.  Handy when tracking down corruption.
+*/
+
+/* alignment for allocations; must be power of 2, and currently >= hdr_xtra */
+#define BLOCK_ALIGN         8
+
+/* default length of memory segment (worst case is probably "dexopt") */
+#define DEFAULT_MAX_LENGTH  (16*1024*1024)
+
+/* leave enough space for a length word */
+#define HEADER_EXTRA        4
+
+/* overload the length word */
+#define LENGTHFLAG_FREE    0x80000000
+#define LENGTHFLAG_RW      0x40000000
+#define LENGTHFLAG_MASK    (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
+
+
+/* fwd */
+static void checkAllFree(Object* classLoader);
+
+
+/*
+ * Someday, retrieve the linear alloc struct associated with a particular
+ * class loader.  For now, always use the boostrap loader's instance.
+ */
+static inline LinearAllocHdr* getHeader(Object* classLoader)
+{
+    return gDvm.pBootLoaderAlloc;
+}
+
+/*
+ * Convert a pointer to memory to a pointer to the block header (which is
+ * currently just a length word).
+ */
+static inline u4* getBlockHeader(void* mem)
+{
+    return ((u4*) mem) -1;
+}
+
+/*
+ * Create a new linear allocation block.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return (LinearAllocHdr*) 0x12345;
+#endif
+    LinearAllocHdr* pHdr;
+
+    pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
+
+
+    /*
+     * "curOffset" points to the location of the next pre-block header,
+     * which means we have to advance to the next BLOCK_ALIGN address and
+     * back up.
+     *
+     * Note we leave the first page empty (see below), and start the
+     * first entry on the second page at an offset that ensures the next
+     * chunk of data will be properly aligned.
+     */
+    assert(BLOCK_ALIGN >= HEADER_EXTRA);
+    pHdr->curOffset = pHdr->firstOffset =
+        (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
+    pHdr->mapLength = DEFAULT_MAX_LENGTH;
+
+#ifdef USE_ASHMEM
+    int fd;
+
+    fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
+    if (fd < 0) {
+        ALOGE("ashmem LinearAlloc failed %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+
+    pHdr->mapAddr = (char*)mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE, fd, 0);
+    if (pHdr->mapAddr == MAP_FAILED) {
+        ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
+            strerror(errno));
+        free(pHdr);
+        close(fd);
+        return NULL;
+    }
+
+    close(fd);
+#else /*USE_ASHMEM*/
+    // MAP_ANON is listed as "deprecated" on Linux,
+    // but MAP_ANONYMOUS is not defined under Mac OS X.
+    pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (pHdr->mapAddr == MAP_FAILED) {
+        ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
+            strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+#endif /*USE_ASHMEM*/
+
+    /* region expected to begin on a page boundary */
+    assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
+
+    /* the system should initialize newly-mapped memory to zero */
+    assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
+
+    /*
+     * Disable access to all except starting page.  We will enable pages
+     * as we use them.  This helps prevent bad pointers from working.  The
+     * pages start out PROT_NONE, become read/write while we access them,
+     * then go to read-only after we finish our changes.
+     *
+     * We have to make the first page readable because we have 4 pad bytes,
+     * followed by 4 length bytes, giving an initial offset of 8.  The
+     * generic code below assumes that there could have been a previous
+     * allocation that wrote into those 4 pad bytes, therefore the page
+     * must have been marked readable by the previous allocation.
+     *
+     * We insert an extra page in here to force a break in the memory map
+     * so we can see ourselves more easily in "showmap".  Otherwise this
+     * stuff blends into the neighboring pages.  [TODO: do we still need
+     * the extra page now that we have ashmem?]
+     */
+    if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
+        ALOGW("LinearAlloc init mprotect failed: %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+    if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
+            ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
+    {
+        ALOGW("LinearAlloc init mprotect #2 failed: %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+
+    if (ENFORCE_READ_ONLY) {
+        /* allocate the per-page ref count */
+        int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+        pHdr->writeRefCount = (short*)calloc(numPages, sizeof(short));
+        if (pHdr->writeRefCount == NULL) {
+            free(pHdr);
+            return NULL;
+        }
+    }
+
+    dvmInitMutex(&pHdr->lock);
+
+    ALOGV("LinearAlloc: created region at %p-%p",
+        pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
+
+    return pHdr;
+}
+
+/*
+ * Destroy a linear allocation area.
+ *
+ * We do a trivial "has everything been freed?" check before unmapping the
+ * memory and freeing the LinearAllocHdr.
+ */
+void dvmLinearAllocDestroy(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    if (pHdr == NULL)
+        return;
+
+    checkAllFree(classLoader);
+
+    //dvmLinearAllocDump(classLoader);
+
+    if (gDvm.verboseShutdown) {
+        ALOGV("Unmapping linear allocator base=%p", pHdr->mapAddr);
+        ALOGD("LinearAlloc %p used %d of %d (%d%%)",
+            classLoader, pHdr->curOffset, pHdr->mapLength,
+            (pHdr->curOffset * 100) / pHdr->mapLength);
+    }
+
+    if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
+        ALOGW("LinearAlloc munmap(%p, %d) failed: %s",
+            pHdr->mapAddr, pHdr->mapLength, strerror(errno));
+    }
+    free(pHdr);
+}
+
+/*
+ * Allocate "size" bytes of storage, associated with a particular class
+ * loader.
+ *
+ * It's okay for size to be zero.
+ *
+ * We always leave "curOffset" pointing at the next place where we will
+ * store the header that precedes the returned storage.
+ *
+ * This aborts the VM on failure, so it's not necessary to check for a
+ * NULL return value.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size)
+{
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    int startOffset, nextOffset;
+    int lastGoodOff, firstWriteOff, lastWriteOff;
+
+#ifdef DISABLE_LINEAR_ALLOC
+    return calloc(1, size);
+#endif
+
+    LOGVV("--- LinearAlloc(%p, %d)", classLoader, size);
+
+    /*
+     * What we'd like to do is just determine the new end-of-alloc size
+     * and atomic-swap the updated value in.  The trouble is that, the
+     * first time we reach a new page, we need to call mprotect() to
+     * make the page available, and we don't want to call mprotect() on
+     * every allocation.  The troubled situation is:
+     *  - thread A allocs across a page boundary, but gets preempted
+     *    before mprotect() completes
+     *  - thread B allocs within the new page, and doesn't call mprotect()
+     */
+    dvmLockMutex(&pHdr->lock);
+
+    startOffset = pHdr->curOffset;
+    assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
+
+    /*
+     * Compute the new offset.  The old offset points at the address where
+     * we will store the hidden block header, so we advance past that,
+     * add the size of data they want, add another header's worth so we
+     * know we have room for that, and round up to BLOCK_ALIGN.  That's
+     * the next location where we'll put user data.  We then subtract the
+     * chunk header size off so we're back to the header pointer.
+     *
+     * Examples:
+     *   old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
+     *   old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
+     */
+    nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
+                    & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
+    LOGVV("--- old=%d size=%d new=%d", startOffset, size, nextOffset);
+
+    if (nextOffset > pHdr->mapLength) {
+        /*
+         * We don't have to abort here.  We could fall back on the system
+         * malloc(), and have our "free" call figure out what to do.  Only
+         * works if the users of these functions actually free everything
+         * they allocate.
+         */
+        ALOGE("LinearAlloc exceeded capacity (%d), last=%d",
+            pHdr->mapLength, (int) size);
+        dvmAbort();
+    }
+
+    /*
+     * Round up "size" to encompass the entire region, including the 0-7
+     * pad bytes before the next chunk header.  This way we get maximum
+     * utility out of "realloc", and when we're doing ENFORCE_READ_ONLY
+     * stuff we always treat the full extent.
+     */
+    size = nextOffset - (startOffset + HEADER_EXTRA);
+    LOGVV("--- (size now %d)", size);
+
+    /*
+     * See if we are starting on or have crossed into a new page.  If so,
+     * call mprotect on the page(s) we're about to write to.  We have to
+     * page-align the start address, but don't have to make the length a
+     * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
+     *
+     * Note that "startOffset" is not the last *allocated* byte, but rather
+     * the offset of the first *unallocated* byte (which we are about to
+     * write the chunk header to).  "nextOffset" is similar.
+     *
+     * If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
+     * we've written to this page before, because it might be read-only.
+     */
+    lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+    firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
+    lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+    LOGVV("---  lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x",
+        lastGoodOff, firstWriteOff, lastWriteOff);
+    if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
+        int cc, start, len;
+
+        start = firstWriteOff;
+        assert(start <= nextOffset);
+        len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
+
+        LOGVV("---    calling mprotect(start=%d len=%d RW)", start, len);
+        cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
+        if (cc != 0) {
+            ALOGE("LinearAlloc mprotect (+%d %d) failed: %s",
+                start, len, strerror(errno));
+            /* we're going to fail soon, might as do it now */
+            dvmAbort();
+        }
+    }
+
+    /* update the ref counts on the now-writable pages */
+    if (ENFORCE_READ_ONLY) {
+        int i, start, end;
+
+        start = firstWriteOff / SYSTEM_PAGE_SIZE;
+        end = lastWriteOff / SYSTEM_PAGE_SIZE;
+
+        LOGVV("---  marking pages %d-%d RW (alloc %d at %p)",
+            start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
+        for (i = start; i <= end; i++)
+            pHdr->writeRefCount[i]++;
+    }
+
+    /* stow the size in the header */
+    if (ENFORCE_READ_ONLY)
+        *(u4*)(pHdr->mapAddr + startOffset) = size | LENGTHFLAG_RW;
+    else
+        *(u4*)(pHdr->mapAddr + startOffset) = size;
+
+    /*
+     * Update data structure.
+     */
+    pHdr->curOffset = nextOffset;
+
+    dvmUnlockMutex(&pHdr->lock);
+    return pHdr->mapAddr + startOffset + HEADER_EXTRA;
+}
+
+/*
+ * Helper function, replaces strdup().
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return strdup(str);
+#endif
+    int len = strlen(str);
+    void* mem = dvmLinearAlloc(classLoader, len+1);
+    memcpy(mem, str, len+1);
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadOnly(classLoader, mem);
+    return (char*) mem;
+}
+
+/*
+ * "Reallocate" a piece of memory.
+ *
+ * If the new size is <= the old size, we return the original pointer
+ * without doing anything.
+ *
+ * If the new size is > the old size, we allocate new storage, copy the
+ * old stuff over, and mark the new stuff as free.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return realloc(mem, newSize);
+#endif
+    /* make sure we have the right region (and mem != NULL) */
+    assert(mem != NULL);
+    assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+           mem < (void*) (getHeader(classLoader)->mapAddr +
+                          getHeader(classLoader)->curOffset));
+
+    const u4* pLen = getBlockHeader(mem);
+    ALOGV("--- LinearRealloc(%d) old=%d", newSize, *pLen);
+
+    /* handle size reduction case */
+    if (*pLen >= newSize) {
+        if (ENFORCE_READ_ONLY)
+            dvmLinearSetReadWrite(classLoader, mem);
+        return mem;
+    }
+
+    void* newMem;
+
+    newMem = dvmLinearAlloc(classLoader, newSize);
+    assert(newMem != NULL);
+    memcpy(newMem, mem, *pLen);
+    dvmLinearFree(classLoader, mem);
+
+    return newMem;
+}
+
+
+/*
+ * Update the read/write status of one or more pages.
+ */
+static void updatePages(Object* classLoader, void* mem, int direction)
+{
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+    dvmLockMutex(&pHdr->lock);
+
+    /* make sure we have the right region */
+    assert(mem >= (void*) pHdr->mapAddr &&
+           mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
+
+    u4* pLen = getBlockHeader(mem);
+    u4 len = *pLen & LENGTHFLAG_MASK;
+    int firstPage, lastPage;
+
+    firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
+    lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
+    LOGVV("--- updating pages %d-%d (%d)", firstPage, lastPage, direction);
+
+    int i, cc;
+
+    /*
+     * Update individual pages.  We could do some sort of "lazy update" to
+     * combine mprotect calls, but that's almost certainly more trouble
+     * than it's worth.
+     */
+    for (i = firstPage; i <= lastPage; i++) {
+        if (direction < 0) {
+            /*
+             * Trying to mark read-only.
+             */
+            if (i == firstPage) {
+                if ((*pLen & LENGTHFLAG_RW) == 0) {
+                    ALOGW("Double RO on %p", mem);
+                    dvmAbort();
+                } else
+                    *pLen &= ~LENGTHFLAG_RW;
+            }
+
+            if (pHdr->writeRefCount[i] == 0) {
+                ALOGE("Can't make page %d any less writable", i);
+                dvmAbort();
+            }
+            pHdr->writeRefCount[i]--;
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RO", i);
+                cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+                        SYSTEM_PAGE_SIZE, PROT_READ);
+                assert(cc == 0);
+            }
+        } else {
+            /*
+             * Trying to mark writable.
+             */
+            if (pHdr->writeRefCount[i] >= 32767) {
+                ALOGE("Can't make page %d any more writable", i);
+                dvmAbort();
+            }
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RW", i);
+                cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+                        SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
+                assert(cc == 0);
+            }
+            pHdr->writeRefCount[i]++;
+
+            if (i == firstPage) {
+                if ((*pLen & LENGTHFLAG_RW) != 0) {
+                    ALOGW("Double RW on %p", mem);
+                    dvmAbort();
+                } else
+                    *pLen |= LENGTHFLAG_RW;
+            }
+        }
+    }
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Try to mark the pages in which a chunk of memory lives as read-only.
+ * Whether or not the pages actually change state depends on how many
+ * others are trying to access the same pages.
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    updatePages(classLoader, mem, -1);
+}
+
+/*
+ * Make the pages on which "mem" sits read-write.
+ *
+ * This covers the header as well as the data itself.  (We could add a
+ * "header-only" mode for dvmLinearFree.)
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadWrite(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    updatePages(classLoader, mem, 1);
+}
+
+/*
+ * Mark an allocation as free.
+ */
+void dvmLinearFree(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    free(mem);
+    return;
+#endif
+    if (mem == NULL)
+        return;
+
+    /* make sure we have the right region */
+    assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+           mem < (void*) (getHeader(classLoader)->mapAddr +
+                          getHeader(classLoader)->curOffset));
+
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadWrite(classLoader, mem);
+
+    u4* pLen = getBlockHeader(mem);
+    *pLen |= LENGTHFLAG_FREE;
+
+    if (ENFORCE_READ_ONLY)
+        dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * For debugging, dump the contents of a linear alloc area.
+ *
+ * We grab the lock so that the header contents and list output are
+ * consistent.
+ */
+void dvmLinearAllocDump(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+
+    dvmLockMutex(&pHdr->lock);
+
+    ALOGI("LinearAlloc classLoader=%p", classLoader);
+    ALOGI("  mapAddr=%p mapLength=%d firstOffset=%d",
+        pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
+    ALOGI("  curOffset=%d", pHdr->curOffset);
+
+    int off = pHdr->firstOffset;
+    u4 rawLen, fullLen;
+
+    while (off < pHdr->curOffset) {
+        rawLen = *(u4*) (pHdr->mapAddr + off);
+        fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+                    & ~(BLOCK_ALIGN-1));
+
+        ALOGI("  %p (%3d): %clen=%d%s", pHdr->mapAddr + off + HEADER_EXTRA,
+            (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
+            (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
+            rawLen & LENGTHFLAG_MASK,
+            (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
+
+        off += fullLen;
+    }
+
+    if (ENFORCE_READ_ONLY) {
+        ALOGI("writeRefCount map:");
+
+        int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+        int zstart = 0;
+        int i;
+
+        for (i = 0; i < numPages; i++) {
+            int count = pHdr->writeRefCount[i];
+
+            if (count != 0) {
+                if (zstart < i-1)
+                    printf(" %d-%d: zero\n", zstart, i-1);
+                else if (zstart == i-1)
+                    printf(" %d: zero\n", zstart);
+                zstart = i+1;
+                printf(" %d: %d\n", i, count);
+            }
+        }
+        if (zstart < i)
+            printf(" %d-%d: zero\n", zstart, i-1);
+    }
+
+    ALOGD("LinearAlloc %p using %d of %d (%d%%)",
+        classLoader, pHdr->curOffset, pHdr->mapLength,
+        (pHdr->curOffset * 100) / pHdr->mapLength);
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Verify that all blocks are freed.
+ *
+ * This should only be done as we're shutting down, but there could be a
+ * daemon thread that's still trying to do something, so we grab the locks.
+ */
+static void checkAllFree(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+    return;
+#endif
+    LinearAllocHdr* pHdr = getHeader(classLoader);
+
+    dvmLockMutex(&pHdr->lock);
+
+    int off = pHdr->firstOffset;
+    u4 rawLen, fullLen;
+
+    while (off < pHdr->curOffset) {
+        rawLen = *(u4*) (pHdr->mapAddr + off);
+        fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+                    & ~(BLOCK_ALIGN-1));
+
+        if ((rawLen & LENGTHFLAG_FREE) == 0) {
+            ALOGW("LinearAlloc %p not freed: %p len=%d", classLoader,
+                pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
+        }
+
+        off += fullLen;
+    }
+
+    dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc.  The full set of linear allocators is scanned.
+ *
+ * [ Since we currently only have one region, this is pretty simple.  In
+ * the future we'll need to traverse a table of class loaders. ]
+ */
+bool dvmLinearAllocContains(const void* start, size_t length)
+{
+    LinearAllocHdr* pHdr = getHeader(NULL);
+
+    if (pHdr == NULL)
+        return false;
+
+    return (char*) start >= pHdr->mapAddr &&
+           ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
+}
diff --git a/vm/LinearAlloc.h b/vm/LinearAlloc.h
new file mode 100644
index 0000000..743b7cc
--- /dev/null
+++ b/vm/LinearAlloc.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Simple linear memory allocator.
+ */
+#ifndef DALVIK_LINEARALLOC_H_
+#define DALVIK_LINEARALLOC_H_
+
+/*
+ * If this is set, we create additional data structures and make many
+ * additional mprotect() calls.
+ */
+#define ENFORCE_READ_ONLY   false
+
+/*
+ * Linear allocation state.  We could tuck this into the start of the
+ * allocated region, but that would prevent us from sharing the rest of
+ * that first page.
+ */
+struct LinearAllocHdr {
+    int     curOffset;          /* offset where next data goes */
+    pthread_mutex_t lock;       /* controls updates to this struct */
+
+    char*   mapAddr;            /* start of mmap()ed region */
+    int     mapLength;          /* length of region */
+    int     firstOffset;        /* for chasing through */
+
+    short*  writeRefCount;      /* for ENFORCE_READ_ONLY */
+};
+
+
+/*
+ * Create a new alloc region.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader);
+
+/*
+ * Destroy a region.
+ */
+void dvmLinearAllocDestroy(Object* classLoader);
+
+/*
+ * Allocate a chunk of memory.  The memory will be zeroed out.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size);
+
+/*
+ * Reallocate a chunk.  The original storage is not released, but may be
+ * erased to aid debugging.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result.  Also, the
+ * caller should probably mark the "mem" argument read-only before calling.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize);
+
+/* don't call these directly */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem);
+void dvmLinearSetReadWrite(Object* classLoader, void* mem);
+
+/*
+ * Mark a chunk of memory from Alloc or Realloc as read-only.  This must
+ * be done after all changes to the block of memory have been made.  This
+ * actually operates on a page granularity.
+ */
+INLINE void dvmLinearReadOnly(Object* classLoader, void* mem)
+{
+    if (ENFORCE_READ_ONLY && mem != NULL)
+        dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * Make a chunk of memory writable again.
+ */
+INLINE void dvmLinearReadWrite(Object* classLoader, void* mem)
+{
+    if (ENFORCE_READ_ONLY && mem != NULL)
+        dvmLinearSetReadWrite(classLoader, mem);
+}
+
+/*
+ * Free a chunk.  Does not increase available storage, but the freed area
+ * may be erased to aid debugging.
+ */
+void dvmLinearFree(Object* classLoader, void* mem);
+
+/*
+ * Helper function; allocates new storage and copies "str" into it.
+ *
+ * For ENFORCE_READ_ONLY, do *not* call dvmLinearReadOnly on the result.
+ * This is done automatically.
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str);
+
+/*
+ * Dump the contents of a linear alloc area.
+ */
+void dvmLinearAllocDump(Object* classLoader);
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc.  The full set of linear allocators is scanned.
+ */
+bool dvmLinearAllocContains(const void* start, size_t length);
+
+#endif  // DALVIK_LINEARALLOC_H_
diff --git a/vm/Misc.cpp b/vm/Misc.cpp
new file mode 100644
index 0000000..057b90d
--- /dev/null
+++ b/vm/Misc.cpp
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+/*
+ * Print a hex dump in this format:
+ *
+01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef\n
+ *
+ * If "mode" is kHexDumpLocal, we start at offset zero, and show a full
+ * 16 bytes on the first line.  If it's kHexDumpMem, we make this look
+ * like a memory dump, using the actual address, outputting a partial line
+ * if "vaddr" isn't aligned on a 16-byte boundary.
+ *
+ * "priority" and "tag" determine the values passed to the log calls.
+ *
+ * Does not use printf() or other string-formatting calls.
+ */
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+    size_t length, HexDumpMode mode)
+{
+    static const char gHexDigit[] = "0123456789abcdef";
+    const unsigned char* addr = (const unsigned char*)vaddr;
+    char out[77];           /* exact fit */
+    unsigned int offset;    /* offset to show while printing */
+    char* hex;
+    char* asc;
+    int gap;
+    //int trickle = 0;
+
+    if (mode == kHexDumpLocal)
+        offset = 0;
+    else
+        offset = (int) addr;
+
+    memset(out, ' ', sizeof(out)-1);
+    out[8] = ':';
+    out[sizeof(out)-2] = '\n';
+    out[sizeof(out)-1] = '\0';
+
+    gap = (int) offset & 0x0f;
+    while (length) {
+        unsigned int lineOffset = offset & ~0x0f;
+        int i, count;
+
+        hex = out;
+        asc = out + 59;
+
+        for (i = 0; i < 8; i++) {
+            *hex++ = gHexDigit[lineOffset >> 28];
+            lineOffset <<= 4;
+        }
+        hex++;
+        hex++;
+
+        count = ((int)length > 16-gap) ? 16-gap : (int)length; /* cap length */
+        assert(count != 0);
+        assert(count+gap <= 16);
+
+        if (gap) {
+            /* only on first line */
+            hex += gap * 3;
+            asc += gap;
+        }
+
+        for (i = gap ; i < count+gap; i++) {
+            *hex++ = gHexDigit[*addr >> 4];
+            *hex++ = gHexDigit[*addr & 0x0f];
+            hex++;
+            if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/)
+                *asc++ = *addr;
+            else
+                *asc++ = '.';
+            addr++;
+        }
+        for ( ; i < 16; i++) {
+            /* erase extra stuff; only happens on last line */
+            *hex++ = ' ';
+            *hex++ = ' ';
+            hex++;
+            *asc++ = ' ';
+        }
+
+        LOG_PRI(priority, tag, "%s", out);
+#if 0 //def HAVE_ANDROID_OS
+        /*
+         * We can overrun logcat easily by writing at full speed.  On the
+         * other hand, we can make Eclipse time out if we're showing
+         * packet dumps while debugging JDWP.
+         */
+        {
+            if (trickle++ == 8) {
+                trickle = 0;
+                usleep(20000);
+            }
+        }
+#endif
+
+        gap = 0;
+        length -= count;
+        offset += count;
+    }
+}
+
+
+/*
+ * Fill out a DebugOutputTarget, suitable for printing to the log.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+    const char* tag)
+{
+    assert(target != NULL);
+    assert(tag != NULL);
+
+    target->which = kDebugTargetLog;
+    target->data.log.priority = priority;
+    target->data.log.tag = tag;
+}
+
+/*
+ * Fill out a DebugOutputTarget suitable for printing to a file pointer.
+ */
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp)
+{
+    assert(target != NULL);
+    assert(fp != NULL);
+
+    target->which = kDebugTargetFile;
+    target->data.file.fp = fp;
+}
+
+/*
+ * Free "target" and any associated data.
+ */
+void dvmFreeOutputTarget(DebugOutputTarget* target)
+{
+    free(target);
+}
+
+/*
+ * Print a debug message, to either a file or the log.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+    ...)
+{
+    va_list args;
+
+    va_start(args, format);
+
+    switch (target->which) {
+    case kDebugTargetLog:
+        LOG_PRI_VA(target->data.log.priority, target->data.log.tag,
+            format, args);
+        break;
+    case kDebugTargetFile:
+        vfprintf(target->data.file.fp, format, args);
+        break;
+    default:
+        ALOGE("unexpected 'which' %d", target->which);
+        break;
+    }
+
+    va_end(args);
+}
+
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'.  If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str)
+{
+    char* newStr = strdup(str);
+    char* cp = newStr;
+
+    if (newStr == NULL)
+        return NULL;
+
+    while (*cp != '\0') {
+        if (*cp == '/') {
+            assert(false);
+            return NULL;
+        }
+        if (*cp == '.')
+            *cp = '/';
+        cp++;
+    }
+
+    return newStr;
+}
+
+std::string dvmHumanReadableDescriptor(const char* descriptor) {
+    // Count the number of '['s to get the dimensionality.
+    const char* c = descriptor;
+    size_t dim = 0;
+    while (*c == '[') {
+        dim++;
+        c++;
+    }
+
+    // Reference or primitive?
+    if (*c == 'L') {
+        // "[[La/b/C;" -> "a.b.C[][]".
+        c++; // Skip the 'L'.
+    } else {
+        // "[[B" -> "byte[][]".
+        // To make life easier, we make primitives look like unqualified
+        // reference types.
+        switch (*c) {
+        case 'B': c = "byte;"; break;
+        case 'C': c = "char;"; break;
+        case 'D': c = "double;"; break;
+        case 'F': c = "float;"; break;
+        case 'I': c = "int;"; break;
+        case 'J': c = "long;"; break;
+        case 'S': c = "short;"; break;
+        case 'Z': c = "boolean;"; break;
+        default: return descriptor;
+        }
+    }
+
+    // At this point, 'c' is a string of the form "fully/qualified/Type;"
+    // or "primitive;". Rewrite the type with '.' instead of '/':
+    std::string result;
+    const char* p = c;
+    while (*p != ';') {
+        char ch = *p++;
+        if (ch == '/') {
+          ch = '.';
+        }
+        result.push_back(ch);
+    }
+    // ...and replace the semicolon with 'dim' "[]" pairs:
+    while (dim--) {
+        result += "[]";
+    }
+    return result;
+}
+
+std::string dvmHumanReadableType(const Object* obj)
+{
+    if (obj == NULL) {
+        return "null";
+    }
+    if (obj->clazz == NULL) {
+        /* should only be possible right after a plain dvmMalloc() */
+        return "(raw)";
+    }
+    std::string result(dvmHumanReadableDescriptor(obj->clazz->descriptor));
+    if (dvmIsClassObject(obj)) {
+        const ClassObject* clazz = reinterpret_cast<const ClassObject*>(obj);
+        result += "<" + dvmHumanReadableDescriptor(clazz->descriptor) + ">";
+    }
+    return result;
+}
+
+std::string dvmHumanReadableField(const Field* field)
+{
+    if (field == NULL) {
+        return "(null)";
+    }
+    std::string result(dvmHumanReadableDescriptor(field->clazz->descriptor));
+    result += '.';
+    result += field->name;
+    return result;
+}
+
+std::string dvmHumanReadableMethod(const Method* method, bool withSignature)
+{
+    if (method == NULL) {
+        return "(null)";
+    }
+    std::string result(dvmHumanReadableDescriptor(method->clazz->descriptor));
+    result += '.';
+    result += method->name;
+    if (withSignature) {
+        // TODO: the types in this aren't human readable!
+        char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+        result += signature;
+        free(signature);
+    }
+    return result;
+}
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ * See also dvmHumanReadableDescriptor.
+ */
+char* dvmDescriptorToDot(const char* str)
+{
+    size_t at = strlen(str);
+    char* newStr;
+
+    if ((at >= 2) && (str[0] == 'L') && (str[at - 1] == ';')) {
+        at -= 2; /* Two fewer chars to copy. */
+        str++; /* Skip the 'L'. */
+    }
+
+    newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
+    if (newStr == NULL)
+        return NULL;
+
+    newStr[at] = '\0';
+
+    while (at > 0) {
+        at--;
+        newStr[at] = (str[at] == '/') ? '.' : str[at];
+    }
+
+    return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounded by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ */
+char* dvmDotToDescriptor(const char* str)
+{
+    size_t length = strlen(str);
+    int wrapElSemi = 0;
+    char* newStr;
+    char* at;
+
+    if (str[0] != '[') {
+        length += 2; /* for "L" and ";" */
+        wrapElSemi = 1;
+    }
+
+    newStr = at = (char*)malloc(length + 1); /* + 1 for the '\0' */
+
+    if (newStr == NULL) {
+        return NULL;
+    }
+
+    if (wrapElSemi) {
+        *(at++) = 'L';
+    }
+
+    while (*str) {
+        char c = *(str++);
+        if (c == '.') {
+            c = '/';
+        }
+        *(at++) = c;
+    }
+
+    if (wrapElSemi) {
+        *(at++) = ';';
+    }
+
+    *at = '\0';
+    return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str)
+{
+    if (str[0] == 'L') {
+        size_t length = strlen(str) - 1;
+        char* newStr = (char*)malloc(length);
+
+        if (newStr == NULL) {
+            return NULL;
+        }
+
+        strlcpy(newStr, str + 1, length);
+        return newStr;
+    }
+
+    return strdup(str);
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str)
+{
+    if (str[0] != '[') {
+        size_t length = strlen(str);
+        char* descriptor = (char*)malloc(length + 3);
+
+        if (descriptor == NULL) {
+            return NULL;
+        }
+
+        descriptor[0] = 'L';
+        strcpy(descriptor + 1, str);
+        descriptor[length + 1] = ';';
+        descriptor[length + 2] = '\0';
+
+        return descriptor;
+    }
+
+    return strdup(str);
+}
+
+/*
+ * Get a notion of the current time, in nanoseconds.  This is meant for
+ * computing durations (e.g. "operation X took 52nsec"), so the result
+ * should not be used to get the current date/time.
+ */
+u8 dvmGetRelativeTimeNsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return (u8)now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds.
+ *
+ * Only useful for time deltas.
+ */
+u8 dvmGetThreadCpuTimeNsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    return (u8) -1;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds, for the specified thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread)
+{
+#if 0 /*def HAVE_POSIX_CLOCKS*/
+    int clockId;
+
+    if (pthread_getcpuclockid(thread, &clockId) != 0)
+        return (u8) -1;
+
+    struct timespec now;
+    clock_gettime(clockId, &now);
+    return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    return (u8) -1;
+#endif
+}
+
+
+/*
+ * Call this repeatedly, with successively higher values for "iteration",
+ * to sleep for a period of time not to exceed "maxTotalSleep".
+ *
+ * For example, when called with iteration==0 we will sleep for a very
+ * brief time.  On the next call we will sleep for a longer time.  When
+ * the sum total of all sleeps reaches "maxTotalSleep", this returns false.
+ *
+ * The initial start time value for "relStartTime" MUST come from the
+ * dvmGetRelativeTimeUsec call.  On the device this must come from the
+ * monotonic clock source, not the wall clock.
+ *
+ * This should be used wherever you might be tempted to call sched_yield()
+ * in a loop.  The problem with sched_yield is that, for a high-priority
+ * thread, the kernel might not actually transfer control elsewhere.
+ *
+ * Returns "false" if we were unable to sleep because our time was up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime)
+{
+    /*
+     * Minimum sleep is one millisecond, it is important to keep this value
+     * low to ensure short GC pauses since dvmSuspendAllThreads() uses this
+     * function.
+     */
+    const int minSleep = 1000;
+    u8 curTime;
+    int curDelay;
+
+    /*
+     * Get current time, and see if we've already exceeded the limit.
+     */
+    curTime = dvmGetRelativeTimeUsec();
+    if (curTime >= relStartTime + maxTotalSleep) {
+        LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)",
+            relStartTime, maxTotalSleep, curTime);
+        return false;
+    }
+
+    /*
+     * Compute current delay.  We're bounded by "maxTotalSleep", so no
+     * real risk of overflow assuming "usleep" isn't returning early.
+     * (Besides, 2^30 usec is about 18 minutes by itself.)
+     *
+     * For iteration==0 we just call sched_yield(), so the first sleep
+     * at iteration==1 is actually (minSleep * 2).
+     */
+    curDelay = minSleep;
+    while (iteration-- > 0)
+        curDelay *= 2;
+    assert(curDelay > 0);
+
+    if (curTime + curDelay >= relStartTime + maxTotalSleep) {
+        LOGVV("exsl: reduced delay from %d to %d",
+            curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
+        curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
+    }
+
+    if (iteration == 0) {
+        LOGVV("exsl: yield");
+        sched_yield();
+    } else {
+        LOGVV("exsl: sleep for %d", curDelay);
+        usleep(curDelay);
+    }
+    return true;
+}
+
+
+/*
+ * Set the "close on exec" flag so we don't expose our file descriptors
+ * to processes launched by us.
+ */
+bool dvmSetCloseOnExec(int fd)
+{
+    int flags;
+
+    /*
+     * There's presently only one flag defined, so getting the previous
+     * value of the fd flags is probably unnecessary.
+     */
+    flags = fcntl(fd, F_GETFD);
+    if (flags < 0) {
+        ALOGW("Unable to get fd flags for fd %d", fd);
+        return false;
+    }
+    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+        ALOGW("Unable to set close-on-exec for fd %d", fd);
+        return false;
+    }
+    return true;
+}
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size) {
+    size_t srcLength = strlen(src);
+    size_t copyLength = srcLength;
+
+    if (srcLength > (size - 1)) {
+        copyLength = size - 1;
+    }
+
+    if (size != 0) {
+        strncpy(dst, src, copyLength);
+        dst[copyLength] = '\0';
+    }
+
+    return srcLength;
+}
+#endif
+
+/*
+ *  Allocates a memory region using ashmem and mmap, initialized to
+ *  zero.  Actual allocation rounded up to page multiple.  Returns
+ *  NULL on failure.
+ */
+void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
+    void *base;
+    int fd, ret;
+
+    byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
+    fd = ashmem_create_region(name, byteCount);
+    if (fd == -1) {
+        return NULL;
+    }
+    base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
+    ret = close(fd);
+    if (base == MAP_FAILED) {
+        return NULL;
+    }
+    if (ret == -1) {
+        munmap(base, byteCount);
+        return NULL;
+    }
+    return base;
+}
+
+/*
+ * Get some per-thread stats.
+ *
+ * This is currently generated by opening the appropriate "stat" file
+ * in /proc and reading the pile of stuff that comes out.
+ */
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
+{
+    /*
+    int pid;
+    char comm[128];
+    char state;
+    int ppid, pgrp, session, tty_nr, tpgid;
+    unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
+    long cutime, cstime, priority, nice, zero, itrealvalue;
+    unsigned long starttime, vsize;
+    long rss;
+    unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
+    unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
+    int exit_signal, processor;
+    unsigned long rt_priority, policy;
+
+    scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
+          "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
+          "%lu %lu %lu %d %d %lu %lu",
+        &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
+        &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
+        &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
+        &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
+        &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
+        &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
+        &rt_priority, &policy);
+
+        (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
+    */
+
+    char nameBuf[64];
+    int i, fd;
+
+    /*
+     * Open and read the appropriate file.  This is expected to work on
+     * Linux but will fail on other platforms (e.g. Mac sim).
+     */
+    sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
+    fd = open(nameBuf, O_RDONLY);
+    if (fd < 0) {
+        ALOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
+        return false;
+    }
+
+    char lineBuf[512];      /* > 2x typical */
+    int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
+    if (cc <= 0) {
+        const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
+        ALOGI("Unable to read '%s': %s", nameBuf, msg);
+        close(fd);
+        return false;
+    }
+    close(fd);
+    lineBuf[cc] = '\0';
+
+    /*
+     * Skip whitespace-separated tokens.  For the most part we can assume
+     * that tokens do not contain spaces, and are separated by exactly one
+     * space character.  The only exception is the second field ("comm")
+     * which may contain spaces but is surrounded by parenthesis.
+     */
+    char* cp = strchr(lineBuf, ')');
+    if (cp == NULL)
+        goto parse_fail;
+    cp += 2;
+    pData->state = *cp++;
+
+    for (i = 3; i < 13; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab utime/stime.
+     */
+    char* endp;
+    pData->utime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
+
+    cp = strchr(cp+1, ' ');
+    if (cp == NULL)
+        goto parse_fail;
+
+    pData->stime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
+
+    /*
+     * Skip more stuff we don't care about.
+     */
+    for (i = 14; i < 38; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab processor number.
+     */
+    pData->processor = strtol(cp+1, &endp, 10);
+    if (endp == cp+1)
+        ALOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
+
+    return true;
+
+parse_fail:
+    ALOGI("stat parse failed (%s)", lineBuf);
+    return false;
+}
+
+/* documented in header file */
+const char* dvmPathToAbsolutePortion(const char* path) {
+    if (path == NULL) {
+        return NULL;
+    }
+
+    if (path[0] == '/') {
+        /* It's a regular absolute path. Return it. */
+        return path;
+    }
+
+    const char* sentinel = strstr(path, "/./");
+
+    if (sentinel != NULL) {
+        /* It's got the sentinel. Return a pointer to the second slash. */
+        return sentinel + 2;
+    }
+
+    return NULL;
+}
+
+// From RE2.
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+    // First try with a small fixed size buffer
+    char space[1024];
+
+    // It's possible for methods that use a va_list to invalidate
+    // the data in it upon use.  The fix is to make a copy
+    // of the structure before using it and use that copy instead.
+    va_list backup_ap;
+    va_copy(backup_ap, ap);
+    int result = vsnprintf(space, sizeof(space), format, backup_ap);
+    va_end(backup_ap);
+
+    if ((result >= 0) && ((size_t) result < sizeof(space))) {
+        // It fit
+        dst->append(space, result);
+        return;
+    }
+
+    // Repeatedly increase buffer size until it fits
+    int length = sizeof(space);
+    while (true) {
+        if (result < 0) {
+            // Older behavior: just try doubling the buffer size
+            length *= 2;
+        } else {
+            // We need exactly "result+1" characters
+            length = result+1;
+        }
+        char* buf = new char[length];
+
+        // Restore the va_list before we use it again
+        va_copy(backup_ap, ap);
+        result = vsnprintf(buf, length, format, backup_ap);
+        va_end(backup_ap);
+
+        if ((result >= 0) && (result < length)) {
+            // It fit
+            dst->append(buf, result);
+            delete[] buf;
+            return;
+        }
+        delete[] buf;
+    }
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    std::string result;
+    StringAppendV(&result, fmt, ap);
+    va_end(ap);
+    return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    StringAppendV(dst, format, ap);
+    va_end(ap);
+}
diff --git a/vm/Misc.h b/vm/Misc.h
new file mode 100644
index 0000000..4c42576
--- /dev/null
+++ b/vm/Misc.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef DALVIK_MISC_H_
+#define DALVIK_MISC_H_
+
+#include <string>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "Inlines.h"
+
+/*
+ * Used to shut up the compiler when a parameter isn't used.
+ */
+#define UNUSED_PARAMETER(p)     (void)(p)
+
+/*
+ * Floating point conversion functions.  These are necessary to avoid
+ * strict-aliasing problems ("dereferencing type-punned pointer will break
+ * strict-aliasing rules").  According to the gcc info page, this usage
+ * is allowed, even with "-fstrict-aliasing".
+ *
+ * The code generated by gcc-4.1.1 appears to be much better than a
+ * type cast dereference ("int foo = *(int*)&myfloat") when the conversion
+ * function is inlined.  It also allows us to take advantage of the
+ * optimizations that strict aliasing rules allow.
+ */
+INLINE float dvmU4ToFloat(u4 val) {
+    union { u4 in; float out; } conv;
+    conv.in = val;
+    return conv.out;
+}
+INLINE u4 dvmFloatToU4(float val) {
+    union { float in; u4 out; } conv;
+    conv.in = val;
+    return conv.out;
+}
+
+/*
+ * Print a hex dump to the log file.
+ *
+ * "local" mode prints a hex dump starting from offset 0 (roughly equivalent
+ * to "xxd -g1").
+ *
+ * "mem" mode shows the actual memory address, and will offset the start
+ * so that the low nibble of the address is always zero.
+ *
+ * If "tag" is NULL the default tag ("dalvikvm") will be used.
+ */
+enum HexDumpMode { kHexDumpLocal, kHexDumpMem };
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+    size_t length, HexDumpMode mode);
+
+/*
+ * Print a hex dump, at INFO level.
+ */
+INLINE void dvmPrintHexDump(const void* vaddr, size_t length) {
+    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+        vaddr, length, kHexDumpLocal);
+}
+
+/*
+ * Print a hex dump at VERBOSE level. This does nothing in non-debug builds.
+ */
+INLINE void dvmPrintHexDumpDbg(const void* vaddr, size_t length,const char* tag)
+{
+#if !LOG_NDEBUG
+    dvmPrintHexDumpEx(ANDROID_LOG_VERBOSE, (tag != NULL) ? tag : LOG_TAG,
+        vaddr, length, kHexDumpLocal);
+#endif
+}
+
+enum DebugTargetKind {
+    kDebugTargetUnknown = 0,
+    kDebugTargetLog,
+    kDebugTargetFile,
+};
+
+/*
+ * We pass one of these around when we want code to be able to write debug
+ * info to either the log or to a file (or stdout/stderr).
+ */
+struct DebugOutputTarget {
+    /* where to? */
+    DebugTargetKind which;
+
+    /* additional bits */
+    union {
+        struct {
+            int priority;
+            const char* tag;
+        } log;
+        struct {
+            FILE* fp;
+        } file;
+    } data;
+};
+
+/*
+ * Fill in a DebugOutputTarget struct.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+    const char* tag);
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp);
+
+/*
+ * Print a debug message.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+    ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'.  If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str);
+
+/*
+ * Return a newly-allocated string containing a human-readable equivalent
+ * of 'descriptor'. So "I" would be "int", "[[I" would be "int[][]",
+ * "[Ljava/lang/String;" would be "java.lang.String[]", and so forth.
+ */
+std::string dvmHumanReadableDescriptor(const char* descriptor);
+
+/**
+ * Returns a human-readable string form of the name of the class of
+ * the given object. So given a java.lang.String, the output would
+ * be "java.lang.String". Given an array of int, the output would be "int[]".
+ * Given String.class, the output would be "java.lang.Class<java.lang.String>".
+ */
+std::string dvmHumanReadableType(const Object* obj);
+
+/**
+ * Returns a human-readable string of the form "package.Class.fieldName".
+ */
+struct Field;
+std::string dvmHumanReadableField(const Field* field);
+
+/**
+ * Returns a human-readable string of the form "package.Class.methodName"
+ * or "package.Class.methodName(Ljava/lang/String;I)V".
+ */
+struct Method;
+std::string dvmHumanReadableMethod(const Method* method, bool withSignature);
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ * See also dvmHumanReadableDescriptor.
+ */
+char* dvmDescriptorToDot(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounded by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ *
+ * "Dot version" names are used in the class loading machinery.
+ */
+char* dvmDotToDescriptor(const char* str);
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str);
+
+/*
+ * Get the current time, in nanoseconds.  This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+u8 dvmGetRelativeTimeNsec(void);
+
+/*
+ * Get the current time, in microseconds.  This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+INLINE u8 dvmGetRelativeTimeUsec(void) {
+    return dvmGetRelativeTimeNsec() / 1000;
+}
+
+/*
+ * Get the current time, in milliseconds.  This is "relative" time,
+ * meaning it could be wall-clock time or a monotonic counter, and is
+ * only suitable for computing time deltas.  The value returned from
+ * this function is a u4 and should only be used for debugging
+ * messages.  TODO: make this value relative to the start-up time of
+ * the VM.
+ */
+INLINE u4 dvmGetRelativeTimeMsec(void) {
+    return (u4)(dvmGetRelativeTimeUsec() / 1000);
+}
+
+/*
+ * Get the current per-thread CPU time.  This clock increases monotonically
+ * when the thread is running, but not when it's sleeping or blocked on a
+ * synchronization object.
+ *
+ * The absolute value of the clock may not be useful, so this should only
+ * be used for time deltas.
+ *
+ * If the thread CPU clock is not available, this always returns (u8)-1.
+ */
+u8 dvmGetThreadCpuTimeNsec(void);
+
+/*
+ * Per-thread CPU time, in micros.
+ */
+INLINE u8 dvmGetThreadCpuTimeUsec(void) {
+    return dvmGetThreadCpuTimeNsec() / 1000;
+}
+
+/*
+ * Like dvmGetThreadCpuTimeNsec, but for a different thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread);
+INLINE u8 dvmGetOtherThreadCpuTimeUsec(pthread_t thread) {
+    return dvmGetOtherThreadCpuTimeNsec(thread) / 1000;
+}
+
+/*
+ * Sleep for increasingly longer periods, until "maxTotalSleep" microseconds
+ * have elapsed.  Pass in the start time, which must be a value returned by
+ * dvmGetRelativeTimeUsec().
+ *
+ * Returns "false" if we were unable to sleep because our time is up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime);
+
+/*
+ * Set the "close on exec" flag on a file descriptor.
+ */
+bool dvmSetCloseOnExec(int fd);
+
+/*
+ * Unconditionally abort the entire VM.  Try not to use this.
+ *
+ * NOTE: if this is marked ((noreturn)), gcc will merge multiple dvmAbort()
+ * calls in a single function together.  This is good, in that it reduces
+ * code size slightly, but also bad, because the native stack trace we
+ * get from the abort may point at the wrong call site.  Best to leave
+ * it undecorated.
+ */
+extern "C" void dvmAbort(void);
+void dvmPrintNativeBackTrace(void);
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+extern "C" size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+/*
+ *  Allocates a memory region using ashmem and mmap, initialized to
+ *  zero.  Actual allocation rounded up to page multiple.  Returns
+ *  NULL on failure.
+ */
+void *dvmAllocRegion(size_t size, int prot, const char *name);
+
+/*
+ * Get some per-thread stats from /proc/self/task/N/stat.
+ */
+struct ProcStatData {
+    char state;             /* process state, e.g. 'R', 'S', 'D' */
+    unsigned long utime;    /* number of jiffies scheduled in user mode */
+    unsigned long stime;    /* number of jiffies scheduled in kernel mode */
+    int processor;          /* number of CPU that last executed thread */
+};
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid);
+
+/*
+ * Returns the pointer to the "absolute path" part of the given path
+ * string, treating first (if any) instance of "/./" as a sentinel
+ * indicating the start of the absolute path. If the path isn't absolute
+ * in the usual way (i.e., starts with "/") and doesn't have the sentinel,
+ * then this returns NULL.
+ *
+ * For example:
+ *     "/foo/bar/baz" returns "/foo/bar/baz"
+ *     "foo/./bar/baz" returns "/bar/baz"
+ *     "foo/bar/baz" returns NULL
+ *
+ * The sentinel is used specifically to aid in cross-optimization, where
+ * a host is processing dex files in a build tree, and where we don't want
+ * the build tree's directory structure to be baked into the output (such
+ * as, for example, in the dependency paths of optimized dex files).
+ */
+const char* dvmPathToAbsolutePortion(const char* path);
+
+/**
+ * Returns a string corresponding to printf-like formatting of the arguments.
+ */
+std::string StringPrintf(const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 1, 2)));
+
+/**
+ * Appends a printf-like formatting of the arguments to 'dst'.
+ */
+void StringAppendF(std::string* dst, const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 2, 3)));
+
+/**
+ * Appends a printf-like formatting of the arguments to 'dst'.
+ */
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+
+#endif  // DALVIK_MISC_H_
diff --git a/vm/Native.cpp b/vm/Native.cpp
new file mode 100644
index 0000000..a12c4e0
--- /dev/null
+++ b/vm/Native.cpp
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Native method resolution.
+ *
+ * Currently the "Dalvik native" methods are only used for internal methods.
+ * Someday we may want to export the interface as a faster but riskier
+ * alternative to JNI.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+
+static void freeSharedLibEntry(void* ptr);
+static void* lookupSharedLibMethod(const Method* method);
+
+
+/*
+ * Initialize the native code loader.
+ */
+bool dvmNativeStartup()
+{
+    gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
+    if (gDvm.nativeLibs == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free up our tables.
+ */
+void dvmNativeShutdown()
+{
+    dvmHashTableFree(gDvm.nativeLibs);
+    gDvm.nativeLibs = NULL;
+}
+
+
+/*
+ * Resolve a native method and invoke it.
+ *
+ * This is executed as if it were a native bridge or function.  If the
+ * resolution succeeds, method->insns is replaced, and we don't go through
+ * here again unless the method is unregistered.
+ *
+ * Initializes method's class if necessary.
+ *
+ * An exception is thrown on resolution failure.
+ *
+ * (This should not be taking "const Method*", because it modifies the
+ * structure, but the declaration needs to match the DalvikBridgeFunc
+ * type definition.)
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    ClassObject* clazz = method->clazz;
+
+    /*
+     * If this is a static method, it could be called before the class
+     * has been initialized.
+     */
+    if (dvmIsStaticMethod(method)) {
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return;
+        }
+    } else {
+        assert(dvmIsClassInitialized(clazz) ||
+               dvmIsClassInitializing(clazz));
+    }
+
+    /* start with our internal-native methods */
+    DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
+    if (infunc != NULL) {
+        /* resolution always gets the same answer, so no race here */
+        IF_LOGVV() {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            LOGVV("+++ resolved native %s.%s %s, invoking",
+                clazz->descriptor, method->name, desc);
+            free(desc);
+        }
+        if (dvmIsSynchronizedMethod(method)) {
+            ALOGE("ERROR: internal-native can't be declared 'synchronized'");
+            ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
+            dvmAbort();     // harsh, but this is VM-internal problem
+        }
+        DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
+        dvmSetNativeFunc((Method*) method, dfunc, NULL);
+        dfunc(args, pResult, method, self);
+        return;
+    }
+
+    /* now scan any DLLs we have loaded for JNI signatures */
+    void* func = lookupSharedLibMethod(method);
+    if (func != NULL) {
+        /* found it, point it at the JNI bridge and then call it */
+        dvmUseJNIBridge((Method*) method, func);
+        (*method->nativeFunc)(args, pResult, method, self);
+        return;
+    }
+
+    IF_ALOGW() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGW("No implementation found for native %s.%s:%s",
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    dvmThrowUnsatisfiedLinkError("Native method not found", method);
+}
+
+
+/*
+ * ===========================================================================
+ *      Native shared library support
+ * ===========================================================================
+ */
+
+// TODO? if a ClassLoader is unloaded, we need to unload all DLLs that
+// are associated with it.  (Or not -- can't determine if native code
+// is still using parts of it.)
+
+enum OnLoadState {
+    kOnLoadPending = 0,     /* initial state, must be zero */
+    kOnLoadFailed,
+    kOnLoadOkay,
+};
+
+/*
+ * We add one of these to the hash table for every library we load.  The
+ * hash is on the "pathName" field.
+ */
+struct SharedLib {
+    char*       pathName;           /* absolute path to library */
+    void*       handle;             /* from dlopen */
+    Object*     classLoader;        /* ClassLoader we are associated with */
+
+    pthread_mutex_t onLoadLock;     /* guards remaining items */
+    pthread_cond_t  onLoadCond;     /* wait for JNI_OnLoad in other thread */
+    u4              onLoadThreadId; /* recursive invocation guard */
+    OnLoadState     onLoadResult;   /* result of earlier JNI_OnLoad */
+};
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the string.
+ */
+static int hashcmpNameStr(const void* ventry, const void* vname)
+{
+    const SharedLib* pLib = (const SharedLib*) ventry;
+    const char* name = (const char*) vname;
+
+    return strcmp(pLib->pathName, name);
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the new entry.
+ *
+ * We don't compare the class loader here, because you're not allowed to
+ * have the same shared library associated with more than one CL.
+ */
+static int hashcmpSharedLib(const void* ventry, const void* vnewEntry)
+{
+    const SharedLib* pLib = (const SharedLib*) ventry;
+    const SharedLib* pNewLib = (const SharedLib*) vnewEntry;
+
+    ALOGD("--- comparing %p '%s' %p '%s'",
+        pLib, pLib->pathName, pNewLib, pNewLib->pathName);
+    return strcmp(pLib->pathName, pNewLib->pathName);
+}
+
+/*
+ * Check to see if an entry with the same pathname already exists.
+ */
+static SharedLib* findSharedLibEntry(const char* pathName)
+{
+    u4 hash = dvmComputeUtf8Hash(pathName);
+    void* ent;
+
+    ent = dvmHashTableLookup(gDvm.nativeLibs, hash, (void*)pathName,
+                hashcmpNameStr, false);
+    return (SharedLib*)ent;
+}
+
+/*
+ * Add the new entry to the table.
+ *
+ * Returns the table entry, which will not be the same as "pLib" if the
+ * entry already exists.
+ */
+static SharedLib* addSharedLibEntry(SharedLib* pLib)
+{
+    u4 hash = dvmComputeUtf8Hash(pLib->pathName);
+
+    /*
+     * Do the lookup with the "add" flag set.  If we add it, we will get
+     * our own pointer back.  If somebody beat us to the punch, we'll get
+     * their pointer back instead.
+     */
+    return (SharedLib*)dvmHashTableLookup(gDvm.nativeLibs, hash, pLib,
+                hashcmpSharedLib, true);
+}
+
+/*
+ * Free up an entry.  (This is a dvmHashTableFree callback.)
+ */
+static void freeSharedLibEntry(void* ptr)
+{
+    SharedLib* pLib = (SharedLib*) ptr;
+
+    /*
+     * Calling dlclose() here is somewhat dangerous, because it's possible
+     * that a thread outside the VM is still accessing the code we loaded.
+     */
+    if (false)
+        dlclose(pLib->handle);
+    free(pLib->pathName);
+    free(pLib);
+}
+
+/*
+ * Convert library name to system-dependent form, e.g. "jpeg" becomes
+ * "libjpeg.so".
+ *
+ * (Should we have this take buffer+len and avoid the alloc?  It gets
+ * called very rarely.)
+ */
+char* dvmCreateSystemLibraryName(char* libName)
+{
+    char buf[256];
+    int len;
+
+    len = snprintf(buf, sizeof(buf), OS_SHARED_LIB_FORMAT_STR, libName);
+    if (len >= (int) sizeof(buf))
+        return NULL;
+    else
+        return strdup(buf);
+}
+
+/*
+ * Check the result of an earlier call to JNI_OnLoad on this library.  If
+ * the call has not yet finished in another thread, wait for it.
+ */
+static bool checkOnLoadResult(SharedLib* pEntry)
+{
+    Thread* self = dvmThreadSelf();
+    if (pEntry->onLoadThreadId == self->threadId) {
+        /*
+         * Check this so we don't end up waiting for ourselves.  We need
+         * to return "true" so the caller can continue.
+         */
+        ALOGI("threadid=%d: recursive native library load attempt (%s)",
+            self->threadId, pEntry->pathName);
+        return true;
+    }
+
+    ALOGV("+++ retrieving %s OnLoad status", pEntry->pathName);
+    bool result;
+
+    dvmLockMutex(&pEntry->onLoadLock);
+    while (pEntry->onLoadResult == kOnLoadPending) {
+        ALOGD("threadid=%d: waiting for %s OnLoad status",
+            self->threadId, pEntry->pathName);
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        pthread_cond_wait(&pEntry->onLoadCond, &pEntry->onLoadLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+    if (pEntry->onLoadResult == kOnLoadOkay) {
+        ALOGV("+++ earlier OnLoad(%s) okay", pEntry->pathName);
+        result = true;
+    } else {
+        ALOGV("+++ earlier OnLoad(%s) failed", pEntry->pathName);
+        result = false;
+    }
+    dvmUnlockMutex(&pEntry->onLoadLock);
+    return result;
+}
+
+typedef int (*OnLoadFunc)(JavaVM*, void*);
+
+/*
+ * Load native code from the specified absolute pathname.  Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO? for better results we should absolutify the pathname.  For fully
+ * correct results we should stat to get the inode and compare that.  The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader.  The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns "true" on success. On failure, sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
+        char** detail)
+{
+    SharedLib* pEntry;
+    void* handle;
+    bool verbose;
+
+    /* reduce noise by not chattering about system libraries */
+    verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
+    verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
+
+    if (verbose)
+        ALOGD("Trying to load lib %s %p", pathName, classLoader);
+
+    *detail = NULL;
+
+    /*
+     * See if we've already loaded it.  If we have, and the class loader
+     * matches, return successfully without doing anything.
+     */
+    pEntry = findSharedLibEntry(pathName);
+    if (pEntry != NULL) {
+        if (pEntry->classLoader != classLoader) {
+            ALOGW("Shared lib '%s' already opened by CL %p; can't open in %p",
+                pathName, pEntry->classLoader, classLoader);
+            return false;
+        }
+        if (verbose) {
+            ALOGD("Shared lib '%s' already loaded in same CL %p",
+                pathName, classLoader);
+        }
+        if (!checkOnLoadResult(pEntry))
+            return false;
+        return true;
+    }
+
+    /*
+     * Open the shared library.  Because we're using a full path, the system
+     * doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
+     * resolve this library's dependencies though.)
+     *
+     * Failures here are expected when java.library.path has several entries
+     * and we have to hunt for the lib.
+     *
+     * The current version of the dynamic linker prints detailed information
+     * about dlopen() failures.  Some things to check if the message is
+     * cryptic:
+     *   - make sure the library exists on the device
+     *   - verify that the right path is being opened (the debug log message
+     *     above can help with that)
+     *   - check to see if the library is valid (e.g. not zero bytes long)
+     *   - check config/prelink-linux-arm.map to ensure that the library
+     *     is listed and is not being overrun by the previous entry (if
+     *     loading suddenly stops working on a prelinked library, this is
+     *     a good one to check)
+     *   - write a trivial app that calls sleep() then dlopen(), attach
+     *     to it with "strace -p <pid>" while it sleeps, and watch for
+     *     attempts to open nonexistent dependent shared libs
+     *
+     * This can execute slowly for a large library on a busy system, so we
+     * want to switch from RUNNING to VMWAIT while it executes.  This allows
+     * the GC to ignore us.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    handle = dlopen(pathName, RTLD_LAZY);
+    dvmChangeStatus(self, oldStatus);
+
+    if (handle == NULL) {
+        *detail = strdup(dlerror());
+        ALOGE("dlopen(\"%s\") failed: %s", pathName, *detail);
+        return false;
+    }
+
+    /* create a new entry */
+    SharedLib* pNewEntry;
+    pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
+    pNewEntry->pathName = strdup(pathName);
+    pNewEntry->handle = handle;
+    pNewEntry->classLoader = classLoader;
+    dvmInitMutex(&pNewEntry->onLoadLock);
+    pthread_cond_init(&pNewEntry->onLoadCond, NULL);
+    pNewEntry->onLoadThreadId = self->threadId;
+
+    /* try to add it to the list */
+    SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
+
+    if (pNewEntry != pActualEntry) {
+        ALOGI("WOW: we lost a race to add a shared lib (%s CL=%p)",
+            pathName, classLoader);
+        freeSharedLibEntry(pNewEntry);
+        return checkOnLoadResult(pActualEntry);
+    } else {
+        if (verbose)
+            ALOGD("Added shared lib %s %p", pathName, classLoader);
+
+        bool result = false;
+        void* vonLoad;
+        int version;
+
+        vonLoad = dlsym(handle, "JNI_OnLoad");
+        if (vonLoad == NULL) {
+            ALOGD("No JNI_OnLoad found in %s %p, skipping init", pathName, classLoader);
+            result = true;
+        } else {
+            /*
+             * Call JNI_OnLoad.  We have to override the current class
+             * loader, which will always be "null" since the stuff at the
+             * top of the stack is around Runtime.loadLibrary().  (See
+             * the comments in the JNI FindClass function.)
+             */
+            OnLoadFunc func = (OnLoadFunc)vonLoad;
+            Object* prevOverride = self->classLoaderOverride;
+
+            self->classLoaderOverride = classLoader;
+            oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+            if (gDvm.verboseJni) {
+                ALOGI("[Calling JNI_OnLoad for \"%s\"]", pathName);
+            }
+            version = (*func)(gDvmJni.jniVm, NULL);
+            dvmChangeStatus(self, oldStatus);
+            self->classLoaderOverride = prevOverride;
+
+            if (version == JNI_ERR) {
+                *detail = strdup(StringPrintf("JNI_ERR returned from JNI_OnLoad in \"%s\"",
+                                              pathName).c_str());
+            } else if (dvmIsBadJniVersion(version)) {
+                *detail = strdup(StringPrintf("Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+                                              pathName, version).c_str());
+                /*
+                 * It's unwise to call dlclose() here, but we can mark it
+                 * as bad and ensure that future load attempts will fail.
+                 *
+                 * We don't know how far JNI_OnLoad got, so there could
+                 * be some partially-initialized stuff accessible through
+                 * newly-registered native method calls.  We could try to
+                 * unregister them, but that doesn't seem worthwhile.
+                 */
+            } else {
+                result = true;
+            }
+            if (gDvm.verboseJni) {
+                ALOGI("[Returned %s from JNI_OnLoad for \"%s\"]",
+                      (result ? "successfully" : "failure"), pathName);
+            }
+        }
+
+        if (result)
+            pNewEntry->onLoadResult = kOnLoadOkay;
+        else
+            pNewEntry->onLoadResult = kOnLoadFailed;
+
+        pNewEntry->onLoadThreadId = 0;
+
+        /*
+         * Broadcast a wakeup to anybody sleeping on the condition variable.
+         */
+        dvmLockMutex(&pNewEntry->onLoadLock);
+        pthread_cond_broadcast(&pNewEntry->onLoadCond);
+        dvmUnlockMutex(&pNewEntry->onLoadLock);
+        return result;
+    }
+}
+
+
+/*
+ * Un-register JNI native methods.
+ *
+ * There are two relevant fields in struct Method, "nativeFunc" and
+ * "insns".  The former holds a function pointer to a "bridge" function
+ * (or, for internal native, the actual implementation).  The latter holds
+ * a pointer to the actual JNI method.
+ *
+ * The obvious approach is to reset both fields to their initial state
+ * (nativeFunc points at dvmResolveNativeMethod, insns holds NULL), but
+ * that creates some unpleasant race conditions.  In particular, if another
+ * thread is executing inside the call bridge for the method in question,
+ * and we reset insns to NULL, the VM will crash.  (See the comments above
+ * dvmSetNativeFunc() for additional commentary.)
+ *
+ * We can't rely on being able to update two 32-bit fields in one atomic
+ * operation (e.g. no 64-bit atomic ops on ARMv5TE), so we want to change
+ * only one field.  It turns out we can simply reset nativeFunc to its
+ * initial state, leaving insns alone, because dvmResolveNativeMethod
+ * ignores "insns" entirely.
+ *
+ * When the method is re-registered, both fields will be updated, but
+ * dvmSetNativeFunc guarantees that "insns" is updated first.  This means
+ * we shouldn't be in a situation where we have a "live" call bridge and
+ * a stale implementation pointer.
+ */
+static void unregisterJNINativeMethods(Method* methods, size_t count)
+{
+    while (count != 0) {
+        count--;
+
+        Method* meth = &methods[count];
+        if (!dvmIsNativeMethod(meth))
+            continue;
+        if (dvmIsAbstractMethod(meth))      /* avoid abstract method stubs */
+            continue;
+
+        /*
+         * Strictly speaking this ought to test the function pointer against
+         * the various JNI bridge functions to ensure that we only undo
+         * methods that were registered through JNI.  In practice, any
+         * native method with a non-NULL "insns" is a registered JNI method.
+         *
+         * If we inadvertently unregister an internal-native, it'll get
+         * re-resolved on the next call; unregistering an unregistered
+         * JNI method is a no-op.  So we don't really need to test for
+         * anything.
+         */
+
+        ALOGD("Unregistering JNI method %s.%s:%s",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+        dvmSetNativeFunc(meth, dvmResolveNativeMethod, NULL);
+    }
+}
+
+/*
+ * Un-register all JNI native methods from a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz)
+{
+    unregisterJNINativeMethods(clazz->directMethods, clazz->directMethodCount);
+    unregisterJNINativeMethods(clazz->virtualMethods, clazz->virtualMethodCount);
+}
+
+
+/*
+ * ===========================================================================
+ *      Signature-based method lookup
+ * ===========================================================================
+ */
+
+/*
+ * Create the pre-mangled form of the class+method string.
+ *
+ * Returns a newly-allocated string, and sets "*pLen" to the length.
+ */
+static char* createJniNameString(const char* classDescriptor,
+    const char* methodName, int* pLen)
+{
+    char* result;
+    size_t descriptorLength = strlen(classDescriptor);
+
+    *pLen = 4 + descriptorLength + strlen(methodName);
+
+    result = (char*)malloc(*pLen +1);
+    if (result == NULL)
+        return NULL;
+
+    /*
+     * Add one to classDescriptor to skip the "L", and then replace
+     * the final ";" with a "/" after the sprintf() call.
+     */
+    sprintf(result, "Java/%s%s", classDescriptor + 1, methodName);
+    result[5 + (descriptorLength - 2)] = '/';
+
+    return result;
+}
+
+/*
+ * Returns a newly-allocated, mangled copy of "str".
+ *
+ * "str" is a "modified UTF-8" string.  We convert it to UTF-16 first to
+ * make life simpler.
+ */
+static char* mangleString(const char* str, int len)
+{
+    //ALOGI("mangling '%s' %d", str, len);
+
+    assert(str[len] == '\0');
+
+    size_t charLen = dvmUtf8Len(str);
+    u2* utf16 = (u2*) malloc(sizeof(u2) * charLen);
+    if (utf16 == NULL)
+        return NULL;
+
+    dvmConvertUtf8ToUtf16(utf16, str);
+
+    /*
+     * Compute the length of the mangled string.
+     */
+    size_t mangleLen = 0;
+    for (size_t i = 0; i < charLen; i++) {
+        u2 ch = utf16[i];
+
+        if (ch == '$' || ch > 127) {
+            mangleLen += 6;
+        } else {
+            switch (ch) {
+            case '_':
+            case ';':
+            case '[':
+                mangleLen += 2;
+                break;
+            default:
+                mangleLen++;
+                break;
+            }
+        }
+    }
+
+    char* mangle = (char*) malloc(mangleLen +1);
+    if (mangle == NULL) {
+        free(utf16);
+        return NULL;
+    }
+
+    char* cp = mangle;
+    for (size_t i = 0; i < charLen; i++) {
+        u2 ch = utf16[i];
+
+        if (ch == '$' || ch > 127) {
+            sprintf(cp, "_0%04x", ch);
+            cp += 6;
+        } else {
+            switch (ch) {
+            case '_':
+                *cp++ = '_';
+                *cp++ = '1';
+                break;
+            case ';':
+                *cp++ = '_';
+                *cp++ = '2';
+                break;
+            case '[':
+                *cp++ = '_';
+                *cp++ = '3';
+                break;
+            case '/':
+                *cp++ = '_';
+                break;
+            default:
+                *cp++ = (char) ch;
+                break;
+            }
+        }
+    }
+
+    *cp = '\0';
+
+    free(utf16);
+    return mangle;
+}
+
+/*
+ * Create the mangled form of the parameter types.
+ */
+static char* createMangledSignature(const DexProto* proto)
+{
+    DexStringCache sigCache;
+    const char* interim;
+    char* result;
+
+    dexStringCacheInit(&sigCache);
+    interim = dexProtoGetParameterDescriptors(proto, &sigCache);
+    result = mangleString(interim, strlen(interim));
+    dexStringCacheRelease(&sigCache);
+
+    return result;
+}
+
+/*
+ * (This is a dvmHashForeach callback.)
+ *
+ * Search for a matching method in this shared library.
+ *
+ * TODO: we may want to skip libraries for which JNI_OnLoad failed.
+ */
+static int findMethodInLib(void* vlib, void* vmethod)
+{
+    const SharedLib* pLib = (const SharedLib*) vlib;
+    const Method* meth = (const Method*) vmethod;
+    char* preMangleCM = NULL;
+    char* mangleCM = NULL;
+    char* mangleSig = NULL;
+    char* mangleCMSig = NULL;
+    void* func = NULL;
+    int len;
+
+    if (meth->clazz->classLoader != pLib->classLoader) {
+        ALOGV("+++ not scanning '%s' for '%s' (wrong CL)",
+            pLib->pathName, meth->name);
+        return 0;
+    } else
+        ALOGV("+++ scanning '%s' for '%s'", pLib->pathName, meth->name);
+
+    /*
+     * First, we try it without the signature.
+     */
+    preMangleCM =
+        createJniNameString(meth->clazz->descriptor, meth->name, &len);
+    if (preMangleCM == NULL)
+        goto bail;
+
+    mangleCM = mangleString(preMangleCM, len);
+    if (mangleCM == NULL)
+        goto bail;
+
+    ALOGV("+++ calling dlsym(%s)", mangleCM);
+    func = dlsym(pLib->handle, mangleCM);
+    if (func == NULL) {
+        mangleSig =
+            createMangledSignature(&meth->prototype);
+        if (mangleSig == NULL)
+            goto bail;
+
+        mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
+        if (mangleCMSig == NULL)
+            goto bail;
+
+        sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
+
+        ALOGV("+++ calling dlsym(%s)", mangleCMSig);
+        func = dlsym(pLib->handle, mangleCMSig);
+        if (func != NULL) {
+            ALOGV("Found '%s' with dlsym", mangleCMSig);
+        }
+    } else {
+        ALOGV("Found '%s' with dlsym", mangleCM);
+    }
+
+bail:
+    free(preMangleCM);
+    free(mangleCM);
+    free(mangleSig);
+    free(mangleCMSig);
+    return (int) func;
+}
+
+/*
+ * See if the requested method lives in any of the currently-loaded
+ * shared libraries.  We do this by checking each of them for the expected
+ * method signature.
+ */
+static void* lookupSharedLibMethod(const Method* method)
+{
+    if (gDvm.nativeLibs == NULL) {
+        ALOGE("Unexpected init state: nativeLibs not ready");
+        dvmAbort();
+    }
+    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
+        (void*) method);
+}
diff --git a/vm/Native.h b/vm/Native.h
new file mode 100644
index 0000000..65ff8dc
--- /dev/null
+++ b/vm/Native.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Dalvik's native call interface.
+ *
+ * You should follow the JNI function naming conventions, but prefix with
+ * "Dalvik_" instead of "Java_".
+ */
+#ifndef DALVIK_NATIVE_H_
+#define DALVIK_NATIVE_H_
+
+/*
+ * Method description; equivalent to a JNI struct.
+ */
+struct DalvikNativeMethod {
+    const char* name;
+    const char* signature;
+    DalvikNativeFunc  fnPtr;
+};
+
+/*
+ * All methods for one class.  The last "methodInfo" has a NULL "name".
+ */
+struct DalvikNativeClass {
+    const char* classDescriptor;
+    const DalvikNativeMethod* methodInfo;
+    u4          classDescriptorHash;          /* initialized at runtime */
+};
+
+
+/* init/shutdown */
+bool dvmNativeStartup(void);
+void dvmNativeShutdown(void);
+
+
+/*
+ * Convert argc/argv into a function call.  This is platform-specific.
+ */
+extern "C" void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+    int argc, const u4* argv, const char* signature, void* func, JValue* pResult);
+
+/*
+ * Generate hints to speed native calls.  This is platform specific.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto);
+
+/*
+ * Convert a short library name ("jpeg") to a system-dependent name
+ * ("libjpeg.so").  Returns a newly-allocated string.
+ */
+char* dvmCreateSystemLibraryName(char* libName);
+bool dvmLoadNativeCode(const char* fileName, Object* classLoader,
+        char** detail);
+
+
+/*
+ * Resolve a native method.  This uses the same prototype as a
+ * DalvikBridgeFunc, because it takes the place of the actual function
+ * until the first time that it's invoked.
+ *
+ * Causes the method's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+    const Method* method, struct Thread* self);
+
+/*
+ * Unregister all JNI native methods associated with a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz);
+
+#define GET_ARG_LONG(_args, _elem)          dvmGetArgLong(_args, _elem)
+
+/*
+ * Helpful function for accessing long integers in "u4* args".
+ *
+ * We can't just return *(s8*)(&args[elem]), because that breaks if our
+ * architecture requires 64-bit alignment of 64-bit values.
+ *
+ * Big/little endian shouldn't matter here -- ordering of words within a
+ * long seems consistent across our supported platforms.
+ */
+INLINE s8 dvmGetArgLong(const u4* args, int elem)
+{
+    s8 val;
+    memcpy(&val, &args[elem], sizeof(val));
+    return val;
+}
+
+#endif  // DALVIK_NATIVE_H_
diff --git a/vm/PointerSet.cpp b/vm/PointerSet.cpp
new file mode 100644
index 0000000..21fea15
--- /dev/null
+++ b/vm/PointerSet.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.
+ */
+#include "Dalvik.h"
+
+/*
+ * Sorted, expanding list of pointers.
+ */
+struct PointerSet {
+    u2          alloc;
+    u2          count;
+    const void** list;
+};
+
+/*
+ * Verify that the set is in sorted order.
+ */
+#ifndef NDEBUG
+static bool verifySorted(PointerSet* pSet)
+{
+    const void* last = NULL;
+    int i;
+
+    for (i = 0; i < pSet->count; i++) {
+        const void* cur = pSet->list[i];
+        if (cur < last)
+            return false;
+        last = cur;
+    }
+
+    return true;
+}
+#endif
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize)
+{
+    PointerSet* pSet = (PointerSet*)calloc(1, sizeof(PointerSet));
+    if (pSet != NULL) {
+        if (initialSize > 0) {
+            pSet->list = (const void**)malloc(sizeof(void*) * initialSize);
+            if (pSet->list == NULL) {
+                free(pSet);
+                return NULL;
+            }
+            pSet->alloc = initialSize;
+        }
+    }
+
+    return pSet;
+}
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet)
+{
+    if (pSet == NULL)
+        return;
+
+    if (pSet->list != NULL) {
+        free(pSet->list);
+        pSet->list = NULL;
+    }
+    free(pSet);
+}
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet)
+{
+    pSet->count = 0;
+}
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet)
+{
+    return pSet->count;
+}
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i)
+{
+    return pSet->list[i];
+}
+
+/*
+ * Insert a new entry into the list.  If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the value was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr)
+{
+    int nearby;
+
+    if (dvmPointerSetHas(pSet, ptr, &nearby))
+        return false;
+
+    /* ensure we have space to add one more */
+    if (pSet->count == pSet->alloc) {
+        /* time to expand */
+        const void** newList;
+
+        if (pSet->alloc == 0)
+            pSet->alloc = 4;
+        else
+            pSet->alloc *= 2;
+        LOGVV("expanding %p to %d", pSet, pSet->alloc);
+        newList = (const void**)realloc(pSet->list, pSet->alloc * sizeof(void*));
+        if (newList == NULL) {
+            ALOGE("Failed expanding ptr set (alloc=%d)", pSet->alloc);
+            dvmAbort();
+        }
+        pSet->list = newList;
+    }
+
+    if (pSet->count == 0) {
+        /* empty list */
+        assert(nearby == 0);
+    } else {
+        /*
+         * Determine the insertion index.  The binary search might have
+         * terminated "above" or "below" the value.
+         */
+        if (nearby != 0 && ptr < pSet->list[nearby-1]) {
+            //ALOGD("nearby-1=%d %p, inserting %p at -1",
+            //    nearby-1, pSet->list[nearby-1], ptr);
+            nearby--;
+        } else if (ptr < pSet->list[nearby]) {
+            //ALOGD("nearby=%d %p, inserting %p at +0",
+            //    nearby, pSet->list[nearby], ptr);
+        } else {
+            //ALOGD("nearby+1=%d %p, inserting %p at +1",
+            //    nearby+1, pSet->list[nearby+1], ptr);
+            nearby++;
+        }
+
+        /*
+         * Move existing values, if necessary.
+         */
+        if (nearby != pSet->count) {
+            /* shift up */
+            memmove(&pSet->list[nearby+1], &pSet->list[nearby],
+                (pSet->count - nearby) * sizeof(pSet->list[0]));
+        }
+    }
+
+    pSet->list[nearby] = ptr;
+    pSet->count++;
+
+    assert(verifySorted(pSet));
+    return true;
+}
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr)
+{
+    int where;
+
+    if (!dvmPointerSetHas(pSet, ptr, &where))
+        return false;
+
+    if (where != pSet->count-1) {
+        /* shift down */
+        memmove(&pSet->list[where], &pSet->list[where+1],
+            (pSet->count-1 - where) * sizeof(pSet->list[0]));
+    }
+
+    pSet->count--;
+    pSet->list[pSet->count] = (const void*) 0xdecadead;     // debug
+    return true;
+}
+
+/*
+ * Returns the index if "ptr" appears in the list.  If it doesn't appear,
+ * this returns a negative index for a nearby element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex)
+{
+    int hi, lo, mid;
+
+    lo = mid = 0;
+    hi = pSet->count-1;
+
+    /* array is sorted, use a binary search */
+    while (lo <= hi) {
+        mid = (lo + hi) / 2;
+        const void* listVal = pSet->list[mid];
+
+        if (ptr > listVal) {
+            lo = mid + 1;
+        } else if (ptr < listVal) {
+            hi = mid - 1;
+        } else /* listVal == ptr */ {
+            if (pIndex != NULL)
+                *pIndex = mid;
+            return true;
+        }
+    }
+
+    if (pIndex != NULL)
+        *pIndex = mid;
+    return false;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count)
+{
+    int i, j;
+
+    for (i = 0; i < pSet->count; i++) {
+        for (j = 0; j < count; j++) {
+            if (pSet->list[i] == ptrArray[j]) {
+                /* match, keep this one */
+                break;
+            }
+        }
+
+        if (j == count) {
+            /* no match, remove entry */
+            if (i != pSet->count-1) {
+                /* shift down */
+                memmove(&pSet->list[i], &pSet->list[i+1],
+                    (pSet->count-1 - i) * sizeof(pSet->list[0]));
+            }
+
+            pSet->count--;
+            pSet->list[pSet->count] = (const void*) 0xdecadead;     // debug
+            i--;        /* adjust loop counter */
+        }
+    }
+}
+
+/*
+ * Print the list contents to stdout.  For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet)
+{
+    ALOGI("PointerSet %p", pSet);
+    int i;
+    for (i = 0; i < pSet->count; i++)
+        ALOGI(" %2d: %p", i, pSet->list[i]);
+}
diff --git a/vm/PointerSet.h b/vm/PointerSet.h
new file mode 100644
index 0000000..7a1cddf
--- /dev/null
+++ b/vm/PointerSet.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.  The set is
+ * kept in sorted order.
+ */
+#ifndef DALVIK_POINTERSET_H_
+#define DALVIK_POINTERSET_H_
+
+struct PointerSet;   /* private */
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize);
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet);
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet);
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet);
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i);
+
+/*
+ * Insert a new entry into the list.  If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the pointer was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the value appears, "false" otherwise.  If "pIndex" is
+ * non-NULL, it will receive the matching index or the index of a nearby
+ * element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex);
+
+/*
+ * Find an entry in the set.  Returns the index, or -1 if not found.
+ */
+INLINE int dvmPointerSetFind(const PointerSet* pSet, const void* ptr) {
+    int idx;
+    if (!dvmPointerSetHas(pSet, ptr, &idx))
+        idx = -1;
+    return idx;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count);
+
+/*
+ * Print the list contents to stdout.  For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet);
+
+#endif  // DALVIK_POINTERSET_H_
diff --git a/vm/Profile.cpp b/vm/Profile.cpp
new file mode 100644
index 0000000..866311c
--- /dev/null
+++ b/vm/Profile.cpp
@@ -0,0 +1,1201 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#include "Dalvik.h"
+#include <interp/InterpDefs.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <cutils/open_memstream.h>
+
+#ifdef HAVE_ANDROID_OS
+# define UPDATE_MAGIC_PAGE      1
+#endif
+
+/*
+ * File format:
+ *  header
+ *  record 0
+ *  record 1
+ *  ...
+ *
+ * Header format:
+ *  u4  magic ('SLOW')
+ *  u2  version
+ *  u2  offset to data
+ *  u8  start date/time in usec
+ *  u2  record size in bytes (version >= 2 only)
+ *  ... padding to 32 bytes
+ *
+ * Record format v1:
+ *  u1  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *
+ * Record format v2:
+ *  u2  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *
+ * Record format v3:
+ *  u2  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *  u4  wall time since start, in usec (when clock == "dual" only)
+ *
+ * 32 bits of microseconds is 70 minutes.
+ *
+ * All values are stored in little-endian order.
+ */
+#define TRACE_REC_SIZE_SINGLE_CLOCK  10 // using v2
+#define TRACE_REC_SIZE_DUAL_CLOCK    14 // using v3 with two timestamps
+#define TRACE_MAGIC         0x574f4c53
+#define TRACE_HEADER_LEN    32
+
+#define FILL_PATTERN        0xeeeeeeee
+
+
+/*
+ * Returns true if the thread CPU clock should be used.
+ */
+static inline bool useThreadCpuClock() {
+#if defined(HAVE_POSIX_CLOCKS)
+    return gDvm.profilerClockSource != kProfilerClockSourceWall;
+#else
+    return false;
+#endif
+}
+
+/*
+ * Returns true if the wall clock should be used.
+ */
+static inline bool useWallClock() {
+#if defined(HAVE_POSIX_CLOCKS)
+    return gDvm.profilerClockSource != kProfilerClockSourceThreadCpu;
+#else
+    return true;
+#endif
+}
+
+/*
+ * Get the wall-clock date/time, in usec.
+ */
+static inline u8 getWallTimeInUsec()
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+#if defined(HAVE_POSIX_CLOCKS)
+/*
+ * Get the thread-cpu time, in usec.
+ * We use this clock when we can because it enables us to track the time that
+ * a thread spends running and not blocked.
+ */
+static inline u8 getThreadCpuTimeInUsec(Thread* thread)
+{
+    clockid_t cid;
+    struct timespec tm;
+    pthread_getcpuclockid(thread->handle, &cid);
+    clock_gettime(cid, &tm);
+    if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
+        ALOGE("bad nsec: %ld", tm.tv_nsec);
+        dvmAbort();
+    }
+    return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+}
+#endif
+
+/*
+ * Get the clock used for stopwatch-like timing measurements on a single thread.
+ */
+static inline u8 getStopwatchClock()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    return getThreadCpuTimeInUsec(dvmThreadSelf());
+#else
+    return getWallTimeInUsec();
+#endif
+}
+
+/*
+ * Write little-endian data.
+ */
+static inline void storeShortLE(u1* buf, u2 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+}
+static inline void storeIntLE(u1* buf, u4 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+    *buf++ = (u1) (val >> 16);
+    *buf++ = (u1) (val >> 24);
+}
+static inline void storeLongLE(u1* buf, u8 val)
+{
+    *buf++ = (u1) val;
+    *buf++ = (u1) (val >> 8);
+    *buf++ = (u1) (val >> 16);
+    *buf++ = (u1) (val >> 24);
+    *buf++ = (u1) (val >> 32);
+    *buf++ = (u1) (val >> 40);
+    *buf++ = (u1) (val >> 48);
+    *buf++ = (u1) (val >> 56);
+}
+
+/*
+ * Gets a thread's stack trace as an array of method pointers of length pCount.
+ * The returned array must be freed by the caller.
+ */
+static const Method** getStackTrace(Thread* thread, size_t* pCount)
+{
+    void* fp = thread->interpSave.curFrame;
+    assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+    /* Compute the stack depth. */
+    size_t stackDepth = 0;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (!dvmIsBreakFrame((u4*) fp))
+            stackDepth++;
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    *pCount = stackDepth;
+
+    /*
+     * Allocate memory for stack trace. This must be freed later, either by
+     * freeThreadStackTraceSamples when tracing stops or by freeThread.
+     */
+    const Method** stackTrace = (const Method**) malloc(sizeof(Method*) *
+                                                        stackDepth);
+    if (stackTrace == NULL)
+        return NULL;
+
+    /* Walk the stack a second time, filling in the stack trace. */
+    const Method** ptr = stackTrace;
+    fp = thread->interpSave.curFrame;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*) fp)) {
+            *ptr++ = method;
+            stackDepth--;
+        }
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    assert(stackDepth == 0);
+
+    return stackTrace;
+}
+/*
+ * Get a sample of the stack trace for a thread.
+ */
+static void getSample(Thread* thread)
+{
+    /* Get old and new stack trace for thread. */
+    size_t newLength = 0;
+    const Method** newStackTrace = getStackTrace(thread, &newLength);
+    size_t oldLength = thread->stackTraceSampleLength;
+    const Method** oldStackTrace = thread->stackTraceSample;
+
+    /* Read time clocks to use for all events in this trace. */
+    u4 cpuClockDiff = 0;
+    u4 wallClockDiff = 0;
+    dvmMethodTraceReadClocks(thread, &cpuClockDiff, &wallClockDiff);
+    if (oldStackTrace == NULL) {
+        /*
+         * If there's no previous stack trace sample, log an entry event for
+         * every method in the trace.
+         */
+        for (int i = newLength - 1; i >= 0; --i) {
+            dvmMethodTraceAdd(thread, newStackTrace[i], METHOD_TRACE_ENTER,
+                              cpuClockDiff, wallClockDiff);
+        }
+    } else {
+        /*
+         * If there's a previous stack trace, diff the traces and emit entry
+         * and exit events accordingly.
+         */
+        int diffIndexOld = oldLength - 1;
+        int diffIndexNew = newLength - 1;
+        /* Iterate bottom-up until there's a difference between traces. */
+        while (diffIndexOld >= 0 && diffIndexNew >= 0 &&
+               oldStackTrace[diffIndexOld] == newStackTrace[diffIndexNew]) {
+            diffIndexOld--;
+            diffIndexNew--;
+        }
+        /* Iterate top-down over old trace until diff, emitting exit events. */
+        for (int i = 0; i <= diffIndexOld; ++i) {
+            dvmMethodTraceAdd(thread, oldStackTrace[i], METHOD_TRACE_EXIT,
+                              cpuClockDiff, wallClockDiff);
+        }
+        /* Iterate bottom-up over new trace from diff, emitting entry events. */
+        for (int i = diffIndexNew; i >= 0; --i) {
+            dvmMethodTraceAdd(thread, newStackTrace[i], METHOD_TRACE_ENTER,
+                              cpuClockDiff, wallClockDiff);
+        }
+    }
+
+    /* Free the old stack trace and update the thread's stack trace sample. */
+    free(oldStackTrace);
+    thread->stackTraceSample = newStackTrace;
+    thread->stackTraceSampleLength = newLength;
+}
+
+/*
+ * Entry point for sampling thread. The sampling interval in microseconds is
+ * passed in as an argument.
+ */
+static void* runSamplingThread(void* arg)
+{
+    int intervalUs = (int) arg;
+    while (gDvm.methodTrace.traceEnabled) {
+        dvmSuspendAllThreads(SUSPEND_FOR_SAMPLING);
+
+        dvmLockThreadList(dvmThreadSelf());
+        for (Thread *thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            getSample(thread);
+        }
+        dvmUnlockThreadList();
+
+        dvmResumeAllThreads(SUSPEND_FOR_SAMPLING);
+
+        usleep(intervalUs);
+    }
+    return NULL;
+}
+
+/*
+ * Boot-time init.
+ */
+bool dvmProfilingStartup()
+{
+    /*
+     * Initialize "dmtrace" method profiling.
+     */
+    memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
+    dvmInitMutex(&gDvm.methodTrace.startStopLock);
+    pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
+
+    assert(!dvmCheckException(dvmThreadSelf()));
+
+    /*
+     * Allocate storage for instruction counters.
+     */
+    gDvm.executedInstrCounts = (int*) calloc(kNumPackedOpcodes, sizeof(int));
+    if (gDvm.executedInstrCounts == NULL)
+        return false;
+
+#ifdef UPDATE_MAGIC_PAGE
+    /*
+     * If we're running on the emulator, there's a magic page into which
+     * we can put interpreted method information.  This allows interpreted
+     * methods to show up in the emulator's code traces.
+     *
+     * We could key this off of the "ro.kernel.qemu" property, but there's
+     * no real harm in doing this on a real device.
+     */
+    int fd = open("/dev/qemu_trace", O_RDWR);
+    if (fd < 0) {
+        ALOGV("Unable to open /dev/qemu_trace");
+    } else {
+        gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
+                                      MAP_SHARED, fd, 0);
+        close(fd);
+        if (gDvm.emulatorTracePage == MAP_FAILED) {
+            ALOGE("Unable to mmap /dev/qemu_trace");
+            gDvm.emulatorTracePage = NULL;
+        } else {
+            *(u4*) gDvm.emulatorTracePage = 0;
+        }
+    }
+#else
+    assert(gDvm.emulatorTracePage == NULL);
+#endif
+
+    return true;
+}
+
+/*
+ * Free up profiling resources.
+ */
+void dvmProfilingShutdown()
+{
+#ifdef UPDATE_MAGIC_PAGE
+    if (gDvm.emulatorTracePage != NULL)
+        munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
+#endif
+    free(gDvm.executedInstrCounts);
+}
+
+/*
+ * Update the set of active profilers
+ */
+static void updateActiveProfilers(ExecutionSubModes newMode, bool enable)
+{
+    int oldValue, newValue;
+
+    // Update global count
+    do {
+        oldValue = gDvm.activeProfilers;
+        newValue = oldValue + (enable ? 1 : -1);
+        if (newValue < 0) {
+            ALOGE("Can't have %d active profilers", newValue);
+            dvmAbort();
+        }
+    } while (android_atomic_release_cas(oldValue, newValue,
+            &gDvm.activeProfilers) != 0);
+
+    // Tell the threads
+    if (enable) {
+        dvmEnableAllSubMode(newMode);
+    } else {
+        dvmDisableAllSubMode(newMode);
+    }
+
+#if defined(WITH_JIT)
+    dvmCompilerUpdateGlobalState();
+#endif
+
+    ALOGD("+++ active profiler count now %d", newValue);
+}
+
+
+/*
+ * Reset the "cpuClockBase" field in all threads.
+ */
+static void resetCpuClockBase()
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        thread->cpuClockBaseSet = false;
+        thread->cpuClockBase = 0;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Free and reset the "stackTraceSample" field in all threads.
+ */
+static void freeThreadStackTraceSamples()
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        free(thread->stackTraceSample);
+        thread->stackTraceSample = NULL;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Dump the thread list to the specified file.
+ */
+static void dumpThreadList(FILE* fp) {
+    dvmLockThreadList(NULL);
+    for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        std::string threadName(dvmGetThreadName(thread));
+        fprintf(fp, "%d\t%s\n", thread->threadId, threadName.c_str());
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpMarkedMethods(void* vclazz, void* vfp)
+{
+    DexStringCache stringCache;
+    ClassObject* clazz = (ClassObject*) vclazz;
+    FILE* fp = (FILE*) vfp;
+    Method* meth;
+    char* name;
+    int i;
+
+    dexStringCacheInit(&stringCache);
+
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        meth = &clazz->virtualMethods[i];
+        if (meth->inProfile) {
+            name = dvmDescriptorToName(meth->clazz->descriptor);
+            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+                name, meth->name,
+                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+            meth->inProfile = false;
+            free(name);
+        }
+    }
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        meth = &clazz->directMethods[i];
+        if (meth->inProfile) {
+            name = dvmDescriptorToName(meth->clazz->descriptor);
+            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+                name, meth->name,
+                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+            meth->inProfile = false;
+            free(name);
+        }
+    }
+
+    dexStringCacheRelease(&stringCache);
+
+    return 0;
+}
+
+/*
+ * Dump the list of "marked" methods to the specified file.
+ */
+static void dumpMethodList(FILE* fp)
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Start method tracing.  Method tracing is global to the VM (i.e. we
+ * trace all threads).
+ *
+ * This opens the output file (if an already open fd has not been supplied,
+ * and we're not going direct to DDMS) and allocates the data buffer.  This
+ * takes ownership of the file descriptor, closing it on completion.
+ *
+ * On failure, we throw an exception and return.
+ */
+void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
+    int flags, bool directToDdms, bool samplingEnabled, int intervalUs)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+
+    assert(bufferSize > 0);
+
+    dvmLockMutex(&state->startStopLock);
+    while (state->traceEnabled != 0) {
+        ALOGI("TRACE start requested, but already in progress; stopping");
+        dvmUnlockMutex(&state->startStopLock);
+        dvmMethodTraceStop();
+        dvmLockMutex(&state->startStopLock);
+    }
+    ALOGI("TRACE STARTED: '%s' %dKB", traceFileName, bufferSize / 1024);
+
+    /*
+     * Allocate storage and open files.
+     *
+     * We don't need to initialize the buffer, but doing so might remove
+     * some fault overhead if the pages aren't mapped until touched.
+     */
+    state->buf = (u1*) malloc(bufferSize);
+    if (state->buf == NULL) {
+        dvmThrowInternalError("buffer alloc failed");
+        goto fail;
+    }
+    if (!directToDdms) {
+        if (traceFd < 0) {
+            state->traceFile = fopen(traceFileName, "w");
+        } else {
+            state->traceFile = fdopen(traceFd, "w");
+        }
+        if (state->traceFile == NULL) {
+            int err = errno;
+            ALOGE("Unable to open trace file '%s': %s",
+                traceFileName, strerror(err));
+            dvmThrowExceptionFmt(gDvm.exRuntimeException,
+                "Unable to open trace file '%s': %s",
+                traceFileName, strerror(err));
+            goto fail;
+        }
+    }
+    traceFd = -1;
+    memset(state->buf, (char)FILL_PATTERN, bufferSize);
+
+    state->directToDdms = directToDdms;
+    state->bufferSize = bufferSize;
+    state->overflow = false;
+
+    /*
+     * Enable alloc counts if we've been requested to do so.
+     */
+    state->flags = flags;
+    if ((flags & TRACE_ALLOC_COUNTS) != 0)
+        dvmStartAllocCounting();
+
+    /* reset our notion of the start time for all CPU threads */
+    resetCpuClockBase();
+
+    state->startWhen = getWallTimeInUsec();
+
+    if (useThreadCpuClock() && useWallClock()) {
+        state->traceVersion = 3;
+        state->recordSize = TRACE_REC_SIZE_DUAL_CLOCK;
+    } else {
+        state->traceVersion = 2;
+        state->recordSize = TRACE_REC_SIZE_SINGLE_CLOCK;
+    }
+
+    state->samplingEnabled = samplingEnabled;
+
+    /*
+     * Output the header.
+     */
+    memset(state->buf, 0, TRACE_HEADER_LEN);
+    storeIntLE(state->buf + 0, TRACE_MAGIC);
+    storeShortLE(state->buf + 4, state->traceVersion);
+    storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
+    storeLongLE(state->buf + 8, state->startWhen);
+    if (state->traceVersion >= 3) {
+        storeShortLE(state->buf + 16, state->recordSize);
+    }
+    state->curOffset = TRACE_HEADER_LEN;
+
+    /*
+     * Set the "enabled" flag.  Once we do this, threads will wait to be
+     * signaled before exiting, so we have to make sure we wake them up.
+     */
+    android_atomic_release_store(true, &state->traceEnabled);
+
+    /*
+     * ENHANCEMENT: To trace just a single thread, modify the
+     * following to take a Thread* argument, and set the appropriate
+     * interpBreak flags only on the target thread.
+     */
+    if (samplingEnabled) {
+        updateActiveProfilers(kSubModeSampleTrace, true);
+        /* Start the sampling thread. */
+        if (!dvmCreateInternalThread(&state->samplingThreadHandle,
+                "Sampling Thread", &runSamplingThread, (void*) intervalUs)) {
+            dvmThrowInternalError("failed to create sampling thread");
+            goto fail;
+        }
+    } else {
+        updateActiveProfilers(kSubModeMethodTrace, true);
+    }
+
+    dvmUnlockMutex(&state->startStopLock);
+    return;
+
+fail:
+    if (state->traceFile != NULL) {
+        fclose(state->traceFile);
+        state->traceFile = NULL;
+    }
+    if (state->buf != NULL) {
+        free(state->buf);
+        state->buf = NULL;
+    }
+    if (traceFd >= 0)
+        close(traceFd);
+    dvmUnlockMutex(&state->startStopLock);
+}
+
+/*
+ * Run through the data buffer and pull out the methods that were visited.
+ * Set a mark so that we know which ones to output.
+ */
+static void markTouchedMethods(int endOffset)
+{
+    u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
+    u1* end = gDvm.methodTrace.buf + endOffset;
+    size_t recordSize = gDvm.methodTrace.recordSize;
+    unsigned int methodVal;
+    Method* method;
+
+    while (ptr < end) {
+        methodVal = ptr[2] | (ptr[3] << 8) | (ptr[4] << 16)
+                    | (ptr[5] << 24);
+        method = (Method*) METHOD_ID(methodVal);
+
+        method->inProfile = true;
+        ptr += recordSize;
+    }
+}
+
+/*
+ * Exercises the clocks in the same way they will be during profiling.
+ */
+static inline void measureClockOverhead()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        getThreadCpuTimeInUsec(dvmThreadSelf());
+    }
+#endif
+    if (useWallClock()) {
+        getWallTimeInUsec();
+    }
+}
+
+/*
+ * Compute the amount of overhead in a clock call, in nsec.
+ *
+ * This value is going to vary depending on what else is going on in the
+ * system.  When examined across several runs a pattern should emerge.
+ */
+static u4 getClockOverhead()
+{
+    u8 calStart, calElapsed;
+    int i;
+
+    calStart = getStopwatchClock();
+    for (i = 1000 * 4; i > 0; i--) {
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+        measureClockOverhead();
+    }
+
+    calElapsed = getStopwatchClock() - calStart;
+    return (int) (calElapsed / (8*4));
+}
+
+/*
+ * Indicates if method tracing is active and what kind of tracing is active.
+ */
+TracingMode dvmGetMethodTracingMode()
+{
+    const MethodTraceState* state = &gDvm.methodTrace;
+    if (!state->traceEnabled) {
+        return TRACING_INACTIVE;
+    } else if (state->samplingEnabled) {
+        return SAMPLE_PROFILING_ACTIVE;
+    } else {
+        return METHOD_TRACING_ACTIVE;
+    }
+}
+
+/*
+ * Stop method tracing.  We write the buffer to disk and generate a key
+ * file so we can interpret it.
+ */
+void dvmMethodTraceStop()
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+    bool samplingEnabled = state->samplingEnabled;
+    u8 elapsed;
+
+    /*
+     * We need this to prevent somebody from starting a new trace while
+     * we're in the process of stopping the old.
+     */
+    dvmLockMutex(&state->startStopLock);
+
+    if (!state->traceEnabled) {
+        /* somebody already stopped it, or it was never started */
+        ALOGD("TRACE stop requested, but not running");
+        dvmUnlockMutex(&state->startStopLock);
+        return;
+    } else {
+        if (samplingEnabled) {
+            updateActiveProfilers(kSubModeSampleTrace, false);
+        } else {
+            updateActiveProfilers(kSubModeMethodTrace, false);
+        }
+    }
+
+    /* compute elapsed time */
+    elapsed = getWallTimeInUsec() - state->startWhen;
+
+    /*
+     * Globally disable it, and allow other threads to notice.  We want
+     * to stall here for at least as long as dvmMethodTraceAdd needs
+     * to finish.  There's no real risk though -- it will take a while to
+     * write the data to disk, and we don't clear the buffer pointer until
+     * after that completes.
+     */
+    state->traceEnabled = false;
+    ANDROID_MEMBAR_FULL();
+    sched_yield();
+    usleep(250 * 1000);
+
+    if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
+        dvmStopAllocCounting();
+
+    /*
+     * It's possible under some circumstances for a thread to have advanced
+     * the data pointer but not written the method value.  It's possible
+     * (though less likely) for the data pointer to be advanced, or partial
+     * data written, while we're doing work here.
+     *
+     * To avoid seeing partially-written data, we grab state->curOffset here,
+     * and use our local copy from here on.  We then scan through what's
+     * already written.  If we see the fill pattern in what should be the
+     * method pointer, we cut things off early.  (If we don't, we'll fail
+     * when we dereference the pointer.)
+     *
+     * There's a theoretical possibility of interrupting another thread
+     * after it has partially written the method pointer, in which case
+     * we'll likely crash when we dereference it.  The possibility of
+     * this actually happening should be at or near zero.  Fixing it
+     * completely could be done by writing the thread number last and
+     * using a sentinel value to indicate a partially-written record,
+     * but that requires memory barriers.
+     */
+    int finalCurOffset = state->curOffset;
+
+    size_t recordSize = state->recordSize;
+    if (finalCurOffset > TRACE_HEADER_LEN) {
+        u4 fillVal = METHOD_ID(FILL_PATTERN);
+        u1* scanPtr = state->buf + TRACE_HEADER_LEN;
+
+        while (scanPtr < state->buf + finalCurOffset) {
+            u4 methodVal = scanPtr[2] | (scanPtr[3] << 8) | (scanPtr[4] << 16)
+                        | (scanPtr[5] << 24);
+            if (METHOD_ID(methodVal) == fillVal) {
+                u1* scanBase = state->buf + TRACE_HEADER_LEN;
+                ALOGW("Found unfilled record at %d (of %d)",
+                    (scanPtr - scanBase) / recordSize,
+                    (finalCurOffset - TRACE_HEADER_LEN) / recordSize);
+                finalCurOffset = scanPtr - state->buf;
+                break;
+            }
+
+            scanPtr += recordSize;
+        }
+    }
+
+    ALOGI("TRACE STOPPED%s: writing %d records",
+        state->overflow ? " (NOTE: overflowed buffer)" : "",
+        (finalCurOffset - TRACE_HEADER_LEN) / recordSize);
+    if (gDvm.debuggerActive) {
+        ALOGW("WARNING: a debugger is active; method-tracing results "
+             "will be skewed");
+    }
+
+    /*
+     * Do a quick calibration test to see how expensive our clock call is.
+     */
+    u4 clockNsec = getClockOverhead();
+
+    markTouchedMethods(finalCurOffset);
+
+    char* memStreamPtr;
+    size_t memStreamSize;
+    if (state->directToDdms) {
+        assert(state->traceFile == NULL);
+        state->traceFile = open_memstream(&memStreamPtr, &memStreamSize);
+        if (state->traceFile == NULL) {
+            /* not expected */
+            ALOGE("Unable to open memstream");
+            dvmAbort();
+        }
+    }
+    assert(state->traceFile != NULL);
+
+    fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
+    fprintf(state->traceFile, "%d\n", state->traceVersion);
+    fprintf(state->traceFile, "data-file-overflow=%s\n",
+        state->overflow ? "true" : "false");
+    if (useThreadCpuClock()) {
+        if (useWallClock()) {
+            fprintf(state->traceFile, "clock=dual\n");
+        } else {
+            fprintf(state->traceFile, "clock=thread-cpu\n");
+        }
+    } else {
+        fprintf(state->traceFile, "clock=wall\n");
+    }
+    fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed);
+    fprintf(state->traceFile, "num-method-calls=%d\n",
+        (finalCurOffset - TRACE_HEADER_LEN) / state->recordSize);
+    fprintf(state->traceFile, "clock-call-overhead-nsec=%d\n", clockNsec);
+    fprintf(state->traceFile, "vm=dalvik\n");
+    if ((state->flags & TRACE_ALLOC_COUNTS) != 0) {
+        fprintf(state->traceFile, "alloc-count=%d\n",
+            gDvm.allocProf.allocCount);
+        fprintf(state->traceFile, "alloc-size=%d\n",
+            gDvm.allocProf.allocSize);
+        fprintf(state->traceFile, "gc-count=%d\n",
+            gDvm.allocProf.gcCount);
+    }
+    fprintf(state->traceFile, "%cthreads\n", TOKEN_CHAR);
+    dumpThreadList(state->traceFile);
+    fprintf(state->traceFile, "%cmethods\n", TOKEN_CHAR);
+    dumpMethodList(state->traceFile);
+    fprintf(state->traceFile, "%cend\n", TOKEN_CHAR);
+
+    if (state->directToDdms) {
+        /*
+         * Data is in two places: memStreamPtr and state->buf.  Send
+         * the whole thing to DDMS, wrapped in an MPSE packet.
+         */
+        fflush(state->traceFile);
+
+        struct iovec iov[2];
+        iov[0].iov_base = memStreamPtr;
+        iov[0].iov_len = memStreamSize;
+        iov[1].iov_base = state->buf;
+        iov[1].iov_len = finalCurOffset;
+        dvmDbgDdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+    } else {
+        /* append the profiling data */
+        if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) {
+            int err = errno;
+            ALOGE("trace fwrite(%d) failed: %s",
+                finalCurOffset, strerror(err));
+            dvmThrowExceptionFmt(gDvm.exRuntimeException,
+                "Trace data write failed: %s", strerror(err));
+        }
+    }
+
+    /* done! */
+    free(state->buf);
+    state->buf = NULL;
+    fclose(state->traceFile);
+    state->traceFile = NULL;
+
+    /* free and clear sampling traces held by all threads */
+    if (samplingEnabled) {
+        freeThreadStackTraceSamples();
+    }
+
+    /* wake any threads that were waiting for profiling to complete */
+    dvmBroadcastCond(&state->threadExitCond);
+    dvmUnlockMutex(&state->startStopLock);
+
+    /* make sure the sampling thread has stopped */
+    if (samplingEnabled &&
+        pthread_join(state->samplingThreadHandle, NULL) != 0) {
+        ALOGW("Sampling thread join failed");
+    }
+}
+
+/*
+ * Read clocks and generate time diffs for method trace events.
+ */
+void dvmMethodTraceReadClocks(Thread* self, u4* cpuClockDiff,
+                              u4* wallClockDiff)
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        if (!self->cpuClockBaseSet) {
+            /* Initialize per-thread CPU clock base time on first use. */
+            self->cpuClockBase = getThreadCpuTimeInUsec(self);
+            self->cpuClockBaseSet = true;
+        } else {
+            *cpuClockDiff = getThreadCpuTimeInUsec(self) - self->cpuClockBase;
+        }
+    }
+#endif
+    if (useWallClock()) {
+        *wallClockDiff = getWallTimeInUsec() - gDvm.methodTrace.startWhen;
+    }
+}
+
+/*
+ * We just did something with a method.  Emit a record.
+ *
+ * Multiple threads may be banging on this all at once.  We use atomic ops
+ * rather than mutexes for speed.
+ */
+void dvmMethodTraceAdd(Thread* self, const Method* method, int action,
+                       u4 cpuClockDiff, u4 wallClockDiff)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+    u4 methodVal;
+    int oldOffset, newOffset;
+    u1* ptr;
+
+    assert(method != NULL);
+
+    /*
+     * Advance "curOffset" atomically.
+     */
+    do {
+        oldOffset = state->curOffset;
+        newOffset = oldOffset + state->recordSize;
+        if (newOffset > state->bufferSize) {
+            state->overflow = true;
+            return;
+        }
+    } while (android_atomic_release_cas(oldOffset, newOffset,
+            &state->curOffset) != 0);
+
+    //assert(METHOD_ACTION((u4) method) == 0);
+
+    methodVal = METHOD_COMBINE((u4) method, action);
+
+    /*
+     * Write data into "oldOffset".
+     */
+    ptr = state->buf + oldOffset;
+    *ptr++ = (u1) self->threadId;
+    *ptr++ = (u1) (self->threadId >> 8);
+    *ptr++ = (u1) methodVal;
+    *ptr++ = (u1) (methodVal >> 8);
+    *ptr++ = (u1) (methodVal >> 16);
+    *ptr++ = (u1) (methodVal >> 24);
+
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        *ptr++ = (u1) cpuClockDiff;
+        *ptr++ = (u1) (cpuClockDiff >> 8);
+        *ptr++ = (u1) (cpuClockDiff >> 16);
+        *ptr++ = (u1) (cpuClockDiff >> 24);
+    }
+#endif
+
+    if (useWallClock()) {
+        *ptr++ = (u1) wallClockDiff;
+        *ptr++ = (u1) (wallClockDiff >> 8);
+        *ptr++ = (u1) (wallClockDiff >> 16);
+        *ptr++ = (u1) (wallClockDiff >> 24);
+    }
+}
+
+
+/*
+ * Register the METHOD_TRACE_ENTER action for the fast interpreter and
+ * JIT'ed code.
+ */
+void dvmFastMethodTraceEnter(const Method* method, Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_ENTER, cpuClockDiff,
+                          wallClockDiff);
+    }
+}
+
+/*
+ * Register the METHOD_TRACE_EXIT action for the fast interpreter and
+ * JIT'ed code for methods. The about-to-return callee method can be
+ * retrieved from self->interpSave.method.
+ */
+void dvmFastMethodTraceExit(Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
+        dvmMethodTraceAdd(self, self->interpSave.method,
+                          METHOD_TRACE_EXIT, cpuClockDiff, wallClockDiff);
+    }
+}
+
+/*
+ * Register the METHOD_TRACE_EXIT action for the fast interpreter and
+ * JIT'ed code for JNI methods. The about-to-return JNI callee method is passed
+ * in explicitly.  Also used for inline-execute.
+ */
+void dvmFastNativeMethodTraceExit(const Method* method, Thread* self)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_EXIT, cpuClockDiff,
+                          wallClockDiff);
+    }
+}
+
+/*
+ * We just did something with a method.  Emit a record by setting a value
+ * in a magic memory location.
+ */
+void dvmEmitEmulatorTrace(const Method* method, int action)
+{
+#ifdef UPDATE_MAGIC_PAGE
+    /*
+     * We store the address of the Dalvik bytecodes to the memory-mapped
+     * trace page for normal methods.  We also trace calls to native
+     * functions by storing the address of the native function to the
+     * trace page.
+     * Abstract methods don't have any bytecodes, so we don't trace them.
+     * (Abstract methods are never called, but in Dalvik they can be
+     * because we do a "late trap" to a native method to generate the
+     * abstract method exception.)
+     */
+    if (dvmIsAbstractMethod(method))
+        return;
+
+    u4* pMagic = (u4*) gDvm.emulatorTracePage;
+    u4 addr;
+
+    if (dvmIsNativeMethod(method)) {
+        /*
+         * The "action" parameter is one of:
+         *   0 = ENTER
+         *   1 = EXIT
+         *   2 = UNROLL
+         * To help the trace tools reconstruct the runtime stack containing
+         * a mix of normal plus native methods, we add 4 to the action if this
+         * is a native method.
+         */
+        action += 4;
+
+        /*
+         * Get the address of the native function.
+         * This isn't the right address -- how do I get it?
+         * Fortunately, the trace tools can get by without the address, but
+         * it would be nice to fix this.
+         */
+         addr = (u4) method->nativeFunc;
+    } else {
+        /*
+         * The dexlist output shows the &DexCode.insns offset value, which
+         * is offset from the start of the base DEX header. Method.insns
+         * is the absolute address, effectively offset from the start of
+         * the optimized DEX header. We either need to return the
+         * optimized DEX base file address offset by the right amount, or
+         * take the "real" address and subtract off the size of the
+         * optimized DEX header.
+         *
+         * Would be nice to factor this out at dexlist time, but we can't count
+         * on having access to the correct optimized DEX file.
+         */
+        assert(method->insns != NULL);
+        const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
+        addr = (u4) method->insns - pOptHdr->dexOffset;
+    }
+
+    *(pMagic+action) = addr;
+    LOGVV("Set %p = 0x%08x (%s.%s)",
+        pMagic+action, addr, method->clazz->descriptor, method->name);
+#endif
+}
+
+/*
+ * The GC calls this when it's about to start.  We add a marker to the
+ * trace output so the tool can exclude the GC cost from the results.
+ */
+void dvmMethodTraceGCBegin()
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceGcMethod);
+}
+void dvmMethodTraceGCEnd()
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceGcMethod);
+}
+
+/*
+ * The class loader calls this when it's loading or initializing a class.
+ */
+void dvmMethodTraceClassPrepBegin()
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
+}
+void dvmMethodTraceClassPrepEnd()
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
+}
+
+
+/*
+ * Enable emulator trace info.
+ */
+void dvmEmulatorTraceStart()
+{
+    /* If we could not map the emulator trace page, then do not enable tracing */
+    if (gDvm.emulatorTracePage == NULL)
+        return;
+
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount++;
+    if (gDvm.emulatorTraceEnableCount == 1)
+        ALOGD("--- emulator method traces enabled");
+    updateActiveProfilers(kSubModeEmulatorTrace, true);
+}
+
+/*
+ * Disable emulator trace info.
+ */
+void dvmEmulatorTraceStop()
+{
+    if (gDvm.emulatorTraceEnableCount == 0) {
+        ALOGE("ERROR: emulator tracing not enabled");
+        return;
+    }
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount--;
+    if (gDvm.emulatorTraceEnableCount == 0)
+        ALOGD("--- emulator method traces disabled");
+    updateActiveProfilers(kSubModeEmulatorTrace,
+                          (gDvm.emulatorTraceEnableCount != 0));
+}
+
+
+/*
+ * Start instruction counting.
+ */
+void dvmStartInstructionCounting()
+{
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.instructionCountEnableCount++;
+    updateActiveProfilers(kSubModeInstCounting, true);
+}
+
+/*
+ * Stop instruction counting.
+ */
+void dvmStopInstructionCounting()
+{
+    if (gDvm.instructionCountEnableCount == 0) {
+        ALOGE("ERROR: instruction counting not enabled");
+        dvmAbort();
+    }
+    gDvm.instructionCountEnableCount--;
+    updateActiveProfilers(kSubModeInstCounting,
+                          (gDvm.instructionCountEnableCount != 0));
+}
+
+
+/*
+ * Start alloc counting.  Note this doesn't affect the "active profilers"
+ * count, since the interpreter loop is not involved.
+ */
+void dvmStartAllocCounting()
+{
+    gDvm.allocProf.enabled = true;
+}
+
+/*
+ * Stop alloc counting.
+ */
+void dvmStopAllocCounting()
+{
+    gDvm.allocProf.enabled = false;
+}
diff --git a/vm/README.txt b/vm/README.txt
new file mode 100644
index 0000000..1be1a63
--- /dev/null
+++ b/vm/README.txt
@@ -0,0 +1,19 @@
+Dalvik Virtual Machine
+
+
+Source code rules of the road:
+
+- All externally-visible function names must start with "dvm" to avoid
+namespace clashes.  Use static functions when possible.
+
+- Do not create static variables (globally or locally).  Do not create
+global variables.  Keep everything with non-local lifespan in "gDvm",
+defined in Globals.h, so that all global VM state is in one place.
+
+- Use "startup" and "shutdown" functions to clean up gDvm.  The VM must
+exit cleanly in valgrind.
+
+- The primary target is ARM Linux.  Others are secondary, but must still
+work correctly.
+
+- Use of gcc-specific and C99 constructs is allowed.
diff --git a/vm/RawDexFile.cpp b/vm/RawDexFile.cpp
new file mode 100644
index 0000000..4598767
--- /dev/null
+++ b/vm/RawDexFile.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Open an unoptimized DEX file.
+ */
+
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+ * Copy the given number of bytes from one fd to another, first
+ * seeking the source fd to the start of the file.
+ */
+static int copyFileToFile(int destFd, int srcFd, size_t size)
+{
+    if (lseek(srcFd, 0, SEEK_SET) != 0) {
+        ALOGE("lseek failure: %s", strerror(errno));
+        return -1;
+    }
+
+    return sysCopyFileToFile(destFd, srcFd, size);
+}
+
+/*
+ * Get the modification time and size in bytes for the given fd.
+ */
+static int getModTimeAndSize(int fd, u4* modTime, size_t* size)
+{
+    struct stat buf;
+    int result = fstat(fd, &buf);
+
+    if (result < 0) {
+        ALOGE("Unable to determine mod time: %s", strerror(errno));
+        return -1;
+    }
+
+    *modTime = (u4) buf.st_mtime;
+    *size = (size_t) buf.st_size;
+    assert((size_t) buf.st_size == buf.st_size);
+
+    return 0;
+}
+
+/*
+ * Verify the dex file magic number, and get the adler32 checksum out
+ * of the given fd, which is presumed to be a reference to a dex file
+ * with the cursor at the start of the file. The fd's cursor is
+ * modified by this operation.
+ */
+static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
+{
+    /*
+     * The start of a dex file is eight bytes of magic followed by
+     * four bytes of checksum.
+     */
+    u1 headerStart[12];
+    ssize_t amt = read(fd, headerStart, sizeof(headerStart));
+
+    if (amt < 0) {
+        ALOGE("Unable to read header: %s", strerror(errno));
+        return -1;
+    }
+
+    if (amt != sizeof(headerStart)) {
+        ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
+        return -1;
+    }
+
+    if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
+        return -1;
+    }
+
+    /*
+     * We can't just cast the data to a u4 and read it, since the
+     * platform might be big-endian (also, because that would make the
+     * compiler complain about type-punned pointers). We assume here
+     * that the dex file is in the standard little-endian format; if
+     * that assumption turns out to be invalid, code that runs later
+     * will notice and complain.
+     */
+    *adler32 = (u4) headerStart[8]
+        | (((u4) headerStart[9]) << 8)
+        | (((u4) headerStart[10]) << 16)
+        | (((u4) headerStart[11]) << 24);
+
+    return 0;
+}
+
+/* See documentation comment in header. */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+    RawDexFile** ppRawDexFile, bool isBootstrap)
+{
+    /*
+     * TODO: This duplicates a lot of code from dvmJarFileOpen() in
+     * JarFile.c. This should be refactored.
+     */
+
+    DvmDex* pDvmDex = NULL;
+    char* cachedName = NULL;
+    int result = -1;
+    int dexFd = -1;
+    int optFd = -1;
+    u4 modTime = 0;
+    u4 adler32 = 0;
+    size_t fileSize = 0;
+    bool newFile = false;
+    bool locked = false;
+
+    dexFd = open(fileName, O_RDONLY);
+    if (dexFd < 0) goto bail;
+
+    /* If we fork/exec into dexopt, don't let it inherit the open fd. */
+    dvmSetCloseOnExec(dexFd);
+
+    if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
+        ALOGE("Error with header for %s", fileName);
+        goto bail;
+    }
+
+    if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
+        ALOGE("Error with stat for %s", fileName);
+        goto bail;
+    }
+
+    /*
+     * See if the cached file matches. If so, optFd will become a reference
+     * to the cached file and will have been seeked to just past the "opt"
+     * header.
+     */
+
+    if (odexOutputName == NULL) {
+        cachedName = dexOptGenerateCacheFileName(fileName, NULL);
+        if (cachedName == NULL)
+            goto bail;
+    } else {
+        cachedName = strdup(odexOutputName);
+    }
+
+    ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
+            fileName, cachedName);
+
+    optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
+        adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
+
+    if (optFd < 0) {
+        ALOGI("Unable to open or create cache for %s (%s)",
+                fileName, cachedName);
+        goto bail;
+    }
+    locked = true;
+
+    /*
+     * If optFd points to a new file (because there was no cached
+     * version, or the cached version was stale), generate the
+     * optimized DEX. The file descriptor returned is still locked,
+     * and is positioned just past the optimization header.
+     */
+    if (newFile) {
+        u8 startWhen, copyWhen, endWhen;
+        bool result;
+        off_t dexOffset;
+
+        dexOffset = lseek(optFd, 0, SEEK_CUR);
+        result = (dexOffset > 0);
+
+        if (result) {
+            startWhen = dvmGetRelativeTimeUsec();
+            result = copyFileToFile(optFd, dexFd, fileSize) == 0;
+            copyWhen = dvmGetRelativeTimeUsec();
+        }
+
+        if (result) {
+            result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
+                fileName, modTime, adler32, isBootstrap);
+        }
+
+        if (!result) {
+            ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
+            goto bail;
+        }
+
+        endWhen = dvmGetRelativeTimeUsec();
+        ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
+            fileName,
+            (int) (copyWhen - startWhen) / 1000,
+            (int) (endWhen - copyWhen) / 1000);
+    }
+
+    /*
+     * Map the cached version.  This immediately rewinds the fd, so it
+     * doesn't have to be seeked anywhere in particular.
+     */
+    if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
+        ALOGI("Unable to map cached %s", fileName);
+        goto bail;
+    }
+
+    if (locked) {
+        /* unlock the fd */
+        if (!dvmUnlockCachedDexFile(optFd)) {
+            /* uh oh -- this process needs to exit or we'll wedge the system */
+            ALOGE("Unable to unlock DEX file");
+            goto bail;
+        }
+        locked = false;
+    }
+
+    ALOGV("Successfully opened '%s'", fileName);
+
+    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
+    (*ppRawDexFile)->cacheFileName = cachedName;
+    (*ppRawDexFile)->pDvmDex = pDvmDex;
+    cachedName = NULL;      // don't free it below
+    result = 0;
+
+bail:
+    free(cachedName);
+    if (dexFd >= 0) {
+        close(dexFd);
+    }
+    if (optFd >= 0) {
+        if (locked)
+            (void) dvmUnlockCachedDexFile(optFd);
+        close(optFd);
+    }
+    return result;
+}
+
+/* See documentation comment in header. */
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
+{
+    DvmDex* pDvmDex = NULL;
+
+    if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
+        ALOGD("Unable to open raw DEX from array");
+        return -1;
+    }
+    assert(pDvmDex != NULL);
+
+    *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
+    (*ppRawDexFile)->pDvmDex = pDvmDex;
+
+    return 0;
+}
+
+/*
+ * Close a RawDexFile and free the struct.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile)
+{
+    if (pRawDexFile == NULL)
+        return;
+
+    dvmDexFileFree(pRawDexFile->pDvmDex);
+    free(pRawDexFile->cacheFileName);
+    free(pRawDexFile);
+}
diff --git a/vm/RawDexFile.h b/vm/RawDexFile.h
new file mode 100644
index 0000000..358c669
--- /dev/null
+++ b/vm/RawDexFile.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * This represents a "raw" unswapped, unoptimized DEX file.  We don't open
+ * them directly, except to create the optimized version that we tuck in
+ * the cache area.
+ */
+#ifndef DALVIK_RAWDEXFILE_H_
+#define DALVIK_RAWDEXFILE_H_
+
+/*
+ * Structure representing a "raw" DEX file, in its unswapped unoptimized
+ * state.
+ */
+struct RawDexFile {
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+};
+
+/*
+ * Open a raw ".dex" file, optimize it, and load it.
+ *
+ * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+    RawDexFile** ppDexFile, bool isBootstrap);
+
+/*
+ * Open a raw ".dex" file based on the given chunk of memory, and load
+ * it. The bytes are assumed to be owned by the caller for the
+ * purposes of memory management and further assumed to not be touched
+ * by the caller while the raw dex file remains open. The bytes *may*
+ * be modified as the result of issuing this call.
+ *
+ * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppDexFile);
+
+/*
+ * Free a RawDexFile structure, along with any associated structures.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile);
+
+/*
+ * Pry the DexFile out of a RawDexFile.
+ */
+INLINE DvmDex* dvmGetRawDexFileDex(RawDexFile* pRawDexFile) {
+    return pRawDexFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetRawDexFileCacheFileName(RawDexFile* pRawDexFile) {
+    return pRawDexFile->cacheFileName;
+}
+
+#endif  // DALVIK_RAWDEXFILE_H_
diff --git a/vm/ReconfigureDvm.mk b/vm/ReconfigureDvm.mk
new file mode 100644
index 0000000..9f19407
--- /dev/null
+++ b/vm/ReconfigureDvm.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2009 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.
+
+include $(CLEAR_VARS)
+
+# Variables used in the included Dvm.mk.
+dvm_os := $(TARGET_OS)
+ifndef TARGET_2ND_ARCH
+dvm_arch := $(TARGET_ARCH)
+dvm_arch_variant := $(TARGET_ARCH_VARIANT)
+else
+# Dalvik doesn't support 64-bit architectures, fall back to the 32-bit 2nd arch
+dvm_arch := $(TARGET_2ND_ARCH)
+dvm_arch_variant := $(TARGET_2ND_ARCH_VARIANT)
+endif
+
+include $(LOCAL_PATH)/Dvm.mk
+
+LOCAL_SHARED_LIBRARIES += \
+	libbacktrace \
+	libcutils \
+	libdl \
+	liblog \
+	libnativehelper \
+	libselinux \
+	libutils \
+	libz
+
+LOCAL_STATIC_LIBRARIES += libdex
+
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+
+# Don't install on any build by default
+LOCAL_MODULE_TAGS := optional
diff --git a/vm/ReferenceTable.cpp b/vm/ReferenceTable.cpp
new file mode 100644
index 0000000..24180d4
--- /dev/null
+++ b/vm/ReferenceTable.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Reference table management.
+ */
+#include "Dalvik.h"
+
+/*
+ * Initialize a ReferenceTable structure.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+    int maxCount)
+{
+    assert(initialCount > 0);
+    assert(initialCount <= maxCount);
+
+    pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
+    if (pRef->table == NULL)
+        return false;
+#ifndef NDEBUG
+    memset(pRef->table, 0xdd, initialCount * sizeof(Object*));
+#endif
+    pRef->nextEntry = pRef->table;
+    pRef->allocEntries = initialCount;
+    pRef->maxEntries = maxCount;
+
+    return true;
+}
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef)
+{
+    free(pRef->table);
+    pRef->table = pRef->nextEntry = NULL;
+    pRef->allocEntries = pRef->maxEntries = -1;
+}
+
+/*
+ * Add "obj" to "pRef".
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
+{
+    assert(obj != NULL);
+    assert(dvmIsHeapAddress(obj));
+    assert(pRef->table != NULL);
+    assert(pRef->allocEntries <= pRef->maxEntries);
+
+    if (pRef->nextEntry == pRef->table + pRef->allocEntries) {
+        /* reached end of allocated space; did we hit buffer max? */
+        if (pRef->nextEntry == pRef->table + pRef->maxEntries) {
+            ALOGW("ReferenceTable overflow (max=%d)", pRef->maxEntries);
+            return false;
+        }
+
+        Object** newTable;
+        int newSize;
+
+        newSize = pRef->allocEntries * 2;
+        if (newSize > pRef->maxEntries)
+            newSize = pRef->maxEntries;
+        assert(newSize > pRef->allocEntries);
+
+        newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
+        if (newTable == NULL) {
+            ALOGE("Unable to expand ref table (from %d to %d %d-byte entries)",
+                pRef->allocEntries, newSize, sizeof(Object*));
+            return false;
+        }
+        LOGVV("Growing %p from %d to %d", pRef, pRef->allocEntries, newSize);
+
+        /* update entries; adjust "nextEntry" in case memory moved */
+        pRef->nextEntry = newTable + (pRef->nextEntry - pRef->table);
+        pRef->table = newTable;
+        pRef->allocEntries = newSize;
+    }
+
+    *pRef->nextEntry++ = obj;
+    return true;
+}
+
+/*
+ * Returns NULL if not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+    Object* obj)
+{
+    Object** ptr;
+
+    ptr = pRef->nextEntry;
+    while (--ptr >= bottom) {
+        if (*ptr == obj)
+            return ptr;
+    }
+    return NULL;
+}
+
+/*
+ * Remove "obj" from "pRef".  We start at the end of the list (where the
+ * most-recently-added element is), and stop searching for a match after
+ * examining the element at "bottom".
+ *
+ * Most of the time "obj" is at or near the end of the list.  If not, we
+ * compact it down.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+    Object* obj)
+{
+    Object** ptr;
+
+    assert(pRef->table != NULL);
+
+    /*
+     * Scan from the most-recently-added entry up to the bottom entry for
+     * this frame.
+     */
+    ptr = dvmFindInReferenceTable(pRef, bottom, obj);
+    if (ptr == NULL)
+        return false;
+
+    /*
+     * Delete the entry.
+     */
+    pRef->nextEntry--;
+    int moveCount = pRef->nextEntry - ptr;
+    if (moveCount != 0) {
+        /* remove from middle, slide the rest down */
+        memmove(ptr, ptr+1, moveCount * sizeof(Object*));
+        //ALOGV("LREF delete %p, shift %d down", obj, moveCount);
+    } else {
+        /* last entry, falls off the end */
+        //ALOGV("LREF delete %p from end", obj);
+    }
+
+    return true;
+}
+
+/*
+ * If "obj" is an array, return the number of elements in the array.
+ * Otherwise, return zero.
+ */
+static size_t getElementCount(const Object* obj)
+{
+    const ArrayObject* arrayObj = (ArrayObject*) obj;
+    if (arrayObj == NULL || arrayObj == kClearedJniWeakGlobal ||
+            arrayObj->clazz == NULL || !dvmIsArray(arrayObj)) {
+        return 0;
+    }
+    return arrayObj->length;
+}
+
+/*
+ * This is a qsort() callback.  We sort Object* by class, allocation size,
+ * and then by the Object* itself.
+ */
+static int compareObject(const void* vobj1, const void* vobj2)
+{
+    const Object* obj1 = *((Object* const*) vobj1);
+    const Object* obj2 = *((Object* const*) vobj2);
+
+    // Ensure null references and cleared jweaks appear at the end.
+    if (obj1 == NULL) {
+        if (obj2 == NULL) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else if (obj2 == NULL) {
+        return -1;
+    }
+    if (obj1 == kClearedJniWeakGlobal) {
+        if (obj2 == kClearedJniWeakGlobal) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else if (obj2 == kClearedJniWeakGlobal) {
+        return -1;
+    }
+
+    if (obj1->clazz != obj2->clazz) {
+        return (u1*)obj1->clazz - (u1*)obj2->clazz;
+    } else {
+        size_t count1 = getElementCount(obj1);
+        size_t count2 = getElementCount(obj2);
+        if (count1 != count2) {
+            return count1 - count2;
+        } else {
+            return (u1*)obj1 - (u1*)obj2;
+        }
+    }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of elements in the array (or 0 if this is not an
+ * array object), and the number of additional objects that are identical
+ * or equivalent to the original.
+ */
+static void logSummaryLine(const Object* obj, size_t elems, int identical, int equiv)
+{
+    if (obj == NULL) {
+        ALOGW("    NULL reference (count=%d)", equiv);
+        return;
+    }
+    if (obj == kClearedJniWeakGlobal) {
+        ALOGW("    cleared jweak (count=%d)", equiv);
+        return;
+    }
+
+    std::string className(dvmHumanReadableType(obj));
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        // We're summarizing multiple instances, so using the exemplar
+        // Class' type parameter here would be misleading.
+        className = "java.lang.Class";
+    }
+    if (elems != 0) {
+        StringAppendF(&className, " (%zd elements)", elems);
+    }
+
+    size_t total = identical + equiv + 1;
+    std::string msg(StringPrintf("%5d of %s", total, className.c_str()));
+    if (identical + equiv != 0) {
+        StringAppendF(&msg, " (%d unique instances)", equiv + 1);
+    }
+    ALOGW("    %s", msg.c_str());
+}
+
+/*
+ * Dump a summary of an array of references to the log file.
+ *
+ * This is used to dump the contents of ReferenceTable and IndirectRefTable
+ * structs.
+ */
+void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
+    const char* descr)
+{
+    ALOGW("%s reference table (%p) dump:", descr, refs);
+
+    if (count == 0) {
+        ALOGW("  (empty)");
+        return;
+    }
+
+    // Dump the most recent N entries.
+    const size_t kLast = 10;
+    int first = count - kLast;
+    if (first < 0) {
+        first = 0;
+    }
+    ALOGW("  Last %d entries (of %d):", (count - first), count);
+    for (int idx = count - 1; idx >= first; --idx) {
+        const Object* ref = refs[idx];
+        if (ref == NULL) {
+            continue;
+        }
+        if (ref == kClearedJniWeakGlobal) {
+            ALOGW("    %5d: cleared jweak", idx);
+            continue;
+        }
+        if (ref->clazz == NULL) {
+            // should only be possible right after a plain dvmMalloc().
+            size_t size = dvmObjectSizeInHeap(ref);
+            ALOGW("    %5d: %p (raw) (%zd bytes)", idx, ref, size);
+            continue;
+        }
+
+        std::string className(dvmHumanReadableType(ref));
+
+        std::string extras;
+        size_t elems = getElementCount(ref);
+        if (elems != 0) {
+            StringAppendF(&extras, " (%zd elements)", elems);
+        } else if (ref->clazz == gDvm.classJavaLangString) {
+            const StringObject* str =
+                    reinterpret_cast<const StringObject*>(ref);
+            extras += " \"";
+            size_t count = 0;
+            char* s = dvmCreateCstrFromString(str);
+            char* p = s;
+            for (; *p && count < 16; ++p, ++count) {
+                extras += *p;
+            }
+            if (*p == 0) {
+                extras += "\"";
+            } else {
+                StringAppendF(&extras, "... (%d chars)", str->length());
+            }
+            free(s);
+        }
+        ALOGW("    %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str());
+    }
+
+    // Make a copy of the table, and sort it.
+    Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+    if (tableCopy == NULL) {
+        ALOGE("Unable to copy table with %d elements", count);
+        return;
+    }
+    memcpy(tableCopy, refs, sizeof(Object*) * count);
+    qsort(tableCopy, count, sizeof(Object*), compareObject);
+    refs = tableCopy;       // use sorted list
+
+    // Remove any uninteresting stuff from the list. The sort moved them all to the end.
+    while (count > 0 && refs[count-1] == NULL) {
+        --count;
+    }
+    while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) {
+        --count;
+    }
+    if (count == 0) {
+        return;
+    }
+
+    // Dump a summary of the whole table.
+    ALOGW("  Summary:");
+    size_t equiv, identical;
+    equiv = identical = 0;
+    size_t idx;
+    size_t elems;
+    for (idx = 1; idx < count; idx++) {
+        elems = getElementCount(refs[idx-1]);
+
+        if (refs[idx] == refs[idx-1]) {
+            // same reference, added more than once.
+            identical++;
+        } else if (refs[idx]->clazz == refs[idx-1]->clazz &&
+            getElementCount(refs[idx]) == elems)
+        {
+            // same class / element count, different object.
+            equiv++;
+        } else {
+            // different class.
+            logSummaryLine(refs[idx-1], elems, identical, equiv);
+            equiv = identical = 0;
+        }
+    }
+
+    // Handle the last entry (everything above outputs refs[i-1]).
+    elems = getElementCount(refs[idx-1]);
+    logSummaryLine(refs[count-1], elems, identical, equiv);
+
+    free(tableCopy);
+}
+
+/*
+ * Dump the contents of a ReferenceTable to the log.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
+{
+    dvmDumpReferenceTableContents(pRef->table, dvmReferenceTableEntries(pRef),
+        descr);
+}
diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h
new file mode 100644
index 0000000..100a918
--- /dev/null
+++ b/vm/ReferenceTable.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Maintain a table of references.  Used for internal local references,
+ * JNI monitor references, and JNI pinned array references.
+ *
+ * None of the table functions are synchronized.
+ */
+#ifndef DALVIK_REFERENCETABLE_H_
+#define DALVIK_REFERENCETABLE_H_
+
+/*
+ * Table definition.
+ *
+ * The expected common operations are adding a new entry and removing a
+ * recently-added entry (usually the most-recently-added entry).
+ *
+ * If "allocEntries" is not equal to "maxEntries", the table may expand when
+ * entries are added, which means the memory may move.  If you want to keep
+ * pointers into "table" rather than offsets, use a fixed-size table.
+ *
+ * (This structure is still somewhat transparent; direct access to
+ * table/nextEntry is allowed.)
+ */
+struct ReferenceTable {
+    Object**        nextEntry;          /* top of the list */
+    Object**        table;              /* bottom of the list */
+
+    int             allocEntries;       /* #of entries we have space for */
+    int             maxEntries;         /* max #of entries allowed */
+};
+
+/*
+ * Initialize a ReferenceTable.
+ *
+ * If "initialCount" != "maxCount", the table will expand as required.
+ *
+ * Returns "false" if table allocation fails.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+    int maxCount);
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ * Does not free "pRef".
+ *
+ * You must call dvmInitReferenceTable() before you can re-use this table.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef);
+
+/*
+ * Return the #of entries currently stored in the ReferenceTable.
+ */
+INLINE size_t dvmReferenceTableEntries(const ReferenceTable* pRef)
+{
+    return pRef->nextEntry - pRef->table;
+}
+
+/*
+ * Returns "true" if the table is full.  The table is considered full if
+ * we would need to expand it to add another entry.
+ */
+INLINE size_t dvmIsReferenceTableFull(const ReferenceTable* pRef)
+{
+    return dvmReferenceTableEntries(pRef) == (size_t)pRef->allocEntries;
+}
+
+/*
+ * Add a new entry.  "obj" must be a valid non-NULL object reference
+ * (though it's okay if it's not fully-formed, e.g. the result from
+ * dvmMalloc doesn't have obj->clazz set).
+ *
+ * Returns "false" if the table is full.
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj);
+
+/*
+ * Determine if "obj" is present in "pRef".  Stops searching when we hit
+ * "bottom".  To include the entire table, pass in "pRef->table" as the
+ * bottom.
+ *
+ * Returns NULL if "obj" was not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+    Object* obj);
+
+/*
+ * Remove an existing entry.
+ *
+ * We stop searching for a match after examining the element at "bottom".
+ * This is useful when entries are associated with a stack frame.
+ *
+ * Returns "false" if the entry was not found.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+    Object* obj);
+
+/*
+ * Dump the contents of a reference table to the log file.
+ *
+ * The caller should lock any external sync before calling.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr);
+
+/*
+ * Internal function, shared with IndirectRefTable.
+ */
+void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
+    const char* descr);
+
+#endif  // DALVIK_REFERENCETABLE_H_
diff --git a/vm/SignalCatcher.cpp b/vm/SignalCatcher.cpp
new file mode 100644
index 0000000..d4302aa
--- /dev/null
+++ b/vm/SignalCatcher.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * This is a thread that catches signals and does something useful.  For
+ * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the
+ * status of all threads.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <cutils/open_memstream.h>
+
+static void* signalCatcherThreadStart(void* arg);
+
+/*
+ * Crank up the signal catcher thread.
+ *
+ * Returns immediately.
+ */
+bool dvmSignalCatcherStartup()
+{
+    gDvm.haltSignalCatcher = false;
+
+    if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle,
+                "Signal Catcher", signalCatcherThreadStart, NULL))
+        return false;
+
+    return true;
+}
+
+/*
+ * Shut down the signal catcher thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for signals
+ * to arrive, send it one.
+ */
+void dvmSignalCatcherShutdown()
+{
+    gDvm.haltSignalCatcher = true;
+    if (gDvm.signalCatcherHandle == 0)      // not started yet
+        return;
+
+    pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
+
+    pthread_join(gDvm.signalCatcherHandle, NULL);
+    ALOGV("signal catcher has shut down");
+}
+
+
+/*
+ * Print the name of the current process, if we can get it.
+ */
+static void printProcessName(const DebugOutputTarget* target)
+{
+    int fd = -1;
+
+    fd = open("/proc/self/cmdline", O_RDONLY, 0);
+    if (fd < 0)
+        goto bail;
+
+    char tmpBuf[256];
+    ssize_t actual;
+
+    actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
+    if (actual <= 0)
+        goto bail;
+
+    tmpBuf[actual] = '\0';
+    dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);
+
+bail:
+    if (fd >= 0)
+        close(fd);
+}
+
+/*
+ * Dump the stack traces for all threads to the supplied file, putting
+ * a timestamp header on it.
+ */
+static void logThreadStacks(FILE* fp)
+{
+    DebugOutputTarget target;
+
+    dvmCreateFileOutputTarget(&target, fp);
+
+    pid_t pid = getpid();
+    time_t now = time(NULL);
+    struct tm* ptm;
+#ifdef HAVE_LOCALTIME_R
+    struct tm tmbuf;
+    ptm = localtime_r(&now, &tmbuf);
+#else
+    ptm = localtime(&now);
+#endif
+    dvmPrintDebugMessage(&target,
+        "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
+        pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+    printProcessName(&target);
+    dvmPrintDebugMessage(&target, "\n");
+    dvmDumpJniStats(&target);
+    dvmDumpAllThreadsEx(&target, true);
+    fprintf(fp, "----- end %d -----\n", pid);
+}
+
+
+/*
+ * Respond to a SIGQUIT by dumping the thread stacks.  Optionally dump
+ * a few other things while we're at it.
+ *
+ * Thread stacks can either go to the log or to a file designated for holding
+ * ANR traces.  If we're writing to a file, we want to do it in one shot,
+ * so we can use a single O_APPEND write instead of contending for exclusive
+ * access with flock().  There may be an advantage in resuming the VM
+ * before doing the file write, so we don't stall the VM if disk I/O is
+ * bottlenecked.
+ *
+ * If JIT tuning is compiled in, dump compiler stats as well.
+ */
+static void handleSigQuit()
+{
+    char* traceBuf = NULL;
+    size_t traceLen;
+
+    dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+    dvmDumpLoaderStats("sig");
+
+    if (gDvm.stackTraceFile == NULL) {
+        /* just dump to log */
+        DebugOutputTarget target;
+        dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+        dvmDumpJniStats(&target);
+        dvmDumpAllThreadsEx(&target, true);
+    } else {
+        /* write to memory buffer */
+        FILE* memfp = open_memstream(&traceBuf, &traceLen);
+        if (memfp == NULL) {
+            ALOGE("Unable to create memstream for stack traces");
+            traceBuf = NULL;        /* make sure it didn't touch this */
+            /* continue on */
+        } else {
+            logThreadStacks(memfp);
+            fclose(memfp);
+        }
+    }
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    dvmCompilerDumpStats();
+#endif
+
+    if (false) dvmDumpTrackedAllocations(true);
+
+    dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+    if (traceBuf != NULL) {
+        /*
+         * We don't know how long it will take to do the disk I/O, so put us
+         * into VMWAIT for the duration.
+         */
+        ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
+
+        /*
+         * Open the stack trace output file, creating it if necessary.  It
+         * needs to be world-writable so other processes can write to it.
+         */
+        int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
+        if (fd < 0) {
+            ALOGE("Unable to open stack trace file '%s': %s",
+                gDvm.stackTraceFile, strerror(errno));
+        } else {
+            ssize_t actual = TEMP_FAILURE_RETRY(write(fd, traceBuf, traceLen));
+            if (actual != (ssize_t) traceLen) {
+                ALOGE("Failed to write stack traces to %s (%d of %zd): %s",
+                    gDvm.stackTraceFile, (int) actual, traceLen,
+                    strerror(errno));
+            } else {
+                ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
+            }
+            close(fd);
+        }
+
+        free(traceBuf);
+        dvmChangeStatus(dvmThreadSelf(), oldStatus);
+    }
+}
+
+/*
+ * Respond to a SIGUSR1 by forcing a GC.
+ */
+static void handleSigUsr1()
+{
+    ALOGI("SIGUSR1 forcing GC (no HPROF)");
+    dvmCollectGarbage();
+}
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+/* Sample callback function for dvmJitScanAllClassPointers */
+void printAllClass(void *ptr)
+{
+    ClassObject **classPP = (ClassObject **) ptr;
+    ALOGE("class %s", (*classPP)->descriptor);
+
+}
+
+/*
+ * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
+ * the code cache.
+ */
+static void handleSigUsr2()
+{
+    static int codeCacheResetCount = 0;
+    gDvmJit.receivedSIGUSR2 ^= true;
+    if ((--codeCacheResetCount & 7) == 0) {
+        /* Dump all class pointers in the traces */
+        dvmJitScanAllClassPointers(printAllClass);
+        gDvmJit.codeCacheFull = true;
+    } else {
+        dvmCompilerDumpStats();
+        /* Stress-test unchain all */
+        dvmJitUnchainAll();
+        ALOGD("Send %d more signals to reset the code cache",
+             codeCacheResetCount & 7);
+    }
+    dvmCheckInterpStateConsistency();
+}
+#endif
+
+/*
+ * Sleep in sigwait() until a signal arrives.
+ */
+static void* signalCatcherThreadStart(void* arg)
+{
+    Thread* self = dvmThreadSelf();
+    sigset_t mask;
+    int cc;
+
+    UNUSED_PARAMETER(arg);
+
+    ALOGV("Signal catcher thread started (threadid=%d)", self->threadId);
+
+    /* set up mask with signals we want to handle */
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGQUIT);
+    sigaddset(&mask, SIGUSR1);
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    sigaddset(&mask, SIGUSR2);
+#endif
+
+    while (true) {
+        int rcvd;
+
+        dvmChangeStatus(self, THREAD_VMWAIT);
+
+        /*
+         * Signals for sigwait() must be blocked but not ignored.  We
+         * block signals like SIGQUIT for all threads, so the condition
+         * is met.  When the signal hits, we wake up, without any signal
+         * handlers being invoked.
+         *
+         * When running under GDB we occasionally return from sigwait()
+         * with EINTR (e.g. when other threads exit).
+         */
+loop:
+        cc = sigwait(&mask, &rcvd);
+        if (cc != 0) {
+            if (cc == EINTR) {
+                //ALOGV("sigwait: EINTR");
+                goto loop;
+            }
+            assert(!"bad result from sigwait");
+        }
+
+        if (!gDvm.haltSignalCatcher) {
+            ALOGI("threadid=%d: reacting to signal %d",
+                dvmThreadSelf()->threadId, rcvd);
+        }
+
+        /* set our status to RUNNING, self-suspending if GC in progress */
+        dvmChangeStatus(self, THREAD_RUNNING);
+
+        if (gDvm.haltSignalCatcher)
+            break;
+
+        switch (rcvd) {
+        case SIGQUIT:
+            handleSigQuit();
+            break;
+        case SIGUSR1:
+            handleSigUsr1();
+            break;
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+        case SIGUSR2:
+            handleSigUsr2();
+            break;
+#endif
+        default:
+            ALOGE("unexpected signal %d", rcvd);
+            break;
+        }
+    }
+
+    return NULL;
+}
diff --git a/vm/SignalCatcher.h b/vm/SignalCatcher.h
new file mode 100644
index 0000000..d1d7a18
--- /dev/null
+++ b/vm/SignalCatcher.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Signal catcher thread.
+ */
+#ifndef DALVIK_SIGNALCATCHER_H_
+#define DALVIK_SIGNALCATCHER_H_
+
+bool dvmSignalCatcherStartup(void);
+void dvmSignalCatcherShutdown(void);
+
+#endif  // DALVIK_SIGNALCATCHER_H_
diff --git a/vm/StdioConverter.cpp b/vm/StdioConverter.cpp
new file mode 100644
index 0000000..f420c4d
--- /dev/null
+++ b/vm/StdioConverter.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Thread that reads from stdout/stderr and converts them to log messages.
+ * (Sort of a hack.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define kFilenoStdout   1
+#define kFilenoStderr   2
+
+#define kMaxLine    512
+
+/*
+ * Hold some data.
+ */
+struct BufferedData {
+    char    buf[kMaxLine+1];
+    int     count;
+};
+
+// fwd
+static void* stdioConverterThreadStart(void* arg);
+static bool readAndLog(int fd, BufferedData* data, const char* tag);
+
+
+/*
+ * Crank up the stdout/stderr converter thread.
+ *
+ * Returns immediately.
+ */
+bool dvmStdioConverterStartup()
+{
+    gDvm.haltStdioConverter = false;
+
+    dvmInitMutex(&gDvm.stdioConverterLock);
+    pthread_cond_init(&gDvm.stdioConverterCond, NULL);
+
+    if (pipe(gDvm.stdoutPipe) != 0) {
+        ALOGW("pipe failed: %s", strerror(errno));
+        return false;
+    }
+    if (pipe(gDvm.stderrPipe) != 0) {
+        ALOGW("pipe failed: %s", strerror(errno));
+        return false;
+    }
+
+    if (dup2(gDvm.stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
+        ALOGW("dup2(1) failed: %s", strerror(errno));
+        return false;
+    }
+    close(gDvm.stdoutPipe[1]);
+    gDvm.stdoutPipe[1] = -1;
+#ifdef HAVE_ANDROID_OS
+    /* don't redirect stderr on sim -- logs get written there! */
+    /* (don't need this on the sim anyway) */
+    if (dup2(gDvm.stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
+        ALOGW("dup2(2) failed: %d %s", errno, strerror(errno));
+        return false;
+    }
+    close(gDvm.stderrPipe[1]);
+    gDvm.stderrPipe[1] = -1;
+#endif
+
+
+    /*
+     * Create the thread.
+     */
+    dvmLockMutex(&gDvm.stdioConverterLock);
+
+    if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
+                                 "Stdio Converter",
+                                 stdioConverterThreadStart,
+                                 NULL)) {
+        return false;
+    }
+
+    while (!gDvm.stdioConverterReady) {
+        dvmWaitCond(&gDvm.stdioConverterCond, &gDvm.stdioConverterLock);
+    }
+    dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+    return true;
+}
+
+/*
+ * Shut down the stdio converter thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for something
+ * to arrive on stdout, print something.
+ */
+void dvmStdioConverterShutdown()
+{
+    gDvm.haltStdioConverter = true;
+    if (gDvm.stdioConverterHandle == 0)    // not started, or still starting
+        return;
+
+    /* print something to wake it up */
+    printf("Shutting down\n");
+    fflush(stdout);
+
+    ALOGD("Joining stdio converter...");
+    pthread_join(gDvm.stdioConverterHandle, NULL);
+}
+
+/*
+ * Select on stdout/stderr pipes, waiting for activity.
+ *
+ * DO NOT use printf from here.
+ */
+static void* stdioConverterThreadStart(void* arg)
+{
+    int cc;
+
+    /* tell the main thread that we're ready */
+    dvmLockMutex(&gDvm.stdioConverterLock);
+    gDvm.stdioConverterReady = true;
+    cc = pthread_cond_signal(&gDvm.stdioConverterCond);
+    assert(cc == 0);
+    dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+    /* we never do anything that affects the rest of the VM */
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+    /*
+     * Allocate read buffers.
+     */
+    BufferedData* stdoutData = new BufferedData;
+    BufferedData* stderrData = new BufferedData;
+    stdoutData->count = stderrData->count = 0;
+
+    /*
+     * Read until shutdown time.
+     */
+    while (!gDvm.haltStdioConverter) {
+        fd_set readfds;
+        int maxFd, fdCount;
+
+        FD_ZERO(&readfds);
+        FD_SET(gDvm.stdoutPipe[0], &readfds);
+        FD_SET(gDvm.stderrPipe[0], &readfds);
+        maxFd = MAX(gDvm.stdoutPipe[0], gDvm.stderrPipe[0]);
+
+        fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
+
+        if (fdCount < 0) {
+            if (errno != EINTR) {
+                ALOGE("select on stdout/stderr failed");
+                break;
+            }
+            ALOGD("Got EINTR, ignoring");
+        } else if (fdCount == 0) {
+            ALOGD("WEIRD: select returned zero");
+        } else {
+            bool err = false;
+            if (FD_ISSET(gDvm.stdoutPipe[0], &readfds)) {
+                err |= !readAndLog(gDvm.stdoutPipe[0], stdoutData,
+                    "stdout");
+            }
+            if (FD_ISSET(gDvm.stderrPipe[0], &readfds)) {
+                err |= !readAndLog(gDvm.stderrPipe[0], stderrData,
+                    "stderr");
+            }
+
+            /* probably EOF; give up */
+            if (err) {
+                ALOGW("stdio converter got read error; shutting it down");
+                break;
+            }
+        }
+    }
+
+    close(gDvm.stdoutPipe[0]);
+    close(gDvm.stderrPipe[0]);
+
+    delete stdoutData;
+    delete stderrData;
+
+    /* change back for shutdown sequence */
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+    return NULL;
+}
+
+/*
+ * Data is pending on "fd".  Read as much as will fit in "data", then
+ * write out any full lines and compact "data".
+ */
+static bool readAndLog(int fd, BufferedData* data, const char* tag)
+{
+    ssize_t actual;
+    size_t want;
+
+    assert(data->count < kMaxLine);
+
+    want = kMaxLine - data->count;
+    actual = read(fd, data->buf + data->count, want);
+    if (actual <= 0) {
+        ALOGW("read %s: (%d,%d) failed (%d): %s",
+            tag, fd, want, (int)actual, strerror(errno));
+        return false;
+    } else {
+        //ALOGI("read %s: %d at %d", tag, actual, data->count);
+    }
+    data->count += actual;
+
+    /*
+     * Got more data, look for an EOL.  We expect LF or CRLF, but will
+     * try to handle a standalone CR.
+     */
+    char* cp = data->buf;
+    const char* start = data->buf;
+    int i = data->count;
+    for (i = data->count; i > 0; i--, cp++) {
+        if (*cp == '\n' || (*cp == '\r' && i != 0 && *(cp+1) != '\n')) {
+            *cp = '\0';
+            //ALOGW("GOT %d at %d '%s'", cp - start, start - data->buf, start);
+            ALOG(LOG_INFO, tag, "%s", start);
+            start = cp+1;
+        }
+    }
+
+    /*
+     * See if we overflowed.  If so, cut it off.
+     */
+    if (start == data->buf && data->count == kMaxLine) {
+        data->buf[kMaxLine] = '\0';
+        ALOG(LOG_INFO, tag, "%s!", start);
+        start = cp + kMaxLine;
+    }
+
+    /*
+     * Update "data" if we consumed some output.  If there's anything left
+     * in the buffer, it's because we didn't see an EOL and need to keep
+     * reading until we see one.
+     */
+    if (start != data->buf) {
+        if (start >= data->buf + data->count) {
+            /* consumed all available */
+            data->count = 0;
+        } else {
+            /* some left over */
+            int remaining = data->count - (start - data->buf);
+            memmove(data->buf, start, remaining);
+            data->count = remaining;
+        }
+    }
+
+    return true;
+}
diff --git a/vm/StdioConverter.h b/vm/StdioConverter.h
new file mode 100644
index 0000000..bd5d879
--- /dev/null
+++ b/vm/StdioConverter.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Stdout/stderr conversion thread.
+ */
+#ifndef DALVIK_STDOUTCONVERTER_H_
+#define DALVIK_STDOUTCONVERTER_H_
+
+bool dvmStdioConverterStartup(void);
+void dvmStdioConverterShutdown(void);
+
+#endif  // DALVIK_STDOUTCONVERTER_H_
diff --git a/vm/Sync.cpp b/vm/Sync.cpp
new file mode 100644
index 0000000..c9a32d4
--- /dev/null
+++ b/vm/Sync.cpp
@@ -0,0 +1,1382 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <errno.h>
+
+/*
+ * Every Object has a monitor associated with it, but not every Object is
+ * actually locked.  Even the ones that are locked do not need a
+ * full-fledged monitor until a) there is actual contention or b) wait()
+ * is called on the Object.
+ *
+ * For Dalvik, we have implemented a scheme similar to the one described
+ * in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
+ * (ACM 1998).  Things are even easier for us, though, because we have
+ * a full 32 bits to work with.
+ *
+ * The two states of an Object's lock are referred to as "thin" and
+ * "fat".  A lock may transition from the "thin" state to the "fat"
+ * state and this transition is referred to as inflation.  Once a lock
+ * has been inflated it remains in the "fat" state indefinitely.
+ *
+ * The lock value itself is stored in Object.lock.  The LSB of the
+ * lock encodes its state.  When cleared, the lock is in the "thin"
+ * state and its bits are formatted as follows:
+ *
+ *    [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
+ *     lock count   thread id  hash state  0
+ *
+ * When set, the lock is in the "fat" state and its bits are formatted
+ * as follows:
+ *
+ *    [31 ---- 3] [2 ---- 1] [0]
+ *      pointer   hash state  1
+ *
+ * For an in-depth description of the mechanics of thin-vs-fat locking,
+ * read the paper referred to above.
+ */
+
+/*
+ * Monitors provide:
+ *  - mutually exclusive access to resources
+ *  - a way for multiple threads to wait for notification
+ *
+ * In effect, they fill the role of both mutexes and condition variables.
+ *
+ * Only one thread can own the monitor at any time.  There may be several
+ * threads waiting on it (the wait call unlocks it).  One or more waiting
+ * threads may be getting interrupted or notified at any given time.
+ *
+ * TODO: the various members of monitor are not SMP-safe.
+ */
+struct Monitor {
+    Thread*     owner;          /* which thread currently owns the lock? */
+    int         lockCount;      /* owner's recursive lock depth */
+    Object*     obj;            /* what object are we part of [debug only] */
+
+    Thread*     waitSet;	/* threads currently waiting on this monitor */
+
+    pthread_mutex_t lock;
+
+    Monitor*    next;
+
+    /*
+     * Who last acquired this monitor, when lock sampling is enabled.
+     * Even when enabled, ownerMethod may be NULL.
+     */
+    const Method* ownerMethod;
+    u4 ownerPc;
+};
+
+
+/*
+ * Create and initialize a monitor.
+ */
+Monitor* dvmCreateMonitor(Object* obj)
+{
+    Monitor* mon;
+
+    mon = (Monitor*) calloc(1, sizeof(Monitor));
+    if (mon == NULL) {
+        ALOGE("Unable to allocate monitor");
+        dvmAbort();
+    }
+    mon->obj = obj;
+    dvmInitMutex(&mon->lock);
+
+    /* replace the head of the list with the new monitor */
+    do {
+        mon->next = gDvm.monitorList;
+    } while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon,
+            (int32_t*)(void*)&gDvm.monitorList) != 0);
+
+    return mon;
+}
+
+/*
+ * Free the monitor list.  Only used when shutting the VM down.
+ */
+void dvmFreeMonitorList()
+{
+    Monitor* mon;
+    Monitor* nextMon;
+
+    mon = gDvm.monitorList;
+    while (mon != NULL) {
+        nextMon = mon->next;
+        free(mon);
+        mon = nextMon;
+    }
+}
+
+/*
+ * Get the object that a monitor is part of.
+ */
+Object* dvmGetMonitorObject(Monitor* mon)
+{
+    if (mon == NULL)
+        return NULL;
+    else
+        return mon->obj;
+}
+
+/*
+ * Returns the thread id of the thread owning the given lock.
+ */
+static u4 lockOwner(Object* obj)
+{
+    Thread *owner;
+    u4 lock;
+
+    assert(obj != NULL);
+    /*
+     * Since we're reading the lock value multiple times, latch it so
+     * that it doesn't change out from under us if we get preempted.
+     */
+    lock = obj->lock;
+    if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+        return LW_LOCK_OWNER(lock);
+    } else {
+        owner = LW_MONITOR(lock)->owner;
+        return owner ? owner->threadId : 0;
+    }
+}
+
+/*
+ * Get the thread that holds the lock on the specified object.  The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+Thread* dvmGetObjectLockHolder(Object* obj)
+{
+    u4 threadId = lockOwner(obj);
+
+    if (threadId == 0)
+        return NULL;
+    return dvmGetThreadByThreadId(threadId);
+}
+
+/*
+ * Checks whether the given thread holds the given
+ * objects's lock.
+ */
+bool dvmHoldsLock(Thread* thread, Object* obj)
+{
+    if (thread == NULL || obj == NULL) {
+        return false;
+    } else {
+        return thread->threadId == lockOwner(obj);
+    }
+}
+
+/*
+ * Free the monitor associated with an object and make the object's lock
+ * thin again.  This is called during garbage collection.
+ */
+static void freeMonitor(Monitor *mon)
+{
+    assert(mon != NULL);
+    assert(mon->obj != NULL);
+    assert(LW_SHAPE(mon->obj->lock) == LW_SHAPE_FAT);
+
+    /* This lock is associated with an object
+     * that's being swept.  The only possible way
+     * anyone could be holding this lock would be
+     * if some JNI code locked but didn't unlock
+     * the object, in which case we've got some bad
+     * native code somewhere.
+     */
+    assert(pthread_mutex_trylock(&mon->lock) == 0);
+    assert(pthread_mutex_unlock(&mon->lock) == 0);
+    dvmDestroyMutex(&mon->lock);
+    free(mon);
+}
+
+/*
+ * Frees monitor objects belonging to unmarked objects.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*))
+{
+    Monitor handle;
+    Monitor *prev, *curr;
+    Object *obj;
+
+    assert(mon != NULL);
+    assert(isUnmarkedObject != NULL);
+    prev = &handle;
+    prev->next = curr = *mon;
+    while (curr != NULL) {
+        obj = curr->obj;
+        if (obj != NULL && (*isUnmarkedObject)(obj) != 0) {
+            prev->next = curr->next;
+            freeMonitor(curr);
+            curr = prev->next;
+        } else {
+            prev = curr;
+            curr = curr->next;
+        }
+    }
+    *mon = handle.next;
+}
+
+static char *logWriteInt(char *dst, int value)
+{
+    *dst++ = EVENT_TYPE_INT;
+    set4LE((u1 *)dst, value);
+    return dst + 4;
+}
+
+static char *logWriteString(char *dst, const char *value, size_t len)
+{
+    *dst++ = EVENT_TYPE_STRING;
+    len = len < 32 ? len : 32;
+    set4LE((u1 *)dst, len);
+    dst += 4;
+    memcpy(dst, value, len);
+    return dst + len;
+}
+
+#define EVENT_LOG_TAG_dvm_lock_sample 20003
+
+static void logContentionEvent(Thread *self, u4 waitMs, u4 samplePercent,
+                               const char *ownerFileName, u4 ownerLineNumber)
+{
+    const StackSaveArea *saveArea;
+    const Method *meth;
+    u4 relativePc;
+    char eventBuffer[174];
+    const char *fileName;
+    char procName[33];
+    char *cp;
+    size_t len;
+    int fd;
+
+    /* When a thread is being destroyed it is normal that the frame depth is zero */
+    if (self->interpSave.curFrame == NULL) {
+        return;
+    }
+
+    saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    meth = saveArea->method;
+    cp = eventBuffer;
+
+    /* Emit the event list length, 1 byte. */
+    *cp++ = 9;
+
+    /* Emit the process name, <= 37 bytes. */
+    fd = open("/proc/self/cmdline", O_RDONLY);
+    memset(procName, 0, sizeof(procName));
+    read(fd, procName, sizeof(procName) - 1);
+    close(fd);
+    len = strlen(procName);
+    cp = logWriteString(cp, procName, len);
+
+    /* Emit the sensitive thread ("main thread") status, 5 bytes. */
+    bool isSensitive = false;
+    if (gDvm.isSensitiveThreadHook != NULL) {
+        isSensitive = gDvm.isSensitiveThreadHook();
+    }
+    cp = logWriteInt(cp, isSensitive);
+
+    /* Emit self thread name string, <= 37 bytes. */
+    std::string selfName = dvmGetThreadName(self);
+    cp = logWriteString(cp, selfName.c_str(), selfName.size());
+
+    /* Emit the wait time, 5 bytes. */
+    cp = logWriteInt(cp, waitMs);
+
+    /* Emit the source code file name, <= 37 bytes. */
+    fileName = dvmGetMethodSourceFile(meth);
+    if (fileName == NULL) fileName = "";
+    cp = logWriteString(cp, fileName, strlen(fileName));
+
+    /* Emit the source code line number, 5 bytes. */
+    relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
+    cp = logWriteInt(cp, dvmLineNumFromPC(meth, relativePc));
+
+    /* Emit the lock owner source code file name, <= 37 bytes. */
+    if (ownerFileName == NULL) {
+        ownerFileName = "";
+    } else if (strcmp(fileName, ownerFileName) == 0) {
+        /* Common case, so save on log space. */
+        ownerFileName = "-";
+    }
+    cp = logWriteString(cp, ownerFileName, strlen(ownerFileName));
+
+    /* Emit the source code line number, 5 bytes. */
+    cp = logWriteInt(cp, ownerLineNumber);
+
+    /* Emit the sample percentage, 5 bytes. */
+    cp = logWriteInt(cp, samplePercent);
+
+    assert((size_t)(cp - eventBuffer) <= sizeof(eventBuffer));
+    android_btWriteLog(EVENT_LOG_TAG_dvm_lock_sample,
+                       EVENT_TYPE_LIST,
+                       eventBuffer,
+                       (size_t)(cp - eventBuffer));
+}
+
+/*
+ * Lock a monitor.
+ */
+static void lockMonitor(Thread* self, Monitor* mon)
+{
+    ThreadStatus oldStatus;
+    u4 waitThreshold, samplePercent;
+    u8 waitStart, waitEnd, waitMs;
+
+    if (mon->owner == self) {
+        mon->lockCount++;
+        return;
+    }
+    if (dvmTryLockMutex(&mon->lock) != 0) {
+        oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+        waitThreshold = gDvm.lockProfThreshold;
+        if (waitThreshold) {
+            waitStart = dvmGetRelativeTimeUsec();
+        }
+
+        const Method* currentOwnerMethod = mon->ownerMethod;
+        u4 currentOwnerPc = mon->ownerPc;
+
+        dvmLockMutex(&mon->lock);
+        if (waitThreshold) {
+            waitEnd = dvmGetRelativeTimeUsec();
+        }
+        dvmChangeStatus(self, oldStatus);
+        if (waitThreshold) {
+            waitMs = (waitEnd - waitStart) / 1000;
+            if (waitMs >= waitThreshold) {
+                samplePercent = 100;
+            } else {
+                samplePercent = 100 * waitMs / waitThreshold;
+            }
+            if (samplePercent != 0 && ((u4)rand() % 100 < samplePercent)) {
+                const char* currentOwnerFileName = "no_method";
+                u4 currentOwnerLineNumber = 0;
+                if (currentOwnerMethod != NULL) {
+                    currentOwnerFileName = dvmGetMethodSourceFile(currentOwnerMethod);
+                    if (currentOwnerFileName == NULL) {
+                        currentOwnerFileName = "no_method_file";
+                    }
+                    currentOwnerLineNumber = dvmLineNumFromPC(currentOwnerMethod, currentOwnerPc);
+                }
+                logContentionEvent(self, waitMs, samplePercent,
+                                   currentOwnerFileName, currentOwnerLineNumber);
+            }
+        }
+    }
+    mon->owner = self;
+    assert(mon->lockCount == 0);
+
+    // When debugging, save the current monitor holder for future
+    // acquisition failures to use in sampled logging.
+    if (gDvm.lockProfThreshold > 0) {
+        mon->ownerMethod = NULL;
+        mon->ownerPc = 0;
+        if (self->interpSave.curFrame == NULL) {
+            return;
+        }
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+        if (saveArea == NULL) {
+            return;
+        }
+        mon->ownerMethod = saveArea->method;
+        mon->ownerPc = (saveArea->xtra.currentPc - saveArea->method->insns);
+    }
+}
+
+/*
+ * Try to lock a monitor.
+ *
+ * Returns "true" on success.
+ */
+#ifdef WITH_COPYING_GC
+static bool tryLockMonitor(Thread* self, Monitor* mon)
+{
+    if (mon->owner == self) {
+        mon->lockCount++;
+        return true;
+    } else {
+        if (dvmTryLockMutex(&mon->lock) == 0) {
+            mon->owner = self;
+            assert(mon->lockCount == 0);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+#endif
+
+/*
+ * Unlock a monitor.
+ *
+ * Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+static bool unlockMonitor(Thread* self, Monitor* mon)
+{
+    assert(self != NULL);
+    assert(mon != NULL);
+    if (mon->owner == self) {
+        /*
+         * We own the monitor, so nobody else can be in here.
+         */
+        if (mon->lockCount == 0) {
+            mon->owner = NULL;
+            mon->ownerMethod = NULL;
+            mon->ownerPc = 0;
+            dvmUnlockMutex(&mon->lock);
+        } else {
+            mon->lockCount--;
+        }
+    } else {
+        /*
+         * We don't own this, so we're not allowed to unlock it.
+         * The JNI spec says that we should throw IllegalMonitorStateException
+         * in this case.
+         */
+        dvmThrowIllegalMonitorStateException("unlock of unowned monitor");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Checks the wait set for circular structure.  Returns 0 if the list
+ * is not circular.  Otherwise, returns 1.  Used only by asserts.
+ */
+#ifndef NDEBUG
+static int waitSetCheck(Monitor *mon)
+{
+    Thread *fast, *slow;
+    size_t n;
+
+    assert(mon != NULL);
+    fast = slow = mon->waitSet;
+    n = 0;
+    for (;;) {
+        if (fast == NULL) return 0;
+        if (fast->waitNext == NULL) return 0;
+        if (fast == slow && n > 0) return 1;
+        n += 2;
+        fast = fast->waitNext->waitNext;
+        slow = slow->waitNext;
+    }
+}
+#endif
+
+/*
+ * Links a thread into a monitor's wait set.  The monitor lock must be
+ * held by the caller of this routine.
+ */
+static void waitSetAppend(Monitor *mon, Thread *thread)
+{
+    Thread *elt;
+
+    assert(mon != NULL);
+    assert(mon->owner == dvmThreadSelf());
+    assert(thread != NULL);
+    assert(thread->waitNext == NULL);
+    assert(waitSetCheck(mon) == 0);
+    if (mon->waitSet == NULL) {
+        mon->waitSet = thread;
+        return;
+    }
+    elt = mon->waitSet;
+    while (elt->waitNext != NULL) {
+        elt = elt->waitNext;
+    }
+    elt->waitNext = thread;
+}
+
+/*
+ * Unlinks a thread from a monitor's wait set.  The monitor lock must
+ * be held by the caller of this routine.
+ */
+static void waitSetRemove(Monitor *mon, Thread *thread)
+{
+    Thread *elt;
+
+    assert(mon != NULL);
+    assert(mon->owner == dvmThreadSelf());
+    assert(thread != NULL);
+    assert(waitSetCheck(mon) == 0);
+    if (mon->waitSet == NULL) {
+        return;
+    }
+    if (mon->waitSet == thread) {
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        return;
+    }
+    elt = mon->waitSet;
+    while (elt->waitNext != NULL) {
+        if (elt->waitNext == thread) {
+            elt->waitNext = thread->waitNext;
+            thread->waitNext = NULL;
+            return;
+        }
+        elt = elt->waitNext;
+    }
+}
+
+/*
+ * Converts the given relative waiting time into an absolute time.
+ */
+static void absoluteTime(s8 msec, s4 nsec, struct timespec *ts)
+{
+    s8 endSec;
+
+    // Clock used here must be consistent with clock specified in dvmInitCondForTimedWait
+#ifdef HAVE_POSIX_CLOCKS
+    clock_gettime(CLOCK_MONOTONIC, ts);
+#else
+    // Platform (probably MacOS) doesn't support CLOCK_MONOTONIC
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        ts->tv_sec = tv.tv_sec;
+        ts->tv_nsec = tv.tv_usec * 1000;
+    }
+#endif
+    endSec = ts->tv_sec + msec / 1000;
+    if (endSec >= 0x7fffffff) {
+        ALOGV("NOTE: end time exceeds epoch");
+        endSec = 0x7ffffffe;
+    }
+    ts->tv_sec = endSec;
+    ts->tv_nsec = (ts->tv_nsec + (msec % 1000) * 1000000) + nsec;
+
+    /* catch rollover */
+    if (ts->tv_nsec >= 1000000000L) {
+        ts->tv_sec++;
+        ts->tv_nsec -= 1000000000L;
+    }
+}
+
+void dvmInitCondForTimedWait(pthread_cond_t* cond)
+{
+    // Must be consistent with clock specified in absoluteTime
+#ifdef HAVE_POSIX_CLOCKS
+    pthread_condattr_t condAttr;
+    pthread_condattr_init(&condAttr);
+    pthread_condattr_setclock(&condAttr, CLOCK_MONOTONIC);
+    pthread_cond_init(cond, &condAttr);
+#else
+    // Platform (probably MacOS) doesn't support CLOCK_MONOTONIC or pthread_condattr_setclock
+    pthread_cond_init(cond, NULL);
+#endif
+}
+
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+                        s8 msec, s4 nsec)
+{
+    struct timespec ts;
+    absoluteTime(msec, nsec, &ts);
+    int ret = pthread_cond_timedwait(cond, mutex, &ts);
+    assert(ret == 0 || ret == ETIMEDOUT);
+    return ret;
+}
+
+/*
+ * Wait on a monitor until timeout, interrupt, or notification.  Used for
+ * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
+ *
+ * If another thread calls Thread.interrupt(), we throw InterruptedException
+ * and return immediately if one of the following are true:
+ *  - blocked in wait(), wait(long), or wait(long, int) methods of Object
+ *  - blocked in join(), join(long), or join(long, int) methods of Thread
+ *  - blocked in sleep(long), or sleep(long, int) methods of Thread
+ * Otherwise, we set the "interrupted" flag.
+ *
+ * Checks to make sure that "nsec" is in the range 0-999999
+ * (i.e. fractions of a millisecond) and throws the appropriate
+ * exception if it isn't.
+ *
+ * The spec allows "spurious wakeups", and recommends that all code using
+ * Object.wait() do so in a loop.  This appears to derive from concerns
+ * about pthread_cond_wait() on multiprocessor systems.  Some commentary
+ * on the web casts doubt on whether these can/should occur.
+ *
+ * Since we're allowed to wake up "early", we clamp extremely long durations
+ * to return at the end of the 32-bit time epoch.
+ */
+static void waitMonitor(Thread* self, Monitor* mon, s8 msec, s4 nsec,
+    bool interruptShouldThrow)
+{
+    struct timespec ts;
+    bool wasInterrupted = false;
+    bool timed;
+    int ret;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before wait()");
+        return;
+    }
+
+    /*
+     * Enforce the timeout range.
+     */
+    if (msec < 0 || nsec < 0 || nsec > 999999) {
+        dvmThrowIllegalArgumentException("timeout arguments out of range");
+        return;
+    }
+
+    /*
+     * Compute absolute wakeup time, if necessary.
+     */
+    if (msec == 0 && nsec == 0) {
+        timed = false;
+    } else {
+        // Calculate absolute time before doing any other work so it is as accurate as possible.
+        absoluteTime(msec, nsec, &ts);
+        timed = true;
+    }
+
+    /*
+     * Add ourselves to the set of threads waiting on this monitor, and
+     * release our hold.  We need to let it go even if we're a few levels
+     * deep in a recursive lock, and we need to restore that later.
+     *
+     * We append to the wait set ahead of clearing the count and owner
+     * fields so the subroutine can check that the calling thread owns
+     * the monitor.  Aside from that, the order of member updates is
+     * not order sensitive as we hold the pthread mutex.
+     */
+    waitSetAppend(mon, self);
+    int prevLockCount = mon->lockCount;
+    mon->lockCount = 0;
+    mon->owner = NULL;
+
+    const Method* savedMethod = mon->ownerMethod;
+    u4 savedPc = mon->ownerPc;
+    mon->ownerMethod = NULL;
+    mon->ownerPc = 0;
+
+    /*
+     * Update thread status.  If the GC wakes up, it'll ignore us, knowing
+     * that we won't touch any references in this state, and we'll check
+     * our suspend mode before we transition out.
+     */
+    if (timed)
+        dvmChangeStatus(self, THREAD_TIMED_WAIT);
+    else
+        dvmChangeStatus(self, THREAD_WAIT);
+
+    dvmLockMutex(&self->waitMutex);
+
+    /*
+     * Set waitMonitor to the monitor object we will be waiting on.
+     * When waitMonitor is non-NULL a notifying or interrupting thread
+     * must signal the thread's waitCond to wake it up.
+     */
+    assert(self->waitMonitor == NULL);
+    self->waitMonitor = mon;
+
+    /*
+     * Handle the case where the thread was interrupted before we called
+     * wait().
+     */
+    if (self->interrupted) {
+        wasInterrupted = true;
+        self->waitMonitor = NULL;
+        dvmUnlockMutex(&self->waitMutex);
+        goto done;
+    }
+
+    /*
+     * Release the monitor lock and wait for a notification or
+     * a timeout to occur.
+     */
+    dvmUnlockMutex(&mon->lock);
+
+    if (!timed) {
+        ret = pthread_cond_wait(&self->waitCond, &self->waitMutex);
+        assert(ret == 0);
+    } else {
+        ret = pthread_cond_timedwait(&self->waitCond, &self->waitMutex, &ts);
+        assert(ret == 0 || ret == ETIMEDOUT);
+    }
+    if (self->interrupted) {
+        wasInterrupted = true;
+    }
+
+    self->interrupted = false;
+    self->waitMonitor = NULL;
+
+    dvmUnlockMutex(&self->waitMutex);
+
+    /* Reacquire the monitor lock. */
+    lockMonitor(self, mon);
+
+done:
+    /*
+     * We remove our thread from wait set after restoring the count
+     * and owner fields so the subroutine can check that the calling
+     * thread owns the monitor. Aside from that, the order of member
+     * updates is not order sensitive as we hold the pthread mutex.
+     */
+    mon->owner = self;
+    mon->lockCount = prevLockCount;
+    mon->ownerMethod = savedMethod;
+    mon->ownerPc = savedPc;
+    waitSetRemove(mon, self);
+
+    /* set self->status back to THREAD_RUNNING, and self-suspend if needed */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    if (wasInterrupted) {
+        /*
+         * We were interrupted while waiting, or somebody interrupted an
+         * un-interruptible thread earlier and we're bailing out immediately.
+         *
+         * The doc sayeth: "The interrupted status of the current thread is
+         * cleared when this exception is thrown."
+         */
+        self->interrupted = false;
+        if (interruptShouldThrow) {
+            dvmThrowInterruptedException(NULL);
+        }
+    }
+}
+
+/*
+ * Notify one thread waiting on this monitor.
+ */
+static void notifyMonitor(Thread* self, Monitor* mon)
+{
+    Thread* thread;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before notify()");
+        return;
+    }
+    /* Signal the first waiting thread in the wait set. */
+    while (mon->waitSet != NULL) {
+        thread = mon->waitSet;
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        dvmLockMutex(&thread->waitMutex);
+        /* Check to see if the thread is still waiting. */
+        if (thread->waitMonitor != NULL) {
+            pthread_cond_signal(&thread->waitCond);
+            dvmUnlockMutex(&thread->waitMutex);
+            return;
+        }
+        dvmUnlockMutex(&thread->waitMutex);
+    }
+}
+
+/*
+ * Notify all threads waiting on this monitor.
+ */
+static void notifyAllMonitor(Thread* self, Monitor* mon)
+{
+    Thread* thread;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowIllegalMonitorStateException(
+            "object not locked by thread before notifyAll()");
+        return;
+    }
+    /* Signal all threads in the wait set. */
+    while (mon->waitSet != NULL) {
+        thread = mon->waitSet;
+        mon->waitSet = thread->waitNext;
+        thread->waitNext = NULL;
+        dvmLockMutex(&thread->waitMutex);
+        /* Check to see if the thread is still waiting. */
+        if (thread->waitMonitor != NULL) {
+            pthread_cond_signal(&thread->waitCond);
+        }
+        dvmUnlockMutex(&thread->waitMutex);
+    }
+}
+
+/*
+ * Changes the shape of a monitor from thin to fat, preserving the
+ * internal lock state.  The calling thread must own the lock.
+ */
+static void inflateMonitor(Thread *self, Object *obj)
+{
+    Monitor *mon;
+    u4 thin;
+
+    assert(self != NULL);
+    assert(obj != NULL);
+    assert(LW_SHAPE(obj->lock) == LW_SHAPE_THIN);
+    assert(LW_LOCK_OWNER(obj->lock) == self->threadId);
+    /* Allocate and acquire a new monitor. */
+    mon = dvmCreateMonitor(obj);
+    lockMonitor(self, mon);
+    /* Propagate the lock state. */
+    thin = obj->lock;
+    mon->lockCount = LW_LOCK_COUNT(thin);
+    thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
+    thin |= (u4)mon | LW_SHAPE_FAT;
+    /* Publish the updated lock word. */
+    android_atomic_release_store(thin, (int32_t *)&obj->lock);
+}
+
+/*
+ * Implements monitorenter for "synchronized" stuff.
+ *
+ * This does not fail or throw an exception (unless deadlock prediction
+ * is enabled and set to "err" mode).
+ */
+void dvmLockObject(Thread* self, Object *obj)
+{
+    volatile u4 *thinp;
+    ThreadStatus oldStatus;
+    struct timespec tm;
+    long sleepDelayNs;
+    long minSleepDelayNs = 1000000;  /* 1 millisecond */
+    long maxSleepDelayNs = 1000000000;  /* 1 second */
+    u4 thin, newThin, threadId;
+
+    assert(self != NULL);
+    assert(obj != NULL);
+    threadId = self->threadId;
+    thinp = &obj->lock;
+retry:
+    thin = *thinp;
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /*
+         * The lock is a thin lock.  The owner field is used to
+         * determine the acquire method, ordered by cost.
+         */
+        if (LW_LOCK_OWNER(thin) == threadId) {
+            /*
+             * The calling thread owns the lock.  Increment the
+             * value of the recursion count field.
+             */
+            obj->lock += 1 << LW_LOCK_COUNT_SHIFT;
+            if (LW_LOCK_COUNT(obj->lock) == LW_LOCK_COUNT_MASK) {
+                /*
+                 * The reacquisition limit has been reached.  Inflate
+                 * the lock so the next acquire will not overflow the
+                 * recursion count field.
+                 */
+                inflateMonitor(self, obj);
+            }
+        } else if (LW_LOCK_OWNER(thin) == 0) {
+            /*
+             * The lock is unowned.  Install the thread id of the
+             * calling thread into the owner field.  This is the
+             * common case.  In performance critical code the JIT
+             * will have tried this before calling out to the VM.
+             */
+            newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+            if (android_atomic_acquire_cas(thin, newThin,
+                    (int32_t*)thinp) != 0) {
+                /*
+                 * The acquire failed.  Try again.
+                 */
+                goto retry;
+            }
+        } else {
+            ALOGV("(%d) spin on lock %p: %#x (%#x) %#x",
+                 threadId, &obj->lock, 0, *thinp, thin);
+            /*
+             * The lock is owned by another thread.  Notify the VM
+             * that we are about to wait.
+             */
+            oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+            /*
+             * Spin until the thin lock is released or inflated.
+             */
+            sleepDelayNs = 0;
+            for (;;) {
+                thin = *thinp;
+                /*
+                 * Check the shape of the lock word.  Another thread
+                 * may have inflated the lock while we were waiting.
+                 */
+                if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+                    if (LW_LOCK_OWNER(thin) == 0) {
+                        /*
+                         * The lock has been released.  Install the
+                         * thread id of the calling thread into the
+                         * owner field.
+                         */
+                        newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+                        if (android_atomic_acquire_cas(thin, newThin,
+                                (int32_t *)thinp) == 0) {
+                            /*
+                             * The acquire succeed.  Break out of the
+                             * loop and proceed to inflate the lock.
+                             */
+                            break;
+                        }
+                    } else {
+                        /*
+                         * The lock has not been released.  Yield so
+                         * the owning thread can run.
+                         */
+                        if (sleepDelayNs == 0) {
+                            sched_yield();
+                            sleepDelayNs = minSleepDelayNs;
+                        } else {
+                            tm.tv_sec = 0;
+                            tm.tv_nsec = sleepDelayNs;
+                            nanosleep(&tm, NULL);
+                            /*
+                             * Prepare the next delay value.  Wrap to
+                             * avoid once a second polls for eternity.
+                             */
+                            if (sleepDelayNs < maxSleepDelayNs / 2) {
+                                sleepDelayNs *= 2;
+                            } else {
+                                sleepDelayNs = minSleepDelayNs;
+                            }
+                        }
+                    }
+                } else {
+                    /*
+                     * The thin lock was inflated by another thread.
+                     * Let the VM know we are no longer waiting and
+                     * try again.
+                     */
+                    ALOGV("(%d) lock %p surprise-fattened",
+                             threadId, &obj->lock);
+                    dvmChangeStatus(self, oldStatus);
+                    goto retry;
+                }
+            }
+            ALOGV("(%d) spin on lock done %p: %#x (%#x) %#x",
+                 threadId, &obj->lock, 0, *thinp, thin);
+            /*
+             * We have acquired the thin lock.  Let the VM know that
+             * we are no longer waiting.
+             */
+            dvmChangeStatus(self, oldStatus);
+            /*
+             * Fatten the lock.
+             */
+            inflateMonitor(self, obj);
+            ALOGV("(%d) lock %p fattened", threadId, &obj->lock);
+        }
+    } else {
+        /*
+         * The lock is a fat lock.
+         */
+        assert(LW_MONITOR(obj->lock) != NULL);
+        lockMonitor(self, LW_MONITOR(obj->lock));
+    }
+}
+
+/*
+ * Implements monitorexit for "synchronized" stuff.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+bool dvmUnlockObject(Thread* self, Object *obj)
+{
+    u4 thin;
+
+    assert(self != NULL);
+    assert(self->status == THREAD_RUNNING);
+    assert(obj != NULL);
+    /*
+     * Cache the lock word as its value can change while we are
+     * examining its state.
+     */
+    thin = *(volatile u4 *)&obj->lock;
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /*
+         * The lock is thin.  We must ensure that the lock is owned
+         * by the given thread before unlocking it.
+         */
+        if (LW_LOCK_OWNER(thin) == self->threadId) {
+            /*
+             * We are the lock owner.  It is safe to update the lock
+             * without CAS as lock ownership guards the lock itself.
+             */
+            if (LW_LOCK_COUNT(thin) == 0) {
+                /*
+                 * The lock was not recursively acquired, the common
+                 * case.  Unlock by clearing all bits except for the
+                 * hash state.
+                 */
+                thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
+                android_atomic_release_store(thin, (int32_t*)&obj->lock);
+            } else {
+                /*
+                 * The object was recursively acquired.  Decrement the
+                 * lock recursion count field.
+                 */
+                obj->lock -= 1 << LW_LOCK_COUNT_SHIFT;
+            }
+        } else {
+            /*
+             * We do not own the lock.  The JVM spec requires that we
+             * throw an exception in this case.
+             */
+            dvmThrowIllegalMonitorStateException("unlock of unowned monitor");
+            return false;
+        }
+    } else {
+        /*
+         * The lock is fat.  We must check to see if unlockMonitor has
+         * raised any exceptions before continuing.
+         */
+        assert(LW_MONITOR(obj->lock) != NULL);
+        if (!unlockMonitor(self, LW_MONITOR(obj->lock))) {
+            /*
+             * An exception has been raised.  Do not fall through.
+             */
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Object.wait().  Also called for class init.
+ */
+void dvmObjectWait(Thread* self, Object *obj, s8 msec, s4 nsec,
+    bool interruptShouldThrow)
+{
+    Monitor* mon;
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, we need to fatten it.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before wait()");
+            return;
+        }
+
+        /* This thread holds the lock.  We need to fatten the lock
+         * so 'self' can block on it.  Don't update the object lock
+         * field yet, because 'self' needs to acquire the lock before
+         * any other thread gets a chance.
+         */
+        inflateMonitor(self, obj);
+        ALOGV("(%d) lock %p fattened by wait()", self->threadId, &obj->lock);
+    }
+    mon = LW_MONITOR(obj->lock);
+    waitMonitor(self, mon, msec, nsec, interruptShouldThrow);
+}
+
+/*
+ * Object.notify().
+ */
+void dvmObjectNotify(Thread* self, Object *obj)
+{
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, there aren't any waiters;
+     * waiting on an object forces lock fattening.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before notify()");
+            return;
+        }
+
+        /* no-op;  there are no waiters to notify.
+         */
+    } else {
+        /* It's a fat lock.
+         */
+        notifyMonitor(self, LW_MONITOR(thin));
+    }
+}
+
+/*
+ * Object.notifyAll().
+ */
+void dvmObjectNotifyAll(Thread* self, Object *obj)
+{
+    u4 thin = *(volatile u4 *)&obj->lock;
+
+    /* If the lock is still thin, there aren't any waiters;
+     * waiting on an object forces lock fattening.
+     */
+    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+        /* Make sure that 'self' holds the lock.
+         */
+        if (LW_LOCK_OWNER(thin) != self->threadId) {
+            dvmThrowIllegalMonitorStateException(
+                "object not locked by thread before notifyAll()");
+            return;
+        }
+
+        /* no-op;  there are no waiters to notify.
+         */
+    } else {
+        /* It's a fat lock.
+         */
+        notifyAllMonitor(self, LW_MONITOR(thin));
+    }
+}
+
+/*
+ * This implements java.lang.Thread.sleep(long msec, int nsec).
+ *
+ * The sleep is interruptible by other threads, which means we can't just
+ * plop into an OS sleep call.  (We probably could if we wanted to send
+ * signals around and rely on EINTR, but that's inefficient and relies
+ * on native code respecting our signal mask.)
+ *
+ * We have to do all of this stuff for Object.wait() as well, so it's
+ * easiest to just sleep on a private Monitor.
+ *
+ * It appears that we want sleep(0,0) to go through the motions of sleeping
+ * for a very short duration, rather than just returning.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec)
+{
+    Thread* self = dvmThreadSelf();
+    Monitor* mon = gDvm.threadSleepMon;
+
+    /* sleep(0,0) wakes up immediately, wait(0,0) means wait forever; adjust */
+    if (msec == 0 && nsec == 0)
+        nsec++;
+
+    lockMonitor(self, mon);
+    waitMonitor(self, mon, msec, nsec, true);
+    unlockMonitor(self, mon);
+}
+
+/*
+ * Implement java.lang.Thread.interrupt().
+ */
+void dvmThreadInterrupt(Thread* thread)
+{
+    assert(thread != NULL);
+
+    dvmLockMutex(&thread->waitMutex);
+
+    /*
+     * If the interrupted flag is already set no additional action is
+     * required.
+     */
+    if (thread->interrupted == true) {
+        dvmUnlockMutex(&thread->waitMutex);
+        return;
+    }
+
+    /*
+     * Raise the "interrupted" flag.  This will cause it to bail early out
+     * of the next wait() attempt, if it's not currently waiting on
+     * something.
+     */
+    thread->interrupted = true;
+
+    /*
+     * Is the thread waiting?
+     *
+     * Note that fat vs. thin doesn't matter here;  waitMonitor
+     * is only set when a thread actually waits on a monitor,
+     * which implies that the monitor has already been fattened.
+     */
+    if (thread->waitMonitor != NULL) {
+        pthread_cond_signal(&thread->waitCond);
+    }
+
+    dvmUnlockMutex(&thread->waitMutex);
+}
+
+#ifndef WITH_COPYING_GC
+u4 dvmIdentityHashCode(Object *obj)
+{
+    return (u4)obj;
+}
+#else
+/*
+ * Returns the identity hash code of the given object.
+ */
+u4 dvmIdentityHashCode(Object *obj)
+{
+    Thread *self, *thread;
+    volatile u4 *lw;
+    size_t size;
+    u4 lock, owner, hashState;
+
+    if (obj == NULL) {
+        /*
+         * Null is defined to have an identity hash code of 0.
+         */
+        return 0;
+    }
+    lw = &obj->lock;
+retry:
+    hashState = LW_HASH_STATE(*lw);
+    if (hashState == LW_HASH_STATE_HASHED) {
+        /*
+         * The object has been hashed but has not had its hash code
+         * relocated by the garbage collector.  Use the raw object
+         * address.
+         */
+        return (u4)obj >> 3;
+    } else if (hashState == LW_HASH_STATE_HASHED_AND_MOVED) {
+        /*
+         * The object has been hashed and its hash code has been
+         * relocated by the collector.  Use the value of the naturally
+         * aligned word following the instance data.
+         */
+        assert(!dvmIsClassObject(obj));
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+            size = dvmArrayObjectSize((ArrayObject *)obj);
+            size = (size + 2) & ~2;
+        } else {
+            size = obj->clazz->objectSize;
+        }
+        return *(u4 *)(((char *)obj) + size);
+    } else if (hashState == LW_HASH_STATE_UNHASHED) {
+        /*
+         * The object has never been hashed.  Change the hash state to
+         * hashed and use the raw object address.
+         */
+        self = dvmThreadSelf();
+        if (self->threadId == lockOwner(obj)) {
+            /*
+             * We already own the lock so we can update the hash state
+             * directly.
+             */
+            *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            return (u4)obj >> 3;
+        }
+        /*
+         * We do not own the lock.  Try acquiring the lock.  Should
+         * this fail, we must suspend the owning thread.
+         */
+        if (LW_SHAPE(*lw) == LW_SHAPE_THIN) {
+            /*
+             * If the lock is thin assume it is unowned.  We simulate
+             * an acquire, update, and release with a single CAS.
+             */
+            lock = (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            if (android_atomic_acquire_cas(
+                                0,
+                                (int32_t)lock,
+                                (int32_t *)lw) == 0) {
+                /*
+                 * A new lockword has been installed with a hash state
+                 * of hashed.  Use the raw object address.
+                 */
+                return (u4)obj >> 3;
+            }
+        } else {
+            if (tryLockMonitor(self, LW_MONITOR(*lw))) {
+                /*
+                 * The monitor lock has been acquired.  Change the
+                 * hash state to hashed and use the raw object
+                 * address.
+                 */
+                *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+                unlockMonitor(self, LW_MONITOR(*lw));
+                return (u4)obj >> 3;
+            }
+        }
+        /*
+         * At this point we have failed to acquire the lock.  We must
+         * identify the owning thread and suspend it.
+         */
+        dvmLockThreadList(self);
+        /*
+         * Cache the lock word as its value can change between
+         * determining its shape and retrieving its owner.
+         */
+        lock = *lw;
+        if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+            /*
+             * Find the thread with the corresponding thread id.
+             */
+            owner = LW_LOCK_OWNER(lock);
+            assert(owner != self->threadId);
+            /*
+             * If the lock has no owner do not bother scanning the
+             * thread list and fall through to the failure handler.
+             */
+            thread = owner ? gDvm.threadList : NULL;
+            while (thread != NULL) {
+                if (thread->threadId == owner) {
+                    break;
+                }
+                thread = thread->next;
+            }
+        } else {
+            thread = LW_MONITOR(lock)->owner;
+        }
+        /*
+         * If thread is NULL the object has been released since the
+         * thread list lock was acquired.  Try again.
+         */
+        if (thread == NULL) {
+            dvmUnlockThreadList();
+            goto retry;
+        }
+        /*
+         * Wait for the owning thread to suspend.
+         */
+        dvmSuspendThread(thread);
+        if (dvmHoldsLock(thread, obj)) {
+            /*
+             * The owning thread has been suspended.  We can safely
+             * change the hash state to hashed.
+             */
+            *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            dvmResumeThread(thread);
+            dvmUnlockThreadList();
+            return (u4)obj >> 3;
+        }
+        /*
+         * The wrong thread has been suspended.  Try again.
+         */
+        dvmResumeThread(thread);
+        dvmUnlockThreadList();
+        goto retry;
+    }
+    ALOGE("object %p has an unknown hash state %#x", obj, hashState);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+#endif  /* WITH_COPYING_GC */
diff --git a/vm/Sync.h b/vm/Sync.h
new file mode 100644
index 0000000..7ac6e8b
--- /dev/null
+++ b/vm/Sync.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Object synchronization functions.
+ */
+#ifndef DALVIK_SYNC_H_
+#define DALVIK_SYNC_H_
+
+/*
+ * Monitor shape field.  Used to distinguish immediate thin locks from
+ * indirecting fat locks.
+ */
+#define LW_SHAPE_THIN 0
+#define LW_SHAPE_FAT 1
+#define LW_SHAPE_MASK 0x1
+#define LW_SHAPE(x) ((x) & LW_SHAPE_MASK)
+
+/*
+ * Hash state field.  Used to signify that an object has had its
+ * identity hash code exposed or relocated.
+ */
+#define LW_HASH_STATE_UNHASHED 0
+#define LW_HASH_STATE_HASHED 1
+#define LW_HASH_STATE_HASHED_AND_MOVED 3
+#define LW_HASH_STATE_MASK 0x3
+#define LW_HASH_STATE_SHIFT 1
+#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK)
+
+/*
+ * Monitor accessor.  Extracts a monitor structure pointer from a fat
+ * lock.  Performs no error checking.
+ */
+#define LW_MONITOR(x) \
+  ((Monitor*)((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | \
+                      LW_SHAPE_MASK)))
+
+/*
+ * Lock owner field.  Contains the thread id of the thread currently
+ * holding the lock.
+ */
+#define LW_LOCK_OWNER_MASK 0xffff
+#define LW_LOCK_OWNER_SHIFT 3
+#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
+
+/*
+ * Lock recursion count field.  Contains a count of the numer of times
+ * a lock has been recursively acquired.
+ */
+#define LW_LOCK_COUNT_MASK 0x1fff
+#define LW_LOCK_COUNT_SHIFT 19
+#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
+
+struct Object;
+struct Monitor;
+struct Thread;
+
+/*
+ * Returns true if the lock has been fattened.
+ */
+#define IS_LOCK_FAT(lock)   (LW_SHAPE(*(lock)) == LW_SHAPE_FAT)
+
+/*
+ * Acquire the object's monitor.
+ */
+extern "C" void dvmLockObject(Thread* self, Object* obj);
+
+/* Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+extern "C" bool dvmUnlockObject(Thread* self, Object* obj);
+
+/*
+ * Implementations of some java/lang/Object calls.
+ */
+void dvmObjectWait(Thread* self, Object* obj,
+    s8 timeout, s4 nanos, bool interruptShouldThrow);
+void dvmObjectNotify(Thread* self, Object* obj);
+void dvmObjectNotifyAll(Thread* self, Object* obj);
+
+/*
+ * Implementation of System.identityHashCode().
+ */
+u4 dvmIdentityHashCode(Object* obj);
+
+/*
+ * Implementation of Thread.sleep().
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Implementation of Thread.interrupt().
+ *
+ * Interrupt a thread.  If it's waiting on a monitor, wake it up.
+ */
+void dvmThreadInterrupt(Thread* thread);
+
+/* create a new Monitor struct */
+Monitor* dvmCreateMonitor(Object* obj);
+
+/*
+ * Frees unmarked monitors from the monitor list.  The given callback
+ * routine should return a non-zero value when passed a pointer to an
+ * unmarked object.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*));
+
+/* free monitor list */
+void dvmFreeMonitorList(void);
+
+/*
+ * Get the object a monitor is part of.
+ *
+ * Returns NULL if "mon" is NULL or the monitor is not part of an object
+ * (which should only happen for Thread.sleep() in the current implementation).
+ */
+Object* dvmGetMonitorObject(Monitor* mon);
+
+/*
+ * Get the thread that holds the lock on the specified object.  The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+Thread* dvmGetObjectLockHolder(Object* obj);
+
+/*
+ * Checks whether the object is held by the specified thread.
+ */
+bool dvmHoldsLock(Thread* thread, Object* obj);
+
+/*
+ * Initialize a pthread_cond_t for use with a timed wait.
+ *
+ * This must be used for initializing conditional variables that are used by dvmRelativeCondWait
+ * to ensure that consistent timers are used.
+ */
+void dvmInitCondForTimedWait(pthread_cond_t* cond);
+
+/*
+ * Relative timed wait on condition
+ */
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+                         s8 msec, s4 nsec);
+
+#endif  // DALVIK_SYNC_H_
diff --git a/vm/Thread.cpp b/vm/Thread.cpp
new file mode 100644
index 0000000..ab4c58e
--- /dev/null
+++ b/vm/Thread.cpp
@@ -0,0 +1,3612 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+/*
+ * Thread support.
+ */
+#include "Dalvik.h"
+#include "os/os.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+#include "interp/Jit.h"         // need for self verification
+#endif
+
+ #include <cutils/trace.h>
+
+/* desktop Linux needs a little help with gettid() */
+#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t,gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+#endif
+
+// Change this to enable logging on cgroup errors
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
+// change this to ALOGV/ALOGD to debug thread activity
+#define LOG_THREAD  LOGVV
+
+/*
+Notes on Threading
+
+All threads are native pthreads.  All threads, except the JDWP debugger
+thread, are visible to code running in the VM and to the debugger.  (We
+don't want the debugger to try to manipulate the thread that listens for
+instructions from the debugger.)  Internal VM threads are in the "system"
+ThreadGroup, all others are in the "main" ThreadGroup, per convention.
+
+The GC only runs when all threads have been suspended.  Threads are
+expected to suspend themselves, using a "safe point" mechanism.  We check
+for a suspend request at certain points in the main interpreter loop,
+and on requests coming in from native code (e.g. all JNI functions).
+Certain debugger events may inspire threads to self-suspend.
+
+Native methods must use JNI calls to modify object references to avoid
+clashes with the GC.  JNI doesn't provide a way for native code to access
+arrays of objects as such -- code must always get/set individual entries --
+so it should be possible to fully control access through JNI.
+
+Internal native VM threads, such as the finalizer thread, must explicitly
+check for suspension periodically.  In most cases they will be sound
+asleep on a condition variable, and won't notice the suspension anyway.
+
+Threads may be suspended by the GC, debugger, or the SIGQUIT listener
+thread.  The debugger may suspend or resume individual threads, while the
+GC always suspends all threads.  Each thread has a "suspend count" that
+is incremented on suspend requests and decremented on resume requests.
+When the count is zero, the thread is runnable.  This allows us to fulfill
+a debugger requirement: if the debugger suspends a thread, the thread is
+not allowed to run again until the debugger resumes it (or disconnects,
+in which case we must resume all debugger-suspended threads).
+
+Paused threads sleep on a condition variable, and are awoken en masse.
+Certain "slow" VM operations, such as starting up a new thread, will be
+done in a separate "VMWAIT" state, so that the rest of the VM doesn't
+freeze up waiting for the operation to finish.  Threads must check for
+pending suspension when leaving VMWAIT.
+
+Because threads suspend themselves while interpreting code or when native
+code makes JNI calls, there is no risk of suspending while holding internal
+VM locks.  All threads can enter a suspended (or native-code-only) state.
+Also, we don't have to worry about object references existing solely
+in hardware registers.
+
+We do, however, have to worry about objects that were allocated internally
+and aren't yet visible to anything else in the VM.  If we allocate an
+object, and then go to sleep on a mutex after changing to a non-RUNNING
+state (e.g. while trying to allocate a second object), the first object
+could be garbage-collected out from under us while we sleep.  To manage
+this, we automatically add all allocated objects to an internal object
+tracking list, and only remove them when we know we won't be suspended
+before the object appears in the GC root set.
+
+The debugger may choose to suspend or resume a single thread, which can
+lead to application-level deadlocks; this is expected behavior.  The VM
+will only check for suspension of single threads when the debugger is
+active (the java.lang.Thread calls for this are deprecated and hence are
+not supported).  Resumption of a single thread is handled by decrementing
+the thread's suspend count and sending a broadcast signal to the condition
+variable.  (This will cause all threads to wake up and immediately go back
+to sleep, which isn't tremendously efficient, but neither is having the
+debugger attached.)
+
+The debugger is not allowed to resume threads suspended by the GC.  This
+is trivially enforced by ignoring debugger requests while the GC is running
+(the JDWP thread is suspended during GC).
+
+The VM maintains a Thread struct for every pthread known to the VM.  There
+is a java/lang/Thread object associated with every Thread.  At present,
+there is no safe way to go from a Thread object to a Thread struct except by
+locking and scanning the list; this is necessary because the lifetimes of
+the two are not closely coupled.  We may want to change this behavior,
+though at present the only performance impact is on the debugger (see
+threadObjToThread()).  See also notes about dvmDetachCurrentThread().
+*/
+/*
+Alternate implementation (signal-based):
+
+Threads run without safe points -- zero overhead.  The VM uses a signal
+(e.g. pthread_kill(SIGUSR1)) to notify threads of suspension or resumption.
+
+The trouble with using signals to suspend threads is that it means a thread
+can be in the middle of an operation when garbage collection starts.
+To prevent some sticky situations, we have to introduce critical sections
+to the VM code.
+
+Critical sections temporarily block suspension for a given thread.
+The thread must move to a non-blocked state (and self-suspend) after
+finishing its current task.  If the thread blocks on a resource held
+by a suspended thread, we're hosed.
+
+One approach is to require that no blocking operations, notably
+acquisition of mutexes, can be performed within a critical section.
+This is too limiting.  For example, if thread A gets suspended while
+holding the thread list lock, it will prevent the GC or debugger from
+being able to safely access the thread list.  We need to wrap the critical
+section around the entire operation (enter critical, get lock, do stuff,
+release lock, exit critical).
+
+A better approach is to declare that certain resources can only be held
+within critical sections.  A thread that enters a critical section and
+then gets blocked on the thread list lock knows that the thread it is
+waiting for is also in a critical section, and will release the lock
+before suspending itself.  Eventually all threads will complete their
+operations and self-suspend.  For this to work, the VM must:
+
+ (1) Determine the set of resources that may be accessed from the GC or
+     debugger threads.  The mutexes guarding those go into the "critical
+     resource set" (CRS).
+ (2) Ensure that no resource in the CRS can be acquired outside of a
+     critical section.  This can be verified with an assert().
+ (3) Ensure that only resources in the CRS can be held while in a critical
+     section.  This is harder to enforce.
+
+If any of these conditions are not met, deadlock can ensue when grabbing
+resources in the GC or debugger (#1) or waiting for threads to suspend
+(#2,#3).  (You won't actually deadlock in the GC, because if the semantics
+above are followed you don't need to lock anything in the GC.  The risk is
+rather that the GC will access data structures in an intermediate state.)
+
+This approach requires more care and awareness in the VM than
+safe-pointing.  Because the GC and debugger are fairly intrusive, there
+really aren't any internal VM resources that aren't shared.  Thus, the
+enter/exit critical calls can be added to internal mutex wrappers, which
+makes it easy to get #1 and #2 right.
+
+An ordering should be established for all locks to avoid deadlocks.
+
+Monitor locks, which are also implemented with pthread calls, should not
+cause any problems here.  Threads fighting over such locks will not be in
+critical sections and can be suspended freely.
+
+This can get tricky if we ever need exclusive access to VM and non-VM
+resources at the same time.  It's not clear if this is a real concern.
+
+There are (at least) two ways to handle the incoming signals:
+
+ (a) Always accept signals.  If we're in a critical section, the signal
+     handler just returns without doing anything (the "suspend level"
+     should have been incremented before the signal was sent).  Otherwise,
+     if the "suspend level" is nonzero, we go to sleep.
+ (b) Block signals in critical sections.  This ensures that we can't be
+     interrupted in a critical section, but requires pthread_sigmask()
+     calls on entry and exit.
+
+This is a choice between blocking the message and blocking the messenger.
+Because UNIX signals are unreliable (you can only know that you have been
+signaled, not whether you were signaled once or 10 times), the choice is
+not significant for correctness.  The choice depends on the efficiency
+of pthread_sigmask() and the desire to actually block signals.  Either way,
+it is best to ensure that there is only one indication of "blocked";
+having two (i.e. block signals and set a flag, then only send a signal
+if the flag isn't set) can lead to race conditions.
+
+The signal handler must take care to copy registers onto the stack (via
+setjmp), so that stack scans find all references.  Because we have to scan
+native stacks, "exact" GC is not possible with this approach.
+
+Some other concerns with flinging signals around:
+ - Odd interactions with some debuggers (e.g. gdb on the Mac)
+ - Restrictions on some standard library calls during GC (e.g. don't
+   use printf on stdout to print GC debug messages)
+*/
+
+#define kMaxThreadId        ((1 << 16) - 1)
+#define kMainThreadId       1
+
+
+static Thread* allocThread(int interpStackSize);
+static bool prepareThread(Thread* thread);
+static void setThreadSelf(Thread* thread);
+static void unlinkThread(Thread* thread);
+static void freeThread(Thread* thread);
+static void assignThreadId(Thread* thread);
+static bool createFakeEntryFrame(Thread* thread);
+static bool createFakeRunFrame(Thread* thread);
+static void* interpThreadStart(void* arg);
+static void* internalThreadStart(void* arg);
+static void threadExitUncaughtException(Thread* thread, Object* group);
+static void threadExitCheck(void* arg);
+static void waitForThreadSuspend(Thread* self, Thread* thread);
+
+/*
+ * Initialize thread list and main thread's environment.  We need to set
+ * up some basic stuff so that dvmThreadSelf() will work when we start
+ * loading classes (e.g. to check for exceptions).
+ */
+bool dvmThreadStartup()
+{
+    Thread* thread;
+
+    /* allocate a TLS slot */
+    if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
+        ALOGE("ERROR: pthread_key_create failed");
+        return false;
+    }
+
+    /* test our pthread lib */
+    if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
+        ALOGW("WARNING: newly-created pthread TLS slot is not NULL");
+
+    /* prep thread-related locks and conditions */
+    dvmInitMutex(&gDvm.threadListLock);
+    pthread_cond_init(&gDvm.threadStartCond, NULL);
+    pthread_cond_init(&gDvm.vmExitCond, NULL);
+    dvmInitMutex(&gDvm._threadSuspendLock);
+    dvmInitMutex(&gDvm.threadSuspendCountLock);
+    pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
+
+    /*
+     * Dedicated monitor for Thread.sleep().
+     * TODO: change this to an Object* so we don't have to expose this
+     * call, and we interact better with JDWP monitor calls.  Requires
+     * deferring the object creation to much later (e.g. final "main"
+     * thread prep) or until first use.
+     */
+    gDvm.threadSleepMon = dvmCreateMonitor(NULL);
+
+    gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
+
+    thread = allocThread(gDvm.mainThreadStackSize);
+    if (thread == NULL)
+        return false;
+
+    /* switch mode for when we run initializers */
+    thread->status = THREAD_RUNNING;
+
+    /*
+     * We need to assign the threadId early so we can lock/notify
+     * object monitors.  We'll set the "threadObj" field later.
+     */
+    prepareThread(thread);
+    gDvm.threadList = thread;
+
+#ifdef COUNT_PRECISE_METHODS
+    gDvm.preciseMethods = dvmPointerSetAlloc(200);
+#endif
+
+    return true;
+}
+
+/*
+ * All threads should be stopped by now.  Clean up some thread globals.
+ */
+void dvmThreadShutdown()
+{
+    if (gDvm.threadList != NULL) {
+        /*
+         * If we walk through the thread list and try to free the
+         * lingering thread structures (which should only be for daemon
+         * threads), the daemon threads may crash if they execute before
+         * the process dies.  Let them leak.
+         */
+        freeThread(gDvm.threadList);
+        gDvm.threadList = NULL;
+    }
+
+    dvmFreeBitVector(gDvm.threadIdMap);
+
+    dvmFreeMonitorList();
+
+    pthread_key_delete(gDvm.pthreadKeySelf);
+}
+
+
+/*
+ * Grab the suspend count global lock.
+ */
+static inline void lockThreadSuspendCount()
+{
+    /*
+     * Don't try to change to VMWAIT here.  When we change back to RUNNING
+     * we have to check for a pending suspend, which results in grabbing
+     * this lock recursively.  Doesn't work with "fast" pthread mutexes.
+     *
+     * This lock is always held for very brief periods, so as long as
+     * mutex ordering is respected we shouldn't stall.
+     */
+    dvmLockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Release the suspend count global lock.
+ */
+static inline void unlockThreadSuspendCount()
+{
+    dvmUnlockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Grab the thread list global lock.
+ *
+ * This is held while "suspend all" is trying to make everybody stop.  If
+ * the shutdown is in progress, and somebody tries to grab the lock, they'll
+ * have to wait for the GC to finish.  Therefore it's important that the
+ * thread not be in RUNNING mode.
+ *
+ * We don't have to check to see if we should be suspended once we have
+ * the lock.  Nobody can suspend all threads without holding the thread list
+ * lock while they do it, so by definition there isn't a GC in progress.
+ *
+ * This function deliberately avoids the use of dvmChangeStatus(),
+ * which could grab threadSuspendCountLock.  To avoid deadlock, threads
+ * are required to grab the thread list lock before the thread suspend
+ * count lock.  (See comment in DvmGlobals.)
+ *
+ * TODO: consider checking for suspend after acquiring the lock, and
+ * backing off if set.  As stated above, it can't happen during normal
+ * execution, but it *can* happen during shutdown when daemon threads
+ * are being suspended.
+ */
+void dvmLockThreadList(Thread* self)
+{
+    ThreadStatus oldStatus;
+
+    if (self == NULL)       /* try to get it from TLS */
+        self = dvmThreadSelf();
+
+    if (self != NULL) {
+        oldStatus = self->status;
+        self->status = THREAD_VMWAIT;
+    } else {
+        /* happens during VM shutdown */
+        oldStatus = THREAD_UNDEFINED;  // shut up gcc
+    }
+
+    dvmLockMutex(&gDvm.threadListLock);
+
+    if (self != NULL)
+        self->status = oldStatus;
+}
+
+/*
+ * Try to lock the thread list.
+ *
+ * Returns "true" if we locked it.  This is a "fast" mutex, so if the
+ * current thread holds the lock this will fail.
+ */
+bool dvmTryLockThreadList()
+{
+    return (dvmTryLockMutex(&gDvm.threadListLock) == 0);
+}
+
+/*
+ * Release the thread list global lock.
+ */
+void dvmUnlockThreadList()
+{
+    dvmUnlockMutex(&gDvm.threadListLock);
+}
+
+/*
+ * Convert SuspendCause to a string.
+ */
+static const char* getSuspendCauseStr(SuspendCause why)
+{
+    switch (why) {
+    case SUSPEND_NOT:               return "NOT?";
+    case SUSPEND_FOR_GC:            return "gc";
+    case SUSPEND_FOR_DEBUG:         return "debug";
+    case SUSPEND_FOR_DEBUG_EVENT:   return "debug-event";
+    case SUSPEND_FOR_STACK_DUMP:    return "stack-dump";
+    case SUSPEND_FOR_VERIFY:        return "verify";
+    case SUSPEND_FOR_HPROF:         return "hprof";
+#if defined(WITH_JIT)
+    case SUSPEND_FOR_TBL_RESIZE:    return "table-resize";
+    case SUSPEND_FOR_IC_PATCH:      return "inline-cache-patch";
+    case SUSPEND_FOR_CC_RESET:      return "reset-code-cache";
+    case SUSPEND_FOR_REFRESH:       return "refresh jit status";
+#endif
+    default:                        return "UNKNOWN";
+    }
+}
+
+/*
+ * Grab the "thread suspend" lock.  This is required to prevent the
+ * GC and the debugger from simultaneously suspending all threads.
+ *
+ * If we fail to get the lock, somebody else is trying to suspend all
+ * threads -- including us.  If we go to sleep on the lock we'll deadlock
+ * the VM.  Loop until we get it or somebody puts us to sleep.
+ */
+static void lockThreadSuspend(const char* who, SuspendCause why)
+{
+    const int kSpinSleepTime = 3*1000*1000;        /* 3s */
+    u8 startWhen = 0;       // init req'd to placate gcc
+    int sleepIter = 0;
+    int cc;
+
+    do {
+        cc = dvmTryLockMutex(&gDvm._threadSuspendLock);
+        if (cc != 0) {
+            Thread* self = dvmThreadSelf();
+
+            if (!dvmCheckSuspendPending(self)) {
+                /*
+                 * Could be that a resume-all is in progress, and something
+                 * grabbed the CPU when the wakeup was broadcast.  The thread
+                 * performing the resume hasn't had a chance to release the
+                 * thread suspend lock.  (We release before the broadcast,
+                 * so this should be a narrow window.)
+                 *
+                 * Could be we hit the window as a suspend was started,
+                 * and the lock has been grabbed but the suspend counts
+                 * haven't been incremented yet.
+                 *
+                 * Could be an unusual JNI thread-attach thing.
+                 *
+                 * Could be the debugger telling us to resume at roughly
+                 * the same time we're posting an event.
+                 *
+                 * Could be two app threads both want to patch predicted
+                 * chaining cells around the same time.
+                 */
+                ALOGI("threadid=%d ODD: want thread-suspend lock (%s:%s),"
+                     " it's held, no suspend pending",
+                    self->threadId, who, getSuspendCauseStr(why));
+            } else {
+                /* we suspended; reset timeout */
+                sleepIter = 0;
+            }
+
+            /* give the lock-holder a chance to do some work */
+            if (sleepIter == 0)
+                startWhen = dvmGetRelativeTimeUsec();
+            if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
+                ALOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s),"
+                     " bailing",
+                    self->threadId, who, getSuspendCauseStr(why));
+                /* threads are not suspended, thread dump could crash */
+                dvmDumpAllThreads(false);
+                dvmAbort();
+            }
+        }
+    } while (cc != 0);
+    assert(cc == 0);
+}
+
+/*
+ * Release the "thread suspend" lock.
+ */
+static inline void unlockThreadSuspend()
+{
+    dvmUnlockMutex(&gDvm._threadSuspendLock);
+}
+
+
+/*
+ * Kill any daemon threads that still exist.  All of ours should be
+ * stopped, so these should be Thread objects or JNI-attached threads
+ * started by the application.  Actively-running threads are likely
+ * to crash the process if they continue to execute while the VM
+ * shuts down, so we really need to kill or suspend them.  (If we want
+ * the VM to restart within this process, we need to kill them, but that
+ * leaves open the possibility of orphaned resources.)
+ *
+ * Waiting for the thread to suspend may be unwise at this point, but
+ * if one of these is wedged in a critical section then we probably
+ * would've locked up on the last GC attempt.
+ *
+ * It's possible for this function to get called after a failed
+ * initialization, so be careful with assumptions about the environment.
+ *
+ * This will be called from whatever thread calls DestroyJavaVM, usually
+ * but not necessarily the main thread.  It's likely, but not guaranteed,
+ * that the current thread has already been cleaned up.
+ */
+void dvmSlayDaemons()
+{
+    Thread* self = dvmThreadSelf();     // may be null
+    Thread* target;
+    int threadId = 0;
+    bool doWait = false;
+
+    dvmLockThreadList(self);
+
+    if (self != NULL)
+        threadId = self->threadId;
+
+    target = gDvm.threadList;
+    while (target != NULL) {
+        if (target == self) {
+            target = target->next;
+            continue;
+        }
+
+        if (!dvmGetFieldBoolean(target->threadObj,
+                gDvm.offJavaLangThread_daemon))
+        {
+            /* should never happen; suspend it with the rest */
+            ALOGW("threadid=%d: non-daemon id=%d still running at shutdown?!",
+                threadId, target->threadId);
+        }
+
+        std::string threadName(dvmGetThreadName(target));
+        ALOGV("threadid=%d: suspending daemon id=%d name='%s'",
+                threadId, target->threadId, threadName.c_str());
+
+        /* mark as suspended */
+        lockThreadSuspendCount();
+        dvmAddToSuspendCounts(target, 1, 0);
+        unlockThreadSuspendCount();
+        doWait = true;
+
+        target = target->next;
+    }
+
+    //dvmDumpAllThreads(false);
+
+    /*
+     * Unlock the thread list, relocking it later if necessary.  It's
+     * possible a thread is in VMWAIT after calling dvmLockThreadList,
+     * and that function *doesn't* check for pending suspend after
+     * acquiring the lock.  We want to let them finish their business
+     * and see the pending suspend before we continue here.
+     *
+     * There's no guarantee of mutex fairness, so this might not work.
+     * (The alternative is to have dvmLockThreadList check for suspend
+     * after acquiring the lock and back off, something we should consider.)
+     */
+    dvmUnlockThreadList();
+
+    if (doWait) {
+        bool complained = false;
+
+        usleep(200 * 1000);
+
+        dvmLockThreadList(self);
+
+        /*
+         * Sleep for a bit until the threads have suspended.  We're trying
+         * to exit, so don't wait for too long.
+         */
+        int i;
+        for (i = 0; i < 10; i++) {
+            bool allSuspended = true;
+
+            target = gDvm.threadList;
+            while (target != NULL) {
+                if (target == self) {
+                    target = target->next;
+                    continue;
+                }
+
+                if (target->status == THREAD_RUNNING) {
+                    if (!complained)
+                        ALOGD("threadid=%d not ready yet", target->threadId);
+                    allSuspended = false;
+                    /* keep going so we log each running daemon once */
+                }
+
+                target = target->next;
+            }
+
+            if (allSuspended) {
+                ALOGV("threadid=%d: all daemons have suspended", threadId);
+                break;
+            } else {
+                if (!complained) {
+                    complained = true;
+                    ALOGD("threadid=%d: waiting briefly for daemon suspension",
+                        threadId);
+                }
+            }
+
+            usleep(200 * 1000);
+        }
+        dvmUnlockThreadList();
+    }
+
+#if 0   /* bad things happen if they come out of JNI or "spuriously" wake up */
+    /*
+     * Abandon the threads and recover their resources.
+     */
+    target = gDvm.threadList;
+    while (target != NULL) {
+        Thread* nextTarget = target->next;
+        unlinkThread(target);
+        freeThread(target);
+        target = nextTarget;
+    }
+#endif
+
+    //dvmDumpAllThreads(true);
+}
+
+
+/*
+ * Finish preparing the parts of the Thread struct required to support
+ * JNI registration.
+ */
+bool dvmPrepMainForJni(JNIEnv* pEnv)
+{
+    Thread* self;
+
+    /* main thread is always first in list at this point */
+    self = gDvm.threadList;
+    assert(self->threadId == kMainThreadId);
+
+    /* create a "fake" JNI frame at the top of the main thread interp stack */
+    if (!createFakeEntryFrame(self))
+        return false;
+
+    /* fill these in, since they weren't ready at dvmCreateJNIEnv time */
+    dvmSetJniEnvThreadId(pEnv, self);
+    dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
+
+    return true;
+}
+
+
+/*
+ * Finish preparing the main thread, allocating some objects to represent
+ * it.  As part of doing so, we finish initializing Thread and ThreadGroup.
+ * This will execute some interpreted code (e.g. class initializers).
+ */
+bool dvmPrepMainThread()
+{
+    Thread* thread;
+    Object* groupObj;
+    Object* threadObj;
+    Object* vmThreadObj;
+    StringObject* threadNameStr;
+    Method* init;
+    JValue unused;
+
+    ALOGV("+++ finishing prep on main VM thread");
+
+    /* main thread is always first in list at this point */
+    thread = gDvm.threadList;
+    assert(thread->threadId == kMainThreadId);
+
+    /*
+     * Make sure the classes are initialized.  We have to do this before
+     * we create an instance of them.
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        ALOGE("'Class' class failed to initialize");
+        return false;
+    }
+    if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
+        !dvmInitClass(gDvm.classJavaLangThread) ||
+        !dvmInitClass(gDvm.classJavaLangVMThread))
+    {
+        ALOGE("thread classes failed to initialize");
+        return false;
+    }
+
+    groupObj = dvmGetMainThreadGroup();
+    if (groupObj == NULL)
+        return false;
+
+    /*
+     * Allocate and construct a Thread with the internal-creation
+     * constructor.
+     */
+    threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+    if (threadObj == NULL) {
+        ALOGE("unable to allocate main thread object");
+        return false;
+    }
+    dvmReleaseTrackedAlloc(threadObj, NULL);
+
+    threadNameStr = dvmCreateStringFromCstr("main");
+    if (threadNameStr == NULL)
+        return false;
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+            "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+    assert(init != NULL);
+    dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
+        THREAD_NORM_PRIORITY, false);
+    if (dvmCheckException(thread)) {
+        ALOGE("exception thrown while constructing main thread object");
+        return false;
+    }
+
+    /*
+     * Allocate and construct a VMThread.
+     */
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL) {
+        ALOGE("unable to allocate main vmthread object");
+        return false;
+    }
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
+            "(Ljava/lang/Thread;)V");
+    dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
+    if (dvmCheckException(thread)) {
+        ALOGE("exception thrown while constructing main vmthread object");
+        return false;
+    }
+
+    /* set the VMThread.vmData field to our Thread struct */
+    assert(gDvm.offJavaLangVMThread_vmData != 0);
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
+
+    /*
+     * Stuff the VMThread back into the Thread.  From this point on, other
+     * Threads will see that this Thread is running (at least, they would,
+     * if there were any).
+     */
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
+        vmThreadObj);
+
+    thread->threadObj = threadObj;
+
+    /*
+     * Set the "context class loader" field in the system class loader.
+     *
+     * Retrieving the system class loader will cause invocation of
+     * ClassLoader.getSystemClassLoader(), which could conceivably call
+     * Thread.currentThread(), so we want the Thread to be fully configured
+     * before we do this.
+     */
+    Object* systemLoader = dvmGetSystemClassLoader();
+    if (systemLoader == NULL) {
+        ALOGW("WARNING: system class loader is NULL (setting main ctxt)");
+        /* keep going? */
+    } else {
+        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_contextClassLoader,
+            systemLoader);
+        dvmReleaseTrackedAlloc(systemLoader, NULL);
+    }
+
+    /* include self in non-daemon threads (mainly for AttachCurrentThread) */
+    gDvm.nonDaemonThreadCount++;
+
+    return true;
+}
+
+
+/*
+ * Alloc and initialize a Thread struct.
+ *
+ * Does not create any objects, just stuff on the system (malloc) heap.
+ */
+static Thread* allocThread(int interpStackSize)
+{
+    Thread* thread;
+    u1* stackBottom;
+
+    thread = (Thread*) calloc(1, sizeof(Thread));
+    if (thread == NULL)
+        return NULL;
+
+    /* Check sizes and alignment */
+    assert((((uintptr_t)&thread->interpBreak.all) & 0x7) == 0);
+    assert(sizeof(thread->interpBreak) == sizeof(thread->interpBreak.all));
+
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (dvmSelfVerificationShadowSpaceAlloc(thread) == NULL)
+        return NULL;
+#endif
+
+    assert(interpStackSize >= kMinStackSize && interpStackSize <=kMaxStackSize);
+
+    thread->status = THREAD_INITIALIZING;
+
+    /*
+     * Allocate and initialize the interpreted code stack.  We essentially
+     * "lose" the alloc pointer, which points at the bottom of the stack,
+     * but we can get it back later because we know how big the stack is.
+     *
+     * The stack must be aligned on a 4-byte boundary.
+     */
+#ifdef MALLOC_INTERP_STACK
+    stackBottom = (u1*) malloc(interpStackSize);
+    if (stackBottom == NULL) {
+#if defined(WITH_SELF_VERIFICATION)
+        dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+        free(thread);
+        return NULL;
+    }
+    memset(stackBottom, 0xc5, interpStackSize);     // stop valgrind complaints
+#else
+    stackBottom = (u1*) mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (stackBottom == MAP_FAILED) {
+#if defined(WITH_SELF_VERIFICATION)
+        dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+        free(thread);
+        return NULL;
+    }
+#endif
+
+    assert(((u4)stackBottom & 0x03) == 0); // looks like our malloc ensures this
+    thread->interpStackSize = interpStackSize;
+    thread->interpStackStart = stackBottom + interpStackSize;
+    thread->interpStackEnd = stackBottom + STACK_OVERFLOW_RESERVE;
+
+#ifndef DVM_NO_ASM_INTERP
+    thread->mainHandlerTable = dvmAsmInstructionStart;
+    thread->altHandlerTable = dvmAsmAltInstructionStart;
+    thread->interpBreak.ctl.curHandlerTable = thread->mainHandlerTable;
+#endif
+
+    /* give the thread code a chance to set things up */
+    dvmInitInterpStack(thread, interpStackSize);
+
+    /* One-time setup for interpreter/JIT state */
+    dvmInitInterpreterState(thread);
+
+    return thread;
+}
+
+/*
+ * Get a meaningful thread ID.  At present this only has meaning under Linux,
+ * where getpid() and gettid() sometimes agree and sometimes don't depending
+ * on your thread model (try "export LD_ASSUME_KERNEL=2.4.19").
+ */
+pid_t dvmGetSysThreadId()
+{
+#ifdef HAVE_GETTID
+    return gettid();
+#else
+    return getpid();
+#endif
+}
+
+/*
+ * Finish initialization of a Thread struct.
+ *
+ * This must be called while executing in the new thread, but before the
+ * thread is added to the thread list.
+ *
+ * NOTE: The threadListLock must be held by the caller (needed for
+ * assignThreadId()).
+ */
+static bool prepareThread(Thread* thread)
+{
+    assignThreadId(thread);
+    thread->handle = pthread_self();
+    thread->systemTid = dvmGetSysThreadId();
+
+    //ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid,
+    //    (int) getpid());
+    /*
+     * If we were called by dvmAttachCurrentThread, the self value is
+     * already correctly established as "thread".
+     */
+    setThreadSelf(thread);
+
+    ALOGV("threadid=%d: interp stack at %p",
+        thread->threadId, thread->interpStackStart - thread->interpStackSize);
+
+    /*
+     * Initialize invokeReq.
+     */
+    dvmInitMutex(&thread->invokeReq.lock);
+    pthread_cond_init(&thread->invokeReq.cv, NULL);
+
+    /*
+     * Initialize our reference tracking tables.
+     *
+     * Most threads won't use jniMonitorRefTable, so we clear out the
+     * structure but don't call the init function (which allocs storage).
+     */
+    if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
+            kJniLocalRefMax, kIndirectKindLocal)) {
+        return false;
+    }
+    if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
+            kInternalRefDefault, kInternalRefMax))
+        return false;
+
+    memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
+
+    dvmInitCondForTimedWait(&thread->waitCond);
+    dvmInitMutex(&thread->waitMutex);
+
+    /* Initialize safepoint callback mechanism */
+    dvmInitMutex(&thread->callbackMutex);
+
+    return true;
+}
+
+/*
+ * Remove a thread from the internal list.
+ * Clear out the links to make it obvious that the thread is
+ * no longer on the list.  Caller must hold gDvm.threadListLock.
+ */
+static void unlinkThread(Thread* thread)
+{
+    LOG_THREAD("threadid=%d: removing from list", thread->threadId);
+    if (thread == gDvm.threadList) {
+        assert(thread->prev == NULL);
+        gDvm.threadList = thread->next;
+    } else {
+        assert(thread->prev != NULL);
+        thread->prev->next = thread->next;
+    }
+    if (thread->next != NULL)
+        thread->next->prev = thread->prev;
+    thread->prev = thread->next = NULL;
+}
+
+/*
+ * Free a Thread struct, and all the stuff allocated within.
+ */
+static void freeThread(Thread* thread)
+{
+    if (thread == NULL)
+        return;
+
+    /* thread->threadId is zero at this point */
+    LOGVV("threadid=%d: freeing", thread->threadId);
+
+    if (thread->interpStackStart != NULL) {
+        u1* interpStackBottom;
+
+        interpStackBottom = thread->interpStackStart;
+        interpStackBottom -= thread->interpStackSize;
+#ifdef MALLOC_INTERP_STACK
+        free(interpStackBottom);
+#else
+        if (munmap(interpStackBottom, thread->interpStackSize) != 0)
+            ALOGW("munmap(thread stack) failed");
+#endif
+    }
+
+    thread->jniLocalRefTable.destroy();
+    dvmClearReferenceTable(&thread->internalLocalRefTable);
+    if (&thread->jniMonitorRefTable.table != NULL)
+        dvmClearReferenceTable(&thread->jniMonitorRefTable);
+
+#if defined(WITH_SELF_VERIFICATION)
+    dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+    free(thread->stackTraceSample);
+    free(thread);
+}
+
+/*
+ * Like pthread_self(), but on a Thread*.
+ */
+Thread* dvmThreadSelf()
+{
+    return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
+}
+
+/*
+ * Explore our sense of self.  Stuffs the thread pointer into TLS.
+ */
+static void setThreadSelf(Thread* thread)
+{
+    int cc;
+
+    cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
+    if (cc != 0) {
+        /*
+         * Sometimes this fails under Bionic with EINVAL during shutdown.
+         * This can happen if the timing is just right, e.g. a thread
+         * fails to attach during shutdown, but the "fail" path calls
+         * here to ensure we clean up after ourselves.
+         */
+        if (thread != NULL) {
+            ALOGE("pthread_setspecific(%p) failed, err=%d", thread, cc);
+            dvmAbort();     /* the world is fundamentally hosed */
+        }
+    }
+}
+
+/*
+ * This is associated with the pthreadKeySelf key.  It's called by the
+ * pthread library when a thread is exiting and the "self" pointer in TLS
+ * is non-NULL, meaning the VM hasn't had a chance to clean up.  In normal
+ * operation this will not be called.
+ *
+ * This is mainly of use to ensure that we don't leak resources if, for
+ * example, a thread attaches itself to us with AttachCurrentThread and
+ * then exits without notifying the VM.
+ *
+ * We could do the detach here instead of aborting, but this will lead to
+ * portability problems.  Other implementations do not do this check and
+ * will simply be unaware that the thread has exited, leading to resource
+ * leaks (and, if this is a non-daemon thread, an infinite hang when the
+ * VM tries to shut down).
+ *
+ * Because some implementations may want to use the pthread destructor
+ * to initiate the detach, and the ordering of destructors is not defined,
+ * we want to iterate a couple of times to give those a chance to run.
+ */
+static void threadExitCheck(void* arg)
+{
+    const int kMaxCount = 2;
+
+    Thread* self = (Thread*) arg;
+    assert(self != NULL);
+
+    ALOGV("threadid=%d: threadExitCheck(%p) count=%d",
+        self->threadId, arg, self->threadExitCheckCount);
+
+    if (self->status == THREAD_ZOMBIE) {
+        ALOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck",
+            self->threadId);
+        return;
+    }
+
+    if (self->threadExitCheckCount < kMaxCount) {
+        /*
+         * Spin a couple of times to let other destructors fire.
+         */
+        ALOGD("threadid=%d: thread exiting, not yet detached (count=%d)",
+            self->threadId, self->threadExitCheckCount);
+        self->threadExitCheckCount++;
+        int cc = pthread_setspecific(gDvm.pthreadKeySelf, self);
+        if (cc != 0) {
+            ALOGE("threadid=%d: unable to re-add thread to TLS",
+                self->threadId);
+            dvmAbort();
+        }
+    } else {
+        ALOGE("threadid=%d: native thread exited without detaching",
+            self->threadId);
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Assign the threadId.  This needs to be a small integer so that our
+ * "thin" locks fit in a small number of bits.
+ *
+ * We reserve zero for use as an invalid ID.
+ *
+ * This must be called with threadListLock held.
+ */
+static void assignThreadId(Thread* thread)
+{
+    /*
+     * Find a small unique integer.  threadIdMap is a vector of
+     * kMaxThreadId bits;  dvmAllocBit() returns the index of a
+     * bit, meaning that it will always be < kMaxThreadId.
+     */
+    int num = dvmAllocBit(gDvm.threadIdMap);
+    if (num < 0) {
+        ALOGE("Ran out of thread IDs");
+        dvmAbort();     // TODO: make this a non-fatal error result
+    }
+
+    thread->threadId = num + 1;
+
+    assert(thread->threadId != 0);
+}
+
+/*
+ * Give back the thread ID.
+ */
+static void releaseThreadId(Thread* thread)
+{
+    assert(thread->threadId > 0);
+    dvmClearBit(gDvm.threadIdMap, thread->threadId - 1);
+    thread->threadId = 0;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native code in the main
+ * thread was originally invoked from interpreted code.  This gives us a
+ * place to hang JNI local references.  The VM spec says (v2 5.2) that the
+ * VM begins by executing "main" in a class, so in a way this brings us
+ * closer to the spec.
+ */
+static bool createFakeEntryFrame(Thread* thread)
+{
+    /*
+     * Because we are creating a frame that represents application code, we
+     * want to stuff the application class loader into the method's class
+     * loader field, even though we're using the system class loader to
+     * load it.  This makes life easier over in JNI FindClass (though it
+     * could bite us in other ways).
+     *
+     * Unfortunately this is occurring too early in the initialization,
+     * of necessity coming before JNI is initialized, and we're not quite
+     * ready to set up the application class loader.  Also, overwriting
+     * the class' defining classloader pointer seems unwise.
+     *
+     * Instead, we save a pointer to the method and explicitly check for
+     * it in FindClass.  The method is private so nobody else can call it.
+     */
+
+    assert(thread->threadId == kMainThreadId);      /* main thread only */
+
+    if (!dvmPushJNIFrame(thread, gDvm.methDalvikSystemNativeStart_main))
+        return false;
+
+    /*
+     * Null out the "String[] args" argument.
+     */
+    assert(gDvm.methDalvikSystemNativeStart_main->registersSize == 1);
+    u4* framePtr = (u4*) thread->interpSave.curFrame;
+    framePtr[0] = 0;
+
+    return true;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native thread has been
+ * executing interpreted code.  This gives us a place to hang JNI local
+ * references.
+ */
+static bool createFakeRunFrame(Thread* thread)
+{
+    return dvmPushJNIFrame(thread, gDvm.methDalvikSystemNativeStart_run);
+}
+
+/*
+ * Helper function to set the name of the current thread
+ */
+void dvmSetThreadName(const char *threadName)
+{
+    int hasAt = 0;
+    int hasDot = 0;
+    const char *s = threadName;
+
+    if (s == NULL) {
+        ALOGW("Unable to set the name of current thread to NULL");
+        return;
+    }
+
+    while (*s) {
+        if (*s == '.') hasDot = 1;
+        else if (*s == '@') hasAt = 1;
+        s++;
+    }
+    int len = s - threadName;
+    if (len < 15 || hasAt || !hasDot) {
+        s = threadName;
+    } else {
+        s = threadName + len - 15;
+    }
+#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+    /* pthread_setname_np fails rather than truncating long strings */
+    char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+    strncpy(buf, s, sizeof(buf)-1);
+    buf[sizeof(buf)-1] = '\0';
+    int err = pthread_setname_np(pthread_self(), buf);
+    if (err != 0) {
+        ALOGW("Unable to set the name of current thread to '%s': %s",
+            buf, strerror(err));
+    }
+#elif defined(HAVE_PRCTL)
+    prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#else
+    ALOGD("No way to set current thread's name (%s)", s);
+#endif
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ *
+ * We do have to worry about some concurrency problems, e.g. programs
+ * that try to call Thread.start() on the same object from multiple threads.
+ * (This will fail for all but one, but we have to make sure that it succeeds
+ * for exactly one.)
+ *
+ * Some of the complexity here arises from our desire to mimic the
+ * Thread vs. VMThread class decomposition we inherited.  We've been given
+ * a Thread, and now we need to create a VMThread and then populate both
+ * objects.  We also need to create one of our internal Thread objects.
+ *
+ * Pass in a stack size of 0 to get the default.
+ *
+ * The "threadObj" reference must be pinned by the caller to prevent the GC
+ * from moving it around (e.g. added to the tracked allocation list).
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
+{
+    assert(threadObj != NULL);
+
+    Thread* self = dvmThreadSelf();
+    int stackSize;
+    if (reqStackSize == 0)
+        stackSize = gDvm.stackSize;
+    else if (reqStackSize < kMinStackSize)
+        stackSize = kMinStackSize;
+    else if (reqStackSize > kMaxStackSize)
+        stackSize = kMaxStackSize;
+    else
+        stackSize = reqStackSize;
+
+    pthread_attr_t threadAttr;
+    pthread_attr_init(&threadAttr);
+    pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+    /*
+     * To minimize the time spent in the critical section, we allocate the
+     * vmThread object here.
+     */
+    Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL)
+        return false;
+
+    Thread* newThread = allocThread(stackSize);
+    if (newThread == NULL) {
+        dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+        return false;
+    }
+
+    newThread->threadObj = threadObj;
+
+    assert(newThread->status == THREAD_INITIALIZING);
+
+    /*
+     * We need to lock out other threads while we test and set the
+     * "vmThread" field in java.lang.Thread, because we use that to determine
+     * if this thread has been started before.  We use the thread list lock
+     * because it's handy and we're going to need to grab it again soon
+     * anyway.
+     */
+    dvmLockThreadList(self);
+
+    if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+        dvmUnlockThreadList();
+        dvmThrowIllegalThreadStateException(
+            "thread has already been started");
+        freeThread(newThread);
+        dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+        return false;
+    }
+
+    /*
+     * There are actually three data structures: Thread (object), VMThread
+     * (object), and Thread (C struct).  All of them point to at least one
+     * other.
+     *
+     * As soon as "VMThread.vmData" is assigned, other threads can start
+     * making calls into us (e.g. setPriority).
+     */
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+    /*
+     * Thread creation might take a while, so release the lock.
+     */
+    dvmUnlockThreadList();
+
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    pthread_t threadHandle;
+    int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);
+    pthread_attr_destroy(&threadAttr);
+    dvmChangeStatus(self, oldStatus);
+
+    if (cc != 0) {
+        /*
+         * Failure generally indicates that we have exceeded system
+         * resource limits.  VirtualMachineError is probably too severe,
+         * so use OutOfMemoryError.
+         */
+
+        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+        ALOGE("pthread_create (stack size %d bytes) failed: %s", stackSize, strerror(cc));
+        dvmThrowExceptionFmt(gDvm.exOutOfMemoryError,
+                             "pthread_create (stack size %d bytes) failed: %s",
+                             stackSize, strerror(cc));
+        goto fail;
+    }
+
+    /*
+     * We need to wait for the thread to start.  Otherwise, depending on
+     * the whims of the OS scheduler, we could return and the code in our
+     * thread could try to do operations on the new thread before it had
+     * finished starting.
+     *
+     * The new thread will lock the thread list, change its state to
+     * THREAD_STARTING, broadcast to gDvm.threadStartCond, and then sleep
+     * on gDvm.threadStartCond (which uses the thread list lock).  This
+     * thread (the parent) will either see that the thread is already ready
+     * after we grab the thread list lock, or will be awakened from the
+     * condition variable on the broadcast.
+     *
+     * We don't want to stall the rest of the VM while the new thread
+     * starts, which can happen if the GC wakes up at the wrong moment.
+     * So, we change our own status to VMWAIT, and self-suspend if
+     * necessary after we finish adding the new thread.
+     *
+     *
+     * We have to deal with an odd race with the GC/debugger suspension
+     * mechanism when creating a new thread.  The information about whether
+     * or not a thread should be suspended is contained entirely within
+     * the Thread struct; this is usually cleaner to deal with than having
+     * one or more globally-visible suspension flags.  The trouble is that
+     * we could create the thread while the VM is trying to suspend all
+     * threads.  The suspend-count won't be nonzero for the new thread,
+     * so dvmChangeStatus(THREAD_RUNNING) won't cause a suspension.
+     *
+     * The easiest way to deal with this is to prevent the new thread from
+     * running until the parent says it's okay.  This results in the
+     * following (correct) sequence of events for a "badly timed" GC
+     * (where '-' is us, 'o' is the child, and '+' is some other thread):
+     *
+     *  - call pthread_create()
+     *  - lock thread list
+     *  - put self into THREAD_VMWAIT so GC doesn't wait for us
+     *  - sleep on condition var (mutex = thread list lock) until child starts
+     *  + GC triggered by another thread
+     *  + thread list locked; suspend counts updated; thread list unlocked
+     *  + loop waiting for all runnable threads to suspend
+     *  + success, start GC
+     *  o child thread wakes, signals condition var to wake parent
+     *  o child waits for parent ack on condition variable
+     *  - we wake up, locking thread list
+     *  - add child to thread list
+     *  - unlock thread list
+     *  - change our state back to THREAD_RUNNING; GC causes us to suspend
+     *  + GC finishes; all threads in thread list are resumed
+     *  - lock thread list
+     *  - set child to THREAD_VMWAIT, and signal it to start
+     *  - unlock thread list
+     *  o child resumes
+     *  o child changes state to THREAD_RUNNING
+     *
+     * The above shows the GC starting up during thread creation, but if
+     * it starts anywhere after VMThread.create() is called it will
+     * produce the same series of events.
+     *
+     * Once the child is in the thread list, it will be suspended and
+     * resumed like any other thread.  In the above scenario the resume-all
+     * code will try to resume the new thread, which was never actually
+     * suspended, and try to decrement the child's thread suspend count to -1.
+     * We can catch this in the resume-all code.
+     *
+     * Bouncing back and forth between threads like this adds a small amount
+     * of scheduler overhead to thread startup.
+     *
+     * One alternative to having the child wait for the parent would be
+     * to have the child inherit the parents' suspension count.  This
+     * would work for a GC, since we can safely assume that the parent
+     * thread didn't cause it, but we must only do so if the parent suspension
+     * was caused by a suspend-all.  If the parent was being asked to
+     * suspend singly by the debugger, the child should not inherit the value.
+     *
+     * We could also have a global "new thread suspend count" that gets
+     * picked up by new threads before changing state to THREAD_RUNNING.
+     * This would be protected by the thread list lock and set by a
+     * suspend-all.
+     */
+    dvmLockThreadList(self);
+    assert(self->status == THREAD_RUNNING);
+    self->status = THREAD_VMWAIT;
+    while (newThread->status != THREAD_STARTING)
+        pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+    LOG_THREAD("threadid=%d: adding to list", newThread->threadId);
+    newThread->next = gDvm.threadList->next;
+    if (newThread->next != NULL)
+        newThread->next->prev = newThread;
+    newThread->prev = gDvm.threadList;
+    gDvm.threadList->next = newThread;
+
+    /* Add any existing global modes to the interpBreak control */
+    dvmInitializeInterpBreak(newThread);
+
+    if (!dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon))
+        gDvm.nonDaemonThreadCount++;        // guarded by thread list lock
+
+    dvmUnlockThreadList();
+
+    /* change status back to RUNNING, self-suspending if necessary */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Tell the new thread to start.
+     *
+     * We must hold the thread list lock before messing with another thread.
+     * In the general case we would also need to verify that newThread was
+     * still in the thread list, but in our case the thread has not started
+     * executing user code and therefore has not had a chance to exit.
+     *
+     * We move it to VMWAIT, and it then shifts itself to RUNNING, which
+     * comes with a suspend-pending check.
+     */
+    dvmLockThreadList(self);
+
+    assert(newThread->status == THREAD_STARTING);
+    newThread->status = THREAD_VMWAIT;
+    pthread_cond_broadcast(&gDvm.threadStartCond);
+
+    dvmUnlockThreadList();
+
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+    return true;
+
+fail:
+    freeThread(newThread);
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+    return false;
+}
+
+/*
+ * pthread entry function for threads started from interpreted code.
+ */
+static void* interpThreadStart(void* arg)
+{
+    Thread* self = (Thread*) arg;
+
+    std::string threadName(dvmGetThreadName(self));
+    dvmSetThreadName(threadName.c_str());
+
+    /*
+     * Finish initializing the Thread struct.
+     */
+    dvmLockThreadList(self);
+    prepareThread(self);
+
+    LOG_THREAD("threadid=%d: created from interp", self->threadId);
+
+    /*
+     * Change our status and wake our parent, who will add us to the
+     * thread list and advance our state to VMWAIT.
+     */
+    self->status = THREAD_STARTING;
+    pthread_cond_broadcast(&gDvm.threadStartCond);
+
+    /*
+     * Wait until the parent says we can go.  Assuming there wasn't a
+     * suspend pending, this will happen immediately.  When it completes,
+     * we're full-fledged citizens of the VM.
+     *
+     * We have to use THREAD_VMWAIT here rather than THREAD_RUNNING
+     * because the pthread_cond_wait below needs to reacquire a lock that
+     * suspend-all is also interested in.  If we get unlucky, the parent could
+     * change us to THREAD_RUNNING, then a GC could start before we get
+     * signaled, and suspend-all will grab the thread list lock and then
+     * wait for us to suspend.  We'll be in the tail end of pthread_cond_wait
+     * trying to get the lock.
+     */
+    while (self->status != THREAD_VMWAIT)
+        pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+    dvmUnlockThreadList();
+
+    /*
+     * Add a JNI context.
+     */
+    self->jniEnv = dvmCreateJNIEnv(self);
+
+    /*
+     * Change our state so the GC will wait for us from now on.  If a GC is
+     * in progress this call will suspend us.
+     */
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Notify the debugger & DDM.  The debugger notification may cause
+     * us to suspend ourselves (and others).  The thread state may change
+     * to VMWAIT briefly if network packets are sent.
+     */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadStart(self);
+
+    /*
+     * Set the system thread priority according to the Thread object's
+     * priority level.  We don't usually need to do this, because both the
+     * Thread object and system thread priorities inherit from parents.  The
+     * tricky case is when somebody creates a Thread object, calls
+     * setPriority(), and then starts the thread.  We could manage this with
+     * a "needs priority update" flag to avoid the redundant call.
+     */
+    int priority = dvmGetFieldInt(self->threadObj,
+                        gDvm.offJavaLangThread_priority);
+    dvmChangeThreadPriority(self, priority);
+
+    /*
+     * Execute the "run" method.
+     *
+     * At this point our stack is empty, so somebody who comes looking for
+     * stack traces right now won't have much to look at.  This is normal.
+     */
+    Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
+    JValue unused;
+
+    ALOGV("threadid=%d: calling run()", self->threadId);
+    assert(strcmp(run->name, "run") == 0);
+    dvmCallMethod(self, run, self->threadObj, &unused);
+    ALOGV("threadid=%d: exiting", self->threadId);
+
+    /*
+     * Remove the thread from various lists, report its death, and free
+     * its resources.
+     */
+    dvmDetachCurrentThread();
+
+    return NULL;
+}
+
+/*
+ * The current thread is exiting with an uncaught exception.  The
+ * Java programming language allows the application to provide a
+ * thread-exit-uncaught-exception handler for the VM, for a specific
+ * Thread, and for all threads in a ThreadGroup.
+ *
+ * Version 1.5 added the per-thread handler.  We need to call
+ * "uncaughtException" in the handler object, which is either the
+ * ThreadGroup object or the Thread-specific handler.
+ *
+ * This should only be called when an exception is pending.  Before
+ * returning, the exception will be cleared.
+ */
+static void threadExitUncaughtException(Thread* self, Object* group)
+{
+    Object* exception;
+    Object* handlerObj;
+    Method* uncaughtHandler;
+
+    ALOGW("threadid=%d: thread exiting with uncaught exception (group=%p)",
+        self->threadId, group);
+    assert(group != NULL);
+
+    /*
+     * Get a pointer to the exception, then clear out the one in the
+     * thread.  We don't want to have it set when executing interpreted code.
+     */
+    exception = dvmGetException(self);
+    assert(exception != NULL);
+    dvmAddTrackedAlloc(exception, self);
+    dvmClearException(self);
+
+    /*
+     * Get the Thread's "uncaughtHandler" object.  Use it if non-NULL;
+     * else use "group" (which is an instance of UncaughtExceptionHandler).
+     * The ThreadGroup will handle it directly or call the default
+     * uncaught exception handler.
+     */
+    handlerObj = dvmGetFieldObject(self->threadObj,
+            gDvm.offJavaLangThread_uncaughtHandler);
+    if (handlerObj == NULL)
+        handlerObj = group;
+
+    /*
+     * Find the "uncaughtException" method in this object.  The method
+     * was declared in the Thread.UncaughtExceptionHandler interface.
+     */
+    uncaughtHandler = dvmFindVirtualMethodHierByDescriptor(handlerObj->clazz,
+            "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+    if (uncaughtHandler != NULL) {
+        //ALOGI("+++ calling %s.uncaughtException",
+        //     handlerObj->clazz->descriptor);
+        JValue unused;
+        dvmCallMethod(self, uncaughtHandler, handlerObj, &unused,
+            self->threadObj, exception);
+    } else {
+        /* should be impossible, but handle it anyway */
+        ALOGW("WARNING: no 'uncaughtException' method in class %s",
+            handlerObj->clazz->descriptor);
+        dvmSetException(self, exception);
+        dvmLogExceptionStackTrace();
+    }
+
+    /* if the uncaught handler threw, clear it */
+    dvmClearException(self);
+
+    dvmReleaseTrackedAlloc(exception, self);
+
+    /* Remove this thread's suspendCount from global suspendCount sum */
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(self, -self->suspendCount, 0);
+    unlockThreadSuspendCount();
+}
+
+
+/*
+ * Create an internal VM thread, for things like JDWP and finalizers.
+ *
+ * The easiest way to do this is create a new thread and then use the
+ * JNI AttachCurrentThread implementation.
+ *
+ * This does not return until after the new thread has begun executing.
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+    InternalThreadStart func, void* funcArg)
+{
+    InternalStartArgs* pArgs;
+    Object* systemGroup;
+    volatile Thread* newThread = NULL;
+    volatile int createStatus = 0;
+
+    systemGroup = dvmGetSystemThreadGroup();
+    if (systemGroup == NULL)
+        return false;
+
+    pArgs = (InternalStartArgs*) malloc(sizeof(*pArgs));
+    pArgs->func = func;
+    pArgs->funcArg = funcArg;
+    pArgs->name = strdup(name);     // storage will be owned by new thread
+    pArgs->group = systemGroup;
+    pArgs->isDaemon = true;
+    pArgs->pThread = &newThread;
+    pArgs->pCreateStatus = &createStatus;
+
+    pthread_attr_t threadAttr;
+    pthread_attr_init(&threadAttr);
+
+    int cc = pthread_create(pHandle, &threadAttr, internalThreadStart, pArgs);
+    pthread_attr_destroy(&threadAttr);
+    if (cc != 0) {
+        ALOGE("internal thread creation failed: %s", strerror(cc));
+        free(pArgs->name);
+        free(pArgs);
+        return false;
+    }
+
+    /*
+     * Wait for the child to start.  This gives us an opportunity to make
+     * sure that the thread started correctly, and allows our caller to
+     * assume that the thread has started running.
+     *
+     * Because we aren't holding a lock across the thread creation, it's
+     * possible that the child will already have completed its
+     * initialization.  Because the child only adjusts "createStatus" while
+     * holding the thread list lock, the initial condition on the "while"
+     * loop will correctly avoid the wait if this occurs.
+     *
+     * It's also possible that we'll have to wait for the thread to finish
+     * being created, and as part of allocating a Thread object it might
+     * need to initiate a GC.  We switch to VMWAIT while we pause.
+     */
+    Thread* self = dvmThreadSelf();
+    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    dvmLockThreadList(self);
+    while (createStatus == 0)
+        pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+    if (newThread == NULL) {
+        ALOGW("internal thread create failed (createStatus=%d)", createStatus);
+        assert(createStatus < 0);
+        /* don't free pArgs -- if pthread_create succeeded, child owns it */
+        dvmUnlockThreadList();
+        dvmChangeStatus(self, oldStatus);
+        return false;
+    }
+
+    /* thread could be in any state now (except early init states) */
+    //assert(newThread->status == THREAD_RUNNING);
+
+    dvmUnlockThreadList();
+    dvmChangeStatus(self, oldStatus);
+
+    return true;
+}
+
+/*
+ * pthread entry function for internally-created threads.
+ *
+ * We are expected to free "arg" and its contents.  If we're a daemon
+ * thread, and we get cancelled abruptly when the VM shuts down, the
+ * storage won't be freed.  If this becomes a concern we can make a copy
+ * on the stack.
+ */
+static void* internalThreadStart(void* arg)
+{
+    InternalStartArgs* pArgs = (InternalStartArgs*) arg;
+    JavaVMAttachArgs jniArgs;
+
+    jniArgs.version = JNI_VERSION_1_2;
+    jniArgs.name = pArgs->name;
+    jniArgs.group = reinterpret_cast<jobject>(pArgs->group);
+
+    dvmSetThreadName(pArgs->name);
+
+    /* use local jniArgs as stack top */
+    if (dvmAttachCurrentThread(&jniArgs, pArgs->isDaemon)) {
+        /*
+         * Tell the parent of our success.
+         *
+         * threadListLock is the mutex for threadStartCond.
+         */
+        dvmLockThreadList(dvmThreadSelf());
+        *pArgs->pCreateStatus = 1;
+        *pArgs->pThread = dvmThreadSelf();
+        pthread_cond_broadcast(&gDvm.threadStartCond);
+        dvmUnlockThreadList();
+
+        LOG_THREAD("threadid=%d: internal '%s'",
+            dvmThreadSelf()->threadId, pArgs->name);
+
+        /* execute */
+        (*pArgs->func)(pArgs->funcArg);
+
+        /* detach ourselves */
+        dvmDetachCurrentThread();
+    } else {
+        /*
+         * Tell the parent of our failure.  We don't have a Thread struct,
+         * so we can't be suspended, so we don't need to enter a critical
+         * section.
+         */
+        dvmLockThreadList(dvmThreadSelf());
+        *pArgs->pCreateStatus = -1;
+        assert(*pArgs->pThread == NULL);
+        pthread_cond_broadcast(&gDvm.threadStartCond);
+        dvmUnlockThreadList();
+
+        assert(*pArgs->pThread == NULL);
+    }
+
+    free(pArgs->name);
+    free(pArgs);
+    return NULL;
+}
+
+/*
+ * Attach the current thread to the VM.
+ *
+ * Used for internally-created threads and JNI's AttachCurrentThread.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
+{
+    Thread* self = NULL;
+    Object* threadObj = NULL;
+    Object* vmThreadObj = NULL;
+    StringObject* threadNameStr = NULL;
+    Method* init;
+    bool ok, ret;
+
+    /* allocate thread struct, and establish a basic sense of self */
+    self = allocThread(gDvm.stackSize);
+    if (self == NULL)
+        goto fail;
+    setThreadSelf(self);
+
+    /*
+     * Finish our thread prep.  We need to do this before adding ourselves
+     * to the thread list or invoking any interpreted code.  prepareThread()
+     * requires that we hold the thread list lock.
+     */
+    dvmLockThreadList(self);
+    ok = prepareThread(self);
+    dvmUnlockThreadList();
+    if (!ok)
+        goto fail;
+
+    self->jniEnv = dvmCreateJNIEnv(self);
+    if (self->jniEnv == NULL)
+        goto fail;
+
+    /*
+     * Create a "fake" JNI frame at the top of the main thread interp stack.
+     * It isn't really necessary for the internal threads, but it gives
+     * the debugger something to show.  It is essential for the JNI-attached
+     * threads.
+     */
+    if (!createFakeRunFrame(self))
+        goto fail;
+
+    /*
+     * The native side of the thread is ready; add it to the list.  Once
+     * it's on the list the thread is visible to the JDWP code and the GC.
+     */
+    LOG_THREAD("threadid=%d: adding to list (attached)", self->threadId);
+
+    dvmLockThreadList(self);
+
+    self->next = gDvm.threadList->next;
+    if (self->next != NULL)
+        self->next->prev = self;
+    self->prev = gDvm.threadList;
+    gDvm.threadList->next = self;
+    if (!isDaemon)
+        gDvm.nonDaemonThreadCount++;
+
+    dvmUnlockThreadList();
+
+    /*
+     * Switch state from initializing to running.
+     *
+     * It's possible that a GC began right before we added ourselves
+     * to the thread list, and is still going.  That means our thread
+     * suspend count won't reflect the fact that we should be suspended.
+     * To deal with this, we transition to VMWAIT, pulse the heap lock,
+     * and then advance to RUNNING.  That will ensure that we stall until
+     * the GC completes.
+     *
+     * Once we're in RUNNING, we're like any other thread in the VM (except
+     * for the lack of an initialized threadObj).  We're then free to
+     * allocate and initialize objects.
+     */
+    assert(self->status == THREAD_INITIALIZING);
+    dvmChangeStatus(self, THREAD_VMWAIT);
+    dvmLockMutex(&gDvm.gcHeapLock);
+    dvmUnlockMutex(&gDvm.gcHeapLock);
+    dvmChangeStatus(self, THREAD_RUNNING);
+
+    /*
+     * Create Thread and VMThread objects.
+     */
+    threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (threadObj == NULL || vmThreadObj == NULL)
+        goto fail_unlink;
+
+    /*
+     * This makes threadObj visible to the GC.  We still have it in the
+     * tracked allocation table, so it can't move around on us.
+     */
+    self->threadObj = threadObj;
+    dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)self);
+
+    /*
+     * Create a string for the thread name.
+     */
+    if (pArgs->name != NULL) {
+        threadNameStr = dvmCreateStringFromCstr(pArgs->name);
+        if (threadNameStr == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            goto fail_unlink;
+        }
+    }
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+            "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+    if (init == NULL) {
+        assert(dvmCheckException(self));
+        goto fail_unlink;
+    }
+
+    /*
+     * Now we're ready to run some interpreted code.
+     *
+     * We need to construct the Thread object and set the VMThread field.
+     * Setting VMThread tells interpreted code that we're alive.
+     *
+     * Call the (group, name, priority, daemon) constructor on the Thread.
+     * This sets the thread's name and adds it to the specified group, and
+     * provides values for priority and daemon (which are normally inherited
+     * from the current thread).
+     */
+    JValue unused;
+    dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group,
+            threadNameStr, os_getThreadPriorityFromSystem(), isDaemon);
+    if (dvmCheckException(self)) {
+        ALOGE("exception thrown while constructing attached thread object");
+        goto fail_unlink;
+    }
+
+    /*
+     * Set the VMThread field, which tells interpreted code that we're alive.
+     *
+     * The risk of a thread start collision here is very low; somebody
+     * would have to be deliberately polling the ThreadGroup list and
+     * trying to start threads against anything it sees, which would
+     * generally cause problems for all thread creation.  However, for
+     * correctness we test "vmThread" before setting it.
+     *
+     * TODO: this still has a race, it's just smaller.  Not sure this is
+     * worth putting effort into fixing.  Need to hold a lock while
+     * fiddling with the field, or maybe initialize the Thread object in a
+     * way that ensures another thread can't call start() on it.
+     */
+    if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+        ALOGW("WOW: thread start hijack");
+        dvmThrowIllegalThreadStateException(
+            "thread has already been started");
+        /* We don't want to free anything associated with the thread
+         * because someone is obviously interested in it.  Just let
+         * it go and hope it will clean itself up when its finished.
+         * This case should never happen anyway.
+         *
+         * Since we're letting it live, we need to finish setting it up.
+         * We just have to let the caller know that the intended operation
+         * has failed.
+         *
+         * [ This seems strange -- stepping on the vmThread object that's
+         * already present seems like a bad idea.  TODO: figure this out. ]
+         */
+        ret = false;
+    } else {
+        ret = true;
+    }
+    dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+    /* we can now safely un-pin these */
+    dvmReleaseTrackedAlloc(threadObj, self);
+    dvmReleaseTrackedAlloc(vmThreadObj, self);
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+
+    LOG_THREAD("threadid=%d: attached from native, name=%s",
+        self->threadId, pArgs->name);
+
+    /* tell the debugger & DDM */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadStart(self);
+
+    return ret;
+
+fail_unlink:
+    dvmLockThreadList(self);
+    unlinkThread(self);
+    if (!isDaemon)
+        gDvm.nonDaemonThreadCount--;
+    dvmUnlockThreadList();
+    /* fall through to "fail" */
+fail:
+    dvmReleaseTrackedAlloc(threadObj, self);
+    dvmReleaseTrackedAlloc(vmThreadObj, self);
+    dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+    if (self != NULL) {
+        if (self->jniEnv != NULL) {
+            dvmDestroyJNIEnv(self->jniEnv);
+            self->jniEnv = NULL;
+        }
+        freeThread(self);
+    }
+    setThreadSelf(NULL);
+    return false;
+}
+
+/*
+ * Detach the thread from the various data structures, notify other threads
+ * that are waiting to "join" it, and free up all heap-allocated storage.
+ *
+ * Used for all threads.
+ *
+ * When we get here the interpreted stack should be empty.  The JNI 1.6 spec
+ * requires us to enforce this for the DetachCurrentThread call, probably
+ * because it also says that DetachCurrentThread causes all monitors
+ * associated with the thread to be released.  (Because the stack is empty,
+ * we only have to worry about explicit JNI calls to MonitorEnter.)
+ *
+ * THOUGHT:
+ * We might want to avoid freeing our internal Thread structure until the
+ * associated Thread/VMThread objects get GCed.  Our Thread is impossible to
+ * get to once the thread shuts down, but there is a small possibility of
+ * an operation starting in another thread before this thread halts, and
+ * finishing much later (perhaps the thread got stalled by a weird OS bug).
+ * We don't want something like Thread.isInterrupted() crawling through
+ * freed storage.  Can do with a Thread finalizer, or by creating a
+ * dedicated ThreadObject class for java/lang/Thread and moving all of our
+ * state into that.
+ */
+void dvmDetachCurrentThread()
+{
+    Thread* self = dvmThreadSelf();
+    Object* vmThread;
+    Object* group;
+
+    /*
+     * Make sure we're not detaching a thread that's still running.  (This
+     * could happen with an explicit JNI detach call.)
+     *
+     * A thread created by interpreted code will finish with a depth of
+     * zero, while a JNI-attached thread will have the synthetic "stack
+     * starter" native method at the top.
+     */
+    int curDepth = dvmComputeExactFrameDepth(self->interpSave.curFrame);
+    if (curDepth != 0) {
+        bool topIsNative = false;
+
+        if (curDepth == 1) {
+            /* not expecting a lingering break frame; just look at curFrame */
+            assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+            StackSaveArea* ssa = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+            if (dvmIsNativeMethod(ssa->method))
+                topIsNative = true;
+        }
+
+        if (!topIsNative) {
+            ALOGE("ERROR: detaching thread with interp frames (count=%d)",
+                curDepth);
+            dvmDumpThread(self, false);
+            dvmAbort();
+        }
+    }
+
+    group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
+    LOG_THREAD("threadid=%d: detach (group=%p)", self->threadId, group);
+
+    /*
+     * Release any held monitors.  Since there are no interpreted stack
+     * frames, the only thing left are the monitors held by JNI MonitorEnter
+     * calls.
+     */
+    dvmReleaseJniMonitors(self);
+
+    /*
+     * Do some thread-exit uncaught exception processing if necessary.
+     */
+    if (dvmCheckException(self))
+        threadExitUncaughtException(self, group);
+
+    /*
+     * Remove the thread from the thread group.
+     */
+    if (group != NULL) {
+        Method* removeThread =
+            group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread];
+        JValue unused;
+        dvmCallMethod(self, removeThread, group, &unused, self->threadObj);
+    }
+
+    /*
+     * Clear the vmThread reference in the Thread object.  Interpreted code
+     * will now see that this Thread is not running.  As this may be the
+     * only reference to the VMThread object that the VM knows about, we
+     * have to create an internal reference to it first.
+     */
+    vmThread = dvmGetFieldObject(self->threadObj,
+                    gDvm.offJavaLangThread_vmThread);
+    dvmAddTrackedAlloc(vmThread, self);
+    dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+    /* clear out our struct Thread pointer, since it's going away */
+    dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL);
+
+    /*
+     * Tell the debugger & DDM.  This may cause the current thread or all
+     * threads to suspend.
+     *
+     * The JDWP spec is somewhat vague about when this happens, other than
+     * that it's issued by the dying thread, which may still appear in
+     * an "all threads" listing.
+     */
+    if (gDvm.debuggerConnected)
+        dvmDbgPostThreadDeath(self);
+
+    /*
+     * Thread.join() is implemented as an Object.wait() on the VMThread
+     * object.  Signal anyone who is waiting.
+     */
+    dvmLockObject(self, vmThread);
+    dvmObjectNotifyAll(self, vmThread);
+    dvmUnlockObject(self, vmThread);
+
+    dvmReleaseTrackedAlloc(vmThread, self);
+    vmThread = NULL;
+
+    /*
+     * We're done manipulating objects, so it's okay if the GC runs in
+     * parallel with us from here out.  It's important to do this if
+     * profiling is enabled, since we can wait indefinitely.
+     */
+    volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+    volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+    android_atomic_release_store(THREAD_VMWAIT, addr);
+
+    /*
+     * If we're doing method trace profiling, we don't want threads to exit,
+     * because if they do we'll end up reusing thread IDs.  This complicates
+     * analysis and makes it impossible to have reasonable output in the
+     * "threads" section of the "key" file.
+     *
+     * We need to do this after Thread.join() completes, or other threads
+     * could get wedged.  Since self->threadObj is still valid, the Thread
+     * object will not get GCed even though we're no longer in the ThreadGroup
+     * list (which is important since the profiling thread needs to get
+     * the thread's name).
+     */
+    MethodTraceState* traceState = &gDvm.methodTrace;
+
+    dvmLockMutex(&traceState->startStopLock);
+    if (traceState->traceEnabled) {
+        ALOGI("threadid=%d: waiting for method trace to finish",
+            self->threadId);
+        while (traceState->traceEnabled) {
+            dvmWaitCond(&traceState->threadExitCond,
+                        &traceState->startStopLock);
+        }
+    }
+    dvmUnlockMutex(&traceState->startStopLock);
+
+    dvmLockThreadList(self);
+
+    /*
+     * Lose the JNI context.
+     */
+    dvmDestroyJNIEnv(self->jniEnv);
+    self->jniEnv = NULL;
+
+    self->status = THREAD_ZOMBIE;
+
+    /*
+     * Remove ourselves from the internal thread list.
+     */
+    unlinkThread(self);
+
+    /*
+     * If we're the last one standing, signal anybody waiting in
+     * DestroyJavaVM that it's okay to exit.
+     */
+    if (!dvmGetFieldBoolean(self->threadObj, gDvm.offJavaLangThread_daemon)) {
+        gDvm.nonDaemonThreadCount--;        // guarded by thread list lock
+
+        if (gDvm.nonDaemonThreadCount == 0) {
+            ALOGV("threadid=%d: last non-daemon thread", self->threadId);
+            //dvmDumpAllThreads(false);
+            // cond var guarded by threadListLock, which we already hold
+            int cc = pthread_cond_signal(&gDvm.vmExitCond);
+            if (cc != 0) {
+                ALOGE("pthread_cond_signal(&gDvm.vmExitCond) failed: %s", strerror(cc));
+                dvmAbort();
+            }
+        }
+    }
+
+    ALOGV("threadid=%d: bye!", self->threadId);
+    releaseThreadId(self);
+    dvmUnlockThreadList();
+
+    setThreadSelf(NULL);
+
+    freeThread(self);
+}
+
+
+/*
+ * Suspend a single thread.  Do not use to suspend yourself.
+ *
+ * This is used primarily for debugger/DDMS activity.  Does not return
+ * until the thread has suspended or is in a "safe" state (e.g. executing
+ * native code outside the VM).
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmSuspendThread(Thread* thread)
+{
+    assert(thread != NULL);
+    assert(thread != dvmThreadSelf());
+    //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(thread, 1, 1);
+
+    LOG_THREAD("threadid=%d: suspend++, now=%d",
+        thread->threadId, thread->suspendCount);
+    unlockThreadSuspendCount();
+
+    waitForThreadSuspend(dvmThreadSelf(), thread);
+}
+
+/*
+ * Reduce the suspend count of a thread.  If it hits zero, tell it to
+ * resume.
+ *
+ * Used primarily for debugger/DDMS activity.  The thread in question
+ * might have been suspended singly or as part of a suspend-all operation.
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmResumeThread(Thread* thread)
+{
+    assert(thread != NULL);
+    assert(thread != dvmThreadSelf());
+    //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+    lockThreadSuspendCount();
+    if (thread->suspendCount > 0) {
+        dvmAddToSuspendCounts(thread, -1, -1);
+    } else {
+        LOG_THREAD("threadid=%d:  suspendCount already zero",
+            thread->threadId);
+    }
+
+    LOG_THREAD("threadid=%d: suspend--, now=%d",
+        thread->threadId, thread->suspendCount);
+
+    if (thread->suspendCount == 0) {
+        dvmBroadcastCond(&gDvm.threadSuspendCountCond);
+    }
+
+    unlockThreadSuspendCount();
+}
+
+/*
+ * Suspend yourself, as a result of debugger activity.
+ */
+void dvmSuspendSelf(bool jdwpActivity)
+{
+    Thread* self = dvmThreadSelf();
+
+    /* debugger thread must not suspend itself due to debugger activity! */
+    assert(gDvm.jdwpState != NULL);
+    if (self->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+        assert(false);
+        return;
+    }
+
+    /*
+     * Collisions with other suspends aren't really interesting.  We want
+     * to ensure that we're the only one fiddling with the suspend count
+     * though.
+     */
+    lockThreadSuspendCount();
+    dvmAddToSuspendCounts(self, 1, 1);
+
+    /*
+     * Suspend ourselves.
+     */
+    assert(self->suspendCount > 0);
+    self->status = THREAD_SUSPENDED;
+    LOG_THREAD("threadid=%d: self-suspending (dbg)", self->threadId);
+
+    /*
+     * Tell JDWP that we've completed suspension.  The JDWP thread can't
+     * tell us to resume before we're fully asleep because we hold the
+     * suspend count lock.
+     *
+     * If we got here via waitForDebugger(), don't do this part.
+     */
+    if (jdwpActivity) {
+        //ALOGI("threadid=%d: clearing wait-for-event (my handle=%08x)",
+        //    self->threadId, (int) self->handle);
+        dvmJdwpClearWaitForEventThread(gDvm.jdwpState);
+    }
+
+    while (self->suspendCount != 0) {
+        dvmWaitCond(&gDvm.threadSuspendCountCond,
+                    &gDvm.threadSuspendCountLock);
+        if (self->suspendCount != 0) {
+            /*
+             * The condition was signaled but we're still suspended.  This
+             * can happen if the debugger lets go while a SIGQUIT thread
+             * dump event is pending (assuming SignalCatcher was resumed for
+             * just long enough to try to grab the thread-suspend lock).
+             */
+            ALOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)",
+                self->threadId, self->suspendCount, self->dbgSuspendCount);
+        }
+    }
+    assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+    self->status = THREAD_RUNNING;
+    LOG_THREAD("threadid=%d: self-reviving (dbg), status=%d",
+        self->threadId, self->status);
+
+    unlockThreadSuspendCount();
+}
+
+/*
+ * Dump the state of the current thread and that of another thread that
+ * we think is wedged.
+ */
+static void dumpWedgedThread(Thread* thread)
+{
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmPrintNativeBackTrace();
+
+    // dumping a running thread is risky, but could be useful
+    dvmDumpThread(thread, true);
+
+    // stop now and get a core dump
+    //abort();
+}
+
+/*
+ * If the thread is running at below-normal priority, temporarily elevate
+ * it to "normal".
+ *
+ * Returns zero if no changes were made.  Otherwise, returns bit flags
+ * indicating what was changed, storing the previous values in the
+ * provided locations.
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+    SchedPolicy* pSavedThreadPolicy)
+{
+    errno = 0;
+    *pSavedThreadPrio = getpriority(PRIO_PROCESS, thread->systemTid);
+    if (errno != 0) {
+        ALOGW("Unable to get priority for threadid=%d sysTid=%d",
+            thread->threadId, thread->systemTid);
+        return 0;
+    }
+    if (get_sched_policy(thread->systemTid, pSavedThreadPolicy) != 0) {
+        ALOGW("Unable to get policy for threadid=%d sysTid=%d",
+            thread->threadId, thread->systemTid);
+        return 0;
+    }
+
+    int changeFlags = 0;
+
+    /*
+     * Change the priority if we're in the background group.
+     */
+    if (*pSavedThreadPolicy == SP_BACKGROUND) {
+        if (set_sched_policy(thread->systemTid, SP_FOREGROUND) != 0) {
+            ALOGW("Couldn't set fg policy on tid %d", thread->systemTid);
+        } else {
+            changeFlags |= kChangedPolicy;
+            ALOGD("Temporarily moving tid %d to fg (was %d)",
+                thread->systemTid, *pSavedThreadPolicy);
+        }
+    }
+
+    /*
+     * getpriority() returns the "nice" value, so larger numbers indicate
+     * lower priority, with 0 being normal.
+     */
+    if (*pSavedThreadPrio > 0) {
+        const int kHigher = 0;
+        if (setpriority(PRIO_PROCESS, thread->systemTid, kHigher) != 0) {
+            ALOGW("Couldn't raise priority on tid %d to %d",
+                thread->systemTid, kHigher);
+        } else {
+            changeFlags |= kChangedPriority;
+            ALOGD("Temporarily raised priority on tid %d (%d -> %d)",
+                thread->systemTid, *pSavedThreadPrio, kHigher);
+        }
+    }
+
+    return changeFlags;
+}
+
+/*
+ * Reset the priority values for the thread in question.
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+    int savedThreadPrio, SchedPolicy savedThreadPolicy)
+{
+    if ((changeFlags & kChangedPolicy) != 0) {
+        if (set_sched_policy(thread->systemTid, savedThreadPolicy) != 0) {
+            ALOGW("NOTE: couldn't reset tid %d to (%d)",
+                thread->systemTid, savedThreadPolicy);
+        } else {
+            ALOGD("Restored policy of %d to %d",
+                thread->systemTid, savedThreadPolicy);
+        }
+    }
+
+    if ((changeFlags & kChangedPriority) != 0) {
+        if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) != 0)
+        {
+            ALOGW("NOTE: couldn't reset priority on thread %d to %d",
+                thread->systemTid, savedThreadPrio);
+        } else {
+            ALOGD("Restored priority on %d to %d",
+                thread->systemTid, savedThreadPrio);
+        }
+    }
+}
+
+/*
+ * Wait for another thread to see the pending suspension and stop running.
+ * It can either suspend itself or go into a non-running state such as
+ * VMWAIT or NATIVE in which it cannot interact with the GC.
+ *
+ * If we're running at a higher priority, sched_yield() may not do anything,
+ * so we need to sleep for "long enough" to guarantee that the other
+ * thread has a chance to finish what it's doing.  Sleeping for too short
+ * a period (e.g. less than the resolution of the sleep clock) might cause
+ * the scheduler to return immediately, so we want to start with a
+ * "reasonable" value and expand.
+ *
+ * This does not return until the other thread has stopped running.
+ * Eventually we time out and the VM aborts.
+ *
+ * This does not try to detect the situation where two threads are
+ * waiting for each other to suspend.  In normal use this is part of a
+ * suspend-all, which implies that the suspend-all lock is held, or as
+ * part of a debugger action in which the JDWP thread is always the one
+ * doing the suspending.  (We may need to re-evaluate this now that
+ * getThreadStackTrace is implemented as suspend-snapshot-resume.)
+ *
+ * TODO: track basic stats about time required to suspend VM.
+ */
+#define FIRST_SLEEP (250*1000)    /* 0.25s */
+#define MORE_SLEEP  (750*1000)    /* 0.75s */
+static void waitForThreadSuspend(Thread* self, Thread* thread)
+{
+    const int kMaxRetries = 10;
+    int spinSleepTime = FIRST_SLEEP;
+    bool complained = false;
+    int priChangeFlags = 0;
+    int savedThreadPrio = -500;
+    SchedPolicy savedThreadPolicy = SP_FOREGROUND;
+
+    int sleepIter = 0;
+    int retryCount = 0;
+    u8 startWhen = 0;       // init req'd to placate gcc
+    u8 firstStartWhen = 0;
+
+    while (thread->status == THREAD_RUNNING) {
+        if (sleepIter == 0) {           // get current time on first iteration
+            startWhen = dvmGetRelativeTimeUsec();
+            if (firstStartWhen == 0)    // first iteration of first attempt
+                firstStartWhen = startWhen;
+
+            /*
+             * After waiting for a bit, check to see if the target thread is
+             * running at a reduced priority.  If so, bump it up temporarily
+             * to give it more CPU time.
+             */
+            if (retryCount == 2) {
+                assert(thread->systemTid != 0);
+                priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
+                    &savedThreadPrio, &savedThreadPolicy);
+            }
+        }
+
+#if defined (WITH_JIT)
+        /*
+         * If we're still waiting after the first timeout, unchain all
+         * translations iff:
+         *   1) There are new chains formed since the last unchain
+         *   2) The top VM frame of the running thread is running JIT'ed code
+         */
+        if (gDvmJit.pJitEntryTable && retryCount > 0 &&
+            gDvmJit.hasNewChain && thread->inJitCodeCache) {
+            ALOGD("JIT unchain all for threadid=%d", thread->threadId);
+            dvmJitUnchainAll();
+        }
+#endif
+
+        /*
+         * Sleep briefly.  The iterative sleep call returns false if we've
+         * exceeded the total time limit for this round of sleeping.
+         */
+        if (!dvmIterativeSleep(sleepIter++, spinSleepTime, startWhen)) {
+            if (spinSleepTime != FIRST_SLEEP) {
+                ALOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)",
+                    self->threadId, retryCount,
+                    thread->threadId, priChangeFlags);
+                if (retryCount > 1) {
+                    /* stack trace logging is slow; skip on first iter */
+                    dumpWedgedThread(thread);
+                }
+                complained = true;
+            }
+
+            // keep going; could be slow due to valgrind
+            sleepIter = 0;
+            spinSleepTime = MORE_SLEEP;
+
+            if (retryCount++ == kMaxRetries) {
+                ALOGE("Fatal spin-on-suspend, dumping threads");
+                dvmDumpAllThreads(false);
+
+                /* log this after -- long traces will scroll off log */
+                ALOGE("threadid=%d: stuck on threadid=%d, giving up",
+                    self->threadId, thread->threadId);
+
+                /* try to get a debuggerd dump from the spinning thread */
+                dvmNukeThread(thread);
+                /* abort the VM */
+                dvmAbort();
+            }
+        }
+    }
+
+    if (complained) {
+        ALOGW("threadid=%d: spin on suspend resolved in %lld msec",
+            self->threadId,
+            (dvmGetRelativeTimeUsec() - firstStartWhen) / 1000);
+        //dvmDumpThread(thread, false);   /* suspended, so dump is safe */
+    }
+    if (priChangeFlags != 0) {
+        dvmResetThreadPriority(thread, priChangeFlags, savedThreadPrio,
+            savedThreadPolicy);
+    }
+}
+
+/*
+ * Suspend all threads except the current one.  This is used by the GC,
+ * the debugger, and by any thread that hits a "suspend all threads"
+ * debugger event (e.g. breakpoint or exception).
+ *
+ * If thread N hits a "suspend all threads" breakpoint, we don't want it
+ * to suspend the JDWP thread.  For the GC, we do, because the debugger can
+ * create objects and even execute arbitrary code.  The "why" argument
+ * allows the caller to say why the suspension is taking place.
+ *
+ * This can be called when a global suspend has already happened, due to
+ * various debugger gymnastics, so keeping an "everybody is suspended" flag
+ * doesn't work.
+ *
+ * DO NOT grab any locks before calling here.  We grab & release the thread
+ * lock and suspend lock here (and we're not using recursive threads), and
+ * we might have to self-suspend if somebody else beats us here.
+ *
+ * We know the current thread is in the thread list, because we attach the
+ * thread before doing anything that could cause VM suspension (like object
+ * allocation).
+ */
+void dvmSuspendAllThreads(SuspendCause why)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    assert(why != 0);
+
+    /*
+     * Start by grabbing the thread suspend lock.  If we can't get it, most
+     * likely somebody else is in the process of performing a suspend or
+     * resume, so lockThreadSuspend() will cause us to self-suspend.
+     *
+     * We keep the lock until all other threads are suspended.
+     */
+    lockThreadSuspend("susp-all", why);
+
+    LOG_THREAD("threadid=%d: SuspendAll starting", self->threadId);
+
+    /*
+     * This is possible if the current thread was in VMWAIT mode when a
+     * suspend-all happened, and then decided to do its own suspend-all.
+     * This can happen when a couple of threads have simultaneous events
+     * of interest to the debugger.
+     */
+    //assert(self->suspendCount == 0);
+
+    /*
+     * Increment everybody's suspend count (except our own).
+     */
+    dvmLockThreadList(self);
+
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        dvmAddToSuspendCounts(thread, 1,
+                              (why == SUSPEND_FOR_DEBUG ||
+                              why == SUSPEND_FOR_DEBUG_EVENT)
+                              ? 1 : 0);
+    }
+    unlockThreadSuspendCount();
+
+    /*
+     * Wait for everybody in THREAD_RUNNING state to stop.  Other states
+     * indicate the code is either running natively or sleeping quietly.
+     * Any attempt to transition back to THREAD_RUNNING will cause a check
+     * for suspension, so it should be impossible for anything to execute
+     * interpreted code or modify objects (assuming native code plays nicely).
+     *
+     * It's also okay if the thread transitions to a non-RUNNING state.
+     *
+     * Note we released the threadSuspendCountLock before getting here,
+     * so if another thread is fiddling with its suspend count (perhaps
+     * self-suspending for the debugger) it won't block while we're waiting
+     * in here.
+     */
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+            continue;
+
+        /* wait for the other thread to see the pending suspend */
+        waitForThreadSuspend(self, thread);
+
+        LOG_THREAD("threadid=%d:   threadid=%d status=%d sc=%d dc=%d",
+            self->threadId, thread->threadId, thread->status,
+            thread->suspendCount, thread->dbgSuspendCount);
+    }
+
+    dvmUnlockThreadList();
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: SuspendAll complete", self->threadId);
+}
+
+/*
+ * Resume all threads that are currently suspended.
+ *
+ * The "why" must match with the previous suspend.
+ */
+void dvmResumeAllThreads(SuspendCause why)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    lockThreadSuspend("res-all", why);  /* one suspend/resume at a time */
+    LOG_THREAD("threadid=%d: ResumeAll starting", self->threadId);
+
+    /*
+     * Decrement the suspend counts for all threads.  No need for atomic
+     * writes, since nobody should be moving until we decrement the count.
+     * We do need to hold the thread list because of JNI attaches.
+     */
+    dvmLockThreadList(self);
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+            thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+        {
+            continue;
+        }
+
+        if (thread->suspendCount > 0) {
+            dvmAddToSuspendCounts(thread, -1,
+                                  (why == SUSPEND_FOR_DEBUG ||
+                                  why == SUSPEND_FOR_DEBUG_EVENT)
+                                  ? -1 : 0);
+        } else {
+            LOG_THREAD("threadid=%d:  suspendCount already zero",
+                thread->threadId);
+        }
+    }
+    unlockThreadSuspendCount();
+    dvmUnlockThreadList();
+
+    /*
+     * In some ways it makes sense to continue to hold the thread-suspend
+     * lock while we issue the wakeup broadcast.  It allows us to complete
+     * one operation before moving on to the next, which simplifies the
+     * thread activity debug traces.
+     *
+     * This approach caused us some difficulty under Linux, because the
+     * condition variable broadcast not only made the threads runnable,
+     * but actually caused them to execute, and it was a while before
+     * the thread performing the wakeup had an opportunity to release the
+     * thread-suspend lock.
+     *
+     * This is a problem because, when a thread tries to acquire that
+     * lock, it times out after 3 seconds.  If at some point the thread
+     * is told to suspend, the clock resets; but since the VM is still
+     * theoretically mid-resume, there's no suspend pending.  If, for
+     * example, the GC was waking threads up while the SIGQUIT handler
+     * was trying to acquire the lock, we would occasionally time out on
+     * a busy system and SignalCatcher would abort.
+     *
+     * We now perform the unlock before the wakeup broadcast.  The next
+     * suspend can't actually start until the broadcast completes and
+     * returns, because we're holding the thread-suspend-count lock, but the
+     * suspending thread is now able to make progress and we avoid the abort.
+     *
+     * (Technically there is a narrow window between when we release
+     * the thread-suspend lock and grab the thread-suspend-count lock.
+     * This could cause us to send a broadcast to threads with nonzero
+     * suspend counts, but this is expected and they'll all just fall
+     * right back to sleep.  It's probably safe to grab the suspend-count
+     * lock before releasing thread-suspend, since we're still following
+     * the correct order of acquisition, but it feels weird.)
+     */
+
+    LOG_THREAD("threadid=%d: ResumeAll waking others", self->threadId);
+    unlockThreadSuspend();
+
+    /*
+     * Broadcast a notification to all suspended threads, some or all of
+     * which may choose to wake up.  No need to wait for them.
+     */
+    lockThreadSuspendCount();
+    int cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    if (cc != 0) {
+        ALOGE("pthread_cond_broadcast(&gDvm.threadSuspendCountCond) failed: %s", strerror(cc));
+        dvmAbort();
+    }
+    unlockThreadSuspendCount();
+
+    LOG_THREAD("threadid=%d: ResumeAll complete", self->threadId);
+}
+
+/*
+ * Undo any debugger suspensions.  This is called when the debugger
+ * disconnects.
+ */
+void dvmUndoDebuggerSuspensions()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    lockThreadSuspend("undo", SUSPEND_FOR_DEBUG);
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp starting", self->threadId);
+
+    /*
+     * Decrement the suspend counts for all threads.  No need for atomic
+     * writes, since nobody should be moving until we decrement the count.
+     * We do need to hold the thread list because of JNI attaches.
+     */
+    dvmLockThreadList(self);
+    lockThreadSuspendCount();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread == self)
+            continue;
+
+        /* debugger events don't suspend JDWP thread */
+        if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+            assert(thread->dbgSuspendCount == 0);
+            continue;
+        }
+
+        assert(thread->suspendCount >= thread->dbgSuspendCount);
+        dvmAddToSuspendCounts(thread, -thread->dbgSuspendCount,
+                              -thread->dbgSuspendCount);
+    }
+    unlockThreadSuspendCount();
+    dvmUnlockThreadList();
+
+    /*
+     * Broadcast a notification to all suspended threads, some or all of
+     * which may choose to wake up.  No need to wait for them.
+     */
+    lockThreadSuspendCount();
+    int cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    if (cc != 0) {
+        ALOGE("pthread_cond_broadcast(&gDvm.threadSuspendCountCond) failed: %s", strerror(cc));
+        dvmAbort();
+    }
+    unlockThreadSuspendCount();
+
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp complete", self->threadId);
+}
+
+/*
+ * Determine if a thread is suspended.
+ *
+ * As with all operations on foreign threads, the caller should hold
+ * the thread list lock before calling.
+ *
+ * If the thread is suspending or waking, these fields could be changing
+ * out from under us (or the thread could change state right after we
+ * examine it), making this generally unreliable.  This is chiefly
+ * intended for use by the debugger.
+ */
+bool dvmIsSuspended(const Thread* thread)
+{
+    /*
+     * The thread could be:
+     *  (1) Running happily.  status is RUNNING, suspendCount is zero.
+     *      Return "false".
+     *  (2) Pending suspend.  status is RUNNING, suspendCount is nonzero.
+     *      Return "false".
+     *  (3) Suspended.  suspendCount is nonzero, and status is !RUNNING.
+     *      Return "true".
+     *  (4) Waking up.  suspendCount is zero, status is SUSPENDED
+     *      Return "false" (since it could change out from under us, unless
+     *      we hold suspendCountLock).
+     */
+
+    return (thread->suspendCount != 0 &&
+            thread->status != THREAD_RUNNING);
+}
+
+/*
+ * Wait until another thread self-suspends.  This is specifically for
+ * synchronization between the JDWP thread and a thread that has decided
+ * to suspend itself after sending an event to the debugger.
+ *
+ * Threads that encounter "suspend all" events work as well -- the thread
+ * in question suspends everybody else and then itself.
+ *
+ * We can't hold a thread lock here or in the caller, because we could
+ * get here just before the to-be-waited-for-thread issues a "suspend all".
+ * There's an opportunity for badness if the thread we're waiting for exits
+ * and gets cleaned up, but since the thread in question is processing a
+ * debugger event, that's not really a possibility.  (To avoid deadlock,
+ * it's important that we not be in THREAD_RUNNING while we wait.)
+ */
+void dvmWaitForSuspend(Thread* thread)
+{
+    Thread* self = dvmThreadSelf();
+
+    LOG_THREAD("threadid=%d: waiting for threadid=%d to sleep",
+        self->threadId, thread->threadId);
+
+    assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+    assert(thread != self);
+    assert(self->status != THREAD_RUNNING);
+
+    waitForThreadSuspend(self, thread);
+
+    LOG_THREAD("threadid=%d: threadid=%d is now asleep",
+        self->threadId, thread->threadId);
+}
+
+/*
+ * Check to see if we need to suspend ourselves.  If so, go to sleep on
+ * a condition variable.
+ *
+ * Returns "true" if we suspended ourselves.
+ */
+static bool fullSuspendCheck(Thread* self)
+{
+    assert(self != NULL);
+    assert(self->suspendCount >= 0);
+
+    /*
+     * Grab gDvm.threadSuspendCountLock.  This gives us exclusive write
+     * access to self->suspendCount.
+     */
+    lockThreadSuspendCount();   /* grab gDvm.threadSuspendCountLock */
+
+    bool needSuspend = (self->suspendCount != 0);
+    if (needSuspend) {
+        LOG_THREAD("threadid=%d: self-suspending", self->threadId);
+        ThreadStatus oldStatus = self->status;      /* should be RUNNING */
+        self->status = THREAD_SUSPENDED;
+
+        ATRACE_BEGIN("DVM Suspend");
+        while (self->suspendCount != 0) {
+            /*
+             * Wait for wakeup signal, releasing lock.  The act of releasing
+             * and re-acquiring the lock provides the memory barriers we
+             * need for correct behavior on SMP.
+             */
+            dvmWaitCond(&gDvm.threadSuspendCountCond,
+                    &gDvm.threadSuspendCountLock);
+        }
+        ATRACE_END();
+        assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+        self->status = oldStatus;
+        LOG_THREAD("threadid=%d: self-reviving, status=%d",
+            self->threadId, self->status);
+    }
+
+    unlockThreadSuspendCount();
+
+    return needSuspend;
+}
+
+/*
+ * Check to see if a suspend is pending.  If so, suspend the current
+ * thread, and return "true" after we have been resumed.
+ */
+bool dvmCheckSuspendPending(Thread* self)
+{
+    assert(self != NULL);
+    if (self->suspendCount == 0) {
+        return false;
+    } else {
+        return fullSuspendCheck(self);
+    }
+}
+
+/*
+ * Update our status.
+ *
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus)
+{
+    ThreadStatus oldStatus;
+
+    if (self == NULL)
+        self = dvmThreadSelf();
+
+    LOGVV("threadid=%d: (status %d -> %d)",
+        self->threadId, self->status, newStatus);
+
+    oldStatus = self->status;
+    if (oldStatus == newStatus)
+        return oldStatus;
+
+    if (newStatus == THREAD_RUNNING) {
+        /*
+         * Change our status to THREAD_RUNNING.  The transition requires
+         * that we check for pending suspension, because the VM considers
+         * us to be "asleep" in all other states, and another thread could
+         * be performing a GC now.
+         *
+         * The order of operations is very significant here.  One way to
+         * do this wrong is:
+         *
+         *   GCing thread                   Our thread (in NATIVE)
+         *   ------------                   ----------------------
+         *                                  check suspend count (== 0)
+         *   dvmSuspendAllThreads()
+         *   grab suspend-count lock
+         *   increment all suspend counts
+         *   release suspend-count lock
+         *   check thread state (== NATIVE)
+         *   all are suspended, begin GC
+         *                                  set state to RUNNING
+         *                                  (continue executing)
+         *
+         * We can correct this by grabbing the suspend-count lock and
+         * performing both of our operations (check suspend count, set
+         * state) while holding it, now we need to grab a mutex on every
+         * transition to RUNNING.
+         *
+         * What we do instead is change the order of operations so that
+         * the transition to RUNNING happens first.  If we then detect
+         * that the suspend count is nonzero, we switch to SUSPENDED.
+         *
+         * Appropriate compiler and memory barriers are required to ensure
+         * that the operations are observed in the expected order.
+         *
+         * This does create a small window of opportunity where a GC in
+         * progress could observe what appears to be a running thread (if
+         * it happens to look between when we set to RUNNING and when we
+         * switch to SUSPENDED).  At worst this only affects assertions
+         * and thread logging.  (We could work around it with some sort
+         * of intermediate "pre-running" state that is generally treated
+         * as equivalent to running, but that doesn't seem worthwhile.)
+         *
+         * We can also solve this by combining the "status" and "suspend
+         * count" fields into a single 32-bit value.  This trades the
+         * store/load barrier on transition to RUNNING for an atomic RMW
+         * op on all transitions and all suspend count updates (also, all
+         * accesses to status or the thread count require bit-fiddling).
+         * It also eliminates the brief transition through RUNNING when
+         * the thread is supposed to be suspended.  This is possibly faster
+         * on SMP and slightly more correct, but less convenient.
+         */
+        volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+        volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+        android_atomic_acquire_store(newStatus, addr);
+        if (self->suspendCount != 0) {
+            fullSuspendCheck(self);
+        }
+    } else {
+        /*
+         * Not changing to THREAD_RUNNING.  No additional work required.
+         *
+         * We use a releasing store to ensure that, if we were RUNNING,
+         * any updates we previously made to objects on the managed heap
+         * will be observed before the state change.
+         */
+        assert(newStatus != THREAD_SUSPENDED);
+        volatile void* raw = reinterpret_cast<volatile void*>(&self->status);
+        volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
+        android_atomic_release_store(newStatus, addr);
+    }
+
+    return oldStatus;
+}
+
+/*
+ * Get a statically defined thread group from a field in the ThreadGroup
+ * Class object.  Expected arguments are "mMain" and "mSystem".
+ */
+static Object* getStaticThreadGroup(const char* fieldName)
+{
+    StaticField* groupField;
+    Object* groupObj;
+
+    groupField = dvmFindStaticField(gDvm.classJavaLangThreadGroup,
+        fieldName, "Ljava/lang/ThreadGroup;");
+    if (groupField == NULL) {
+        ALOGE("java.lang.ThreadGroup does not have an '%s' field", fieldName);
+        dvmThrowInternalError("bad definition for ThreadGroup");
+        return NULL;
+    }
+    groupObj = dvmGetStaticFieldObject(groupField);
+    if (groupObj == NULL) {
+        ALOGE("java.lang.ThreadGroup.%s not initialized", fieldName);
+        dvmThrowInternalError(NULL);
+        return NULL;
+    }
+
+    return groupObj;
+}
+Object* dvmGetSystemThreadGroup()
+{
+    return getStaticThreadGroup("mSystem");
+}
+Object* dvmGetMainThreadGroup()
+{
+    return getStaticThreadGroup("mMain");
+}
+
+/*
+ * Given a VMThread object, return the associated Thread*.
+ *
+ * NOTE: if the thread detaches, the struct Thread will disappear, and
+ * we will be touching invalid data.  For safety, lock the thread list
+ * before calling this.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj)
+{
+    int vmData;
+
+    vmData = dvmGetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData);
+
+    if (false) {
+        Thread* thread = gDvm.threadList;
+        while (thread != NULL) {
+            if ((Thread*)vmData == thread)
+                break;
+
+            thread = thread->next;
+        }
+
+        if (thread == NULL) {
+            ALOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list",
+                vmThreadObj, (Thread*)vmData);
+            vmData = 0;
+        }
+    }
+
+    return (Thread*) vmData;
+}
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle)
+{
+    Thread* thread;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->handle == handle)
+            break;
+    }
+    return thread;
+}
+
+/*
+ * Given a threadId, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId)
+{
+    Thread* thread;
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadId == threadId)
+            break;
+    }
+    return thread;
+}
+
+void dvmChangeThreadPriority(Thread* thread, int newPriority)
+{
+    os_changeThreadPriority(thread, newPriority);
+}
+
+/*
+ * Return true if the thread is on gDvm.threadList.
+ * Caller should not hold gDvm.threadListLock.
+ */
+bool dvmIsOnThreadList(const Thread* thread)
+{
+    bool ret = false;
+
+    dvmLockThreadList(NULL);
+    if (thread == gDvm.threadList) {
+        ret = true;
+    } else {
+        ret = thread->prev != NULL || thread->next != NULL;
+    }
+    dvmUnlockThreadList();
+
+    return ret;
+}
+
+/*
+ * Dump a thread to the log file -- just calls dvmDumpThreadEx() with an
+ * output target.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning)
+{
+    DebugOutputTarget target;
+
+    dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+    dvmDumpThreadEx(&target, thread, isRunning);
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks (something) like:
+ *  2:cpu:/bg_non_interactive
+ *  1:cpuacct:/
+ *
+ * We return the part on the "cpu" line after the '/', which will be an
+ * empty string for the default cgroup.  If the string is longer than
+ * "bufLen", the string will be truncated.
+ *
+ * On error, -1 is returned, and an error description will be stored in
+ * the buffer.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+    char pathBuf[32];
+    char lineBuf[256];
+    FILE *fp;
+
+    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+    if ((fp = fopen(pathBuf, "r")) == NULL) {
+        snprintf(buf, bufLen, "[fopen-error:%d]", errno);
+        return -1;
+    }
+
+    while (fgets(lineBuf, sizeof(lineBuf) -1, fp) != NULL) {
+        char* subsys;
+        char* grp;
+        size_t len;
+
+        /* Junk the first field */
+        subsys = strchr(lineBuf, ':');
+        if (subsys == NULL) {
+            goto out_bad_data;
+        }
+
+        if (strncmp(subsys, ":cpu:", 5) != 0) {
+            /* Not the subsys we're looking for */
+            continue;
+        }
+
+        grp = strchr(subsys, '/');
+        if (grp == NULL) {
+            goto out_bad_data;
+        }
+        grp++; /* Drop the leading '/' */
+
+        len = strlen(grp);
+        grp[len-1] = '\0'; /* Drop the trailing '\n' */
+
+        if (bufLen <= len) {
+            len = bufLen - 1;
+        }
+        strncpy(buf, grp, len);
+        buf[len] = '\0';
+        fclose(fp);
+        return 0;
+    }
+
+    snprintf(buf, bufLen, "[no-cpu-subsys]");
+    fclose(fp);
+    return -1;
+
+out_bad_data:
+    ALOGE("Bad cgroup data {%s}", lineBuf);
+    snprintf(buf, bufLen, "[data-parse-failed]");
+    fclose(fp);
+    return -1;
+
+#else
+    snprintf(buf, bufLen, "[n/a]");
+    return -1;
+#endif
+}
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status)
+{
+    switch (status) {
+    case THREAD_ZOMBIE:         return "ZOMBIE";
+    case THREAD_RUNNING:        return "RUNNABLE";
+    case THREAD_TIMED_WAIT:     return "TIMED_WAIT";
+    case THREAD_MONITOR:        return "MONITOR";
+    case THREAD_WAIT:           return "WAIT";
+    case THREAD_INITIALIZING:   return "INITIALIZING";
+    case THREAD_STARTING:       return "STARTING";
+    case THREAD_NATIVE:         return "NATIVE";
+    case THREAD_VMWAIT:         return "VMWAIT";
+    case THREAD_SUSPENDED:      return "SUSPENDED";
+    default:                    return "UNKNOWN";
+    }
+}
+
+static void dumpSchedStat(const DebugOutputTarget* target, pid_t tid) {
+#ifdef HAVE_ANDROID_OS
+    /* get some bits from /proc/self/stat */
+    ProcStatData procStatData;
+    if (!dvmGetThreadStats(&procStatData, tid)) {
+        /* failed, use zeroed values */
+        memset(&procStatData, 0, sizeof(procStatData));
+    }
+
+    /* grab the scheduler stats for this thread */
+    char schedstatBuf[64];
+    snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/self/task/%d/schedstat", tid);
+    int schedstatFd = open(schedstatBuf, O_RDONLY);
+    strcpy(schedstatBuf, "0 0 0");          /* show this if open/read fails */
+    if (schedstatFd >= 0) {
+        ssize_t bytes;
+        bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1);
+        close(schedstatFd);
+        if (bytes >= 1) {
+            schedstatBuf[bytes - 1] = '\0';   /* remove trailing newline */
+        }
+    }
+
+    /* show what we got */
+    dvmPrintDebugMessage(target,
+        "  | state=%c schedstat=( %s ) utm=%lu stm=%lu core=%d\n",
+        procStatData.state, schedstatBuf, procStatData.utime,
+        procStatData.stime, procStatData.processor);
+#endif
+}
+
+struct SchedulerStats {
+    int policy;
+    int priority;
+    char group[32];
+};
+
+/*
+ * Get scheduler statistics.
+ */
+static void getSchedulerStats(SchedulerStats* stats, pid_t tid) {
+    struct sched_param sp;
+    if (pthread_getschedparam(pthread_self(), &stats->policy, &sp) != 0) {
+        ALOGW("Warning: pthread_getschedparam failed");
+        stats->policy = -1;
+        stats->priority = -1;
+    } else {
+        stats->priority = sp.sched_priority;
+    }
+    if (getSchedulerGroup(tid, stats->group, sizeof(stats->group)) == 0 &&
+            stats->group[0] == '\0') {
+        strcpy(stats->group, "default");
+    }
+}
+
+static bool shouldShowNativeStack(Thread* thread) {
+    // In native code somewhere in the VM? That's interesting.
+    if (thread->status == THREAD_VMWAIT) {
+        return true;
+    }
+
+    // In an Object.wait variant? That's not interesting.
+    if (thread->status == THREAD_TIMED_WAIT || thread->status == THREAD_WAIT) {
+        return false;
+    }
+
+    // The Signal Catcher thread? That's not interesting.
+    if (thread->status == THREAD_RUNNING) {
+        return false;
+    }
+
+    // In some other native method? That's interesting.
+    // We don't just check THREAD_NATIVE because native methods will be in
+    // state THREAD_SUSPENDED if they're calling back into the VM, or THREAD_MONITOR
+    // if they're blocked on a monitor, or one of the thread-startup states if
+    // it's early enough in their life cycle (http://b/7432159).
+    u4* fp = thread->interpSave.curFrame;
+    if (fp == NULL) {
+        // The thread has no managed frames, so native frames are all there is.
+        return true;
+    }
+    const Method* currentMethod = SAVEAREA_FROM_FP(fp)->method;
+    return currentMethod != NULL && dvmIsNativeMethod(currentMethod);
+}
+
+/*
+ * Print information about the specified thread.
+ *
+ * Works best when the thread in question is "self" or has been suspended.
+ * When dumping a separate thread that's still running, set "isRunning" to
+ * use a more cautious thread dump function.
+ */
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+    bool isRunning)
+{
+    Object* threadObj;
+    Object* groupObj;
+    StringObject* nameStr;
+    char* threadName = NULL;
+    char* groupName = NULL;
+    bool isDaemon;
+    int priority;               // java.lang.Thread priority
+
+    /*
+     * Get the java.lang.Thread object.  This function gets called from
+     * some weird debug contexts, so it's possible that there's a GC in
+     * progress on some other thread.  To decrease the chances of the
+     * thread object being moved out from under us, we add the reference
+     * to the tracked allocation list, which pins it in place.
+     *
+     * If threadObj is NULL, the thread is still in the process of being
+     * attached to the VM, and there's really nothing interesting to
+     * say about it yet.
+     */
+    threadObj = thread->threadObj;
+    if (threadObj == NULL) {
+        ALOGI("Can't dump thread %d: threadObj not set", thread->threadId);
+        return;
+    }
+    dvmAddTrackedAlloc(threadObj, NULL);
+
+    nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                gDvm.offJavaLangThread_name);
+    threadName = dvmCreateCstrFromString(nameStr);
+
+    priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+    isDaemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+
+    /* a null value for group is not expected, but deal with it anyway */
+    groupObj = (Object*) dvmGetFieldObject(threadObj,
+                gDvm.offJavaLangThread_group);
+    if (groupObj != NULL) {
+        nameStr = (StringObject*)
+            dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
+        groupName = dvmCreateCstrFromString(nameStr);
+    }
+    if (groupName == NULL)
+        groupName = strdup("(null; initializing?)");
+
+    SchedulerStats schedStats;
+    getSchedulerStats(&schedStats, thread->systemTid);
+
+    dvmPrintDebugMessage(target,
+        "\"%s\"%s prio=%d tid=%d %s%s\n",
+        threadName, isDaemon ? " daemon" : "",
+        priority, thread->threadId, dvmGetThreadStatusStr(thread->status),
+#if defined(WITH_JIT)
+        thread->inJitCodeCache ? " JIT" : ""
+#else
+        ""
+#endif
+        );
+    dvmPrintDebugMessage(target,
+        "  | group=\"%s\" sCount=%d dsCount=%d obj=%p self=%p\n",
+        groupName, thread->suspendCount, thread->dbgSuspendCount,
+        thread->threadObj, thread);
+    dvmPrintDebugMessage(target,
+        "  | sysTid=%d nice=%d sched=%d/%d cgrp=%s handle=%d\n",
+        thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid),
+        schedStats.policy, schedStats.priority, schedStats.group, (int)thread->handle);
+
+    dumpSchedStat(target, thread->systemTid);
+
+    if (shouldShowNativeStack(thread)) {
+        dvmDumpNativeStack(target, thread->systemTid);
+    }
+
+    if (isRunning)
+        dvmDumpRunningThreadStack(target, thread);
+    else
+        dvmDumpThreadStack(target, thread);
+
+    dvmPrintDebugMessage(target, "\n");
+
+    dvmReleaseTrackedAlloc(threadObj, NULL);
+    free(threadName);
+    free(groupName);
+}
+
+std::string dvmGetThreadName(Thread* thread) {
+    if (thread->threadObj == NULL) {
+        ALOGW("threadObj is NULL, name not available");
+        return "-unknown-";
+    }
+
+    StringObject* nameObj = (StringObject*)
+        dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name);
+    char* name = dvmCreateCstrFromString(nameObj);
+    std::string result(name);
+    free(name);
+    return result;
+}
+
+#ifdef HAVE_ANDROID_OS
+/*
+ * Dumps information about a non-Dalvik thread.
+ */
+static void dumpNativeThread(const DebugOutputTarget* target, pid_t tid) {
+    char path[64];
+    snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+
+    int fd = open(path, O_RDONLY);
+    char name[64];
+    ssize_t n = 0;
+    if (fd >= 0) {
+        n = read(fd, name, sizeof(name) - 1);
+        close(fd);
+    }
+    if (n > 0 && name[n - 1] == '\n') {
+        n -= 1;
+    }
+    if (n <= 0) {
+        strcpy(name, "<no name>");
+    } else {
+        name[n] = '\0';
+    }
+
+    SchedulerStats schedStats;
+    getSchedulerStats(&schedStats, tid);
+
+    dvmPrintDebugMessage(target,
+        "\"%s\" sysTid=%d nice=%d sched=%d/%d cgrp=%s\n",
+        name, tid, getpriority(PRIO_PROCESS, tid),
+        schedStats.policy, schedStats.priority, schedStats.group);
+    dumpSchedStat(target, tid);
+    // Temporarily disabled collecting native stacks from non-Dalvik
+    // threads because sometimes they misbehave.
+    //dvmDumpNativeStack(target, tid);
+
+    dvmPrintDebugMessage(target, "\n");
+}
+
+/*
+ * Returns true if the specified tid is a Dalvik thread.
+ * Assumes the thread list lock is held.
+ */
+static bool isDalvikThread(pid_t tid) {
+    for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->systemTid == tid) {
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
+/*
+ * Dump all threads to the log file -- just calls dvmDumpAllThreadsEx() with
+ * an output target.
+ */
+void dvmDumpAllThreads(bool grabLock)
+{
+    DebugOutputTarget target;
+
+    dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+    dvmDumpAllThreadsEx(&target, grabLock);
+}
+
+/*
+ * Print information about all known threads.  Assumes they have been
+ * suspended (or are in a non-interpreting state, e.g. WAIT or NATIVE).
+ *
+ * If "grabLock" is true, we grab the thread lock list.  This is important
+ * to do unless the caller already holds the lock.
+ */
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock)
+{
+    Thread* thread;
+
+    dvmPrintDebugMessage(target, "DALVIK THREADS:\n");
+
+#ifdef HAVE_ANDROID_OS
+    dvmPrintDebugMessage(target,
+        "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n\n",
+        gDvm.threadListLock.value,
+        gDvm._threadSuspendLock.value,
+        gDvm.threadSuspendCountLock.value,
+        gDvm.gcHeapLock.value);
+#endif
+
+    if (grabLock)
+        dvmLockThreadList(dvmThreadSelf());
+
+    thread = gDvm.threadList;
+    while (thread != NULL) {
+        dvmDumpThreadEx(target, thread, false);
+
+        /* verify link */
+        assert(thread->next == NULL || thread->next->prev == thread);
+
+        thread = thread->next;
+    }
+
+#ifdef HAVE_ANDROID_OS
+    DIR* d = opendir("/proc/self/task");
+    if (d != NULL) {
+        dirent* entry = NULL;
+        bool first = true;
+        while ((entry = readdir(d)) != NULL) {
+            char* end;
+            pid_t tid = strtol(entry->d_name, &end, 10);
+            if (!*end && !isDalvikThread(tid)) {
+                if (first) {
+                    dvmPrintDebugMessage(target, "NATIVE THREADS:\n");
+                    first = false;
+                }
+                dumpNativeThread(target, tid);
+            }
+        }
+        closedir(d);
+    }
+#endif
+
+    if (grabLock)
+        dvmUnlockThreadList();
+}
+
+/*
+ * Nuke the target thread from orbit.
+ *
+ * The idea is to send a "crash" signal to the target thread so that
+ * debuggerd will take notice and dump an appropriate stack trace.
+ * Because of the way debuggerd works, we have to throw the same signal
+ * at it twice.
+ *
+ * This does not necessarily cause the entire process to stop, but once a
+ * thread has been nuked the rest of the system is likely to be unstable.
+ * This returns so that some limited set of additional operations may be
+ * performed, but it's advisable (and expected) to call dvmAbort soon.
+ * (This is NOT a way to simply cancel a thread.)
+ */
+void dvmNukeThread(Thread* thread)
+{
+    int killResult;
+
+    /* suppress the heapworker watchdog to assist anyone using a debugger */
+    gDvm.nativeDebuggerActive = true;
+
+    /*
+     * Send the signals, separated by a brief interval to allow debuggerd
+     * to work its magic.  An uncommon signal like SIGFPE or SIGSTKFLT
+     * can be used instead of SIGSEGV to avoid making it look like the
+     * code actually crashed at the current point of execution.
+     *
+     * (Observed behavior: with SIGFPE, debuggerd will dump the target
+     * thread and then the thread that calls dvmAbort.  With SIGSEGV,
+     * you don't get the second stack trace; possibly something in the
+     * kernel decides that a signal has already been sent and it's time
+     * to just kill the process.  The position in the current thread is
+     * generally known, so the second dump is not useful.)
+     *
+     * The target thread can continue to execute between the two signals.
+     * (The first just causes debuggerd to attach to it.)
+     */
+#ifdef SIGSTKFLT
+#define SIG SIGSTKFLT
+#define SIGNAME "SIGSTKFLT"
+#elif defined(SIGEMT)
+#define SIG SIGEMT
+#define SIGNAME "SIGEMT"
+#else
+#error No signal available for dvmNukeThread
+#endif
+
+    ALOGD("threadid=%d: sending two " SIGNAME "s to threadid=%d (tid=%d) to"
+          " cause debuggerd dump",
+          dvmThreadSelf()->threadId, thread->threadId, thread->systemTid);
+    killResult = pthread_kill(thread->handle, SIG);
+    if (killResult != 0) {
+        ALOGD("NOTE: pthread_kill #1 failed: %s", strerror(killResult));
+    }
+    usleep(2 * 1000 * 1000);    // TODO: timed-wait until debuggerd attaches
+    killResult = pthread_kill(thread->handle, SIG);
+    if (killResult != 0) {
+        ALOGD("NOTE: pthread_kill #2 failed: %s", strerror(killResult));
+    }
+    ALOGD("Sent, pausing to let debuggerd run");
+    usleep(8 * 1000 * 1000);    // TODO: timed-wait until debuggerd finishes
+
+    /* ignore SIGSEGV so the eventual dvmAbort() doesn't notify debuggerd */
+    signal(SIGSEGV, SIG_IGN);
+    ALOGD("Continuing");
+}
diff --git a/vm/Thread.h b/vm/Thread.h
new file mode 100644
index 0000000..0bf15e6
--- /dev/null
+++ b/vm/Thread.h
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * VM thread support.
+ */
+#ifndef DALVIK_THREAD_H_
+#define DALVIK_THREAD_H_
+
+#include "jni.h"
+#include "interp/InterpState.h"
+
+#include <errno.h>
+#include <cutils/sched_policy.h>
+
+#if defined(CHECK_MUTEX) && !defined(__USE_UNIX98)
+/* glibc lacks this unless you #define __USE_UNIX98 */
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
+enum { PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP };
+#endif
+
+/*
+ * Current status; these map to JDWP constants, so don't rearrange them.
+ * (If you do alter this, update the strings in dvmDumpThread and the
+ * conversion table in VMThread.java.)
+ *
+ * Note that "suspended" is orthogonal to these values (so says JDWP).
+ */
+enum ThreadStatus {
+    THREAD_UNDEFINED    = -1,       /* makes enum compatible with int32_t */
+
+    /* these match up with JDWP values */
+    THREAD_ZOMBIE       = 0,        /* TERMINATED */
+    THREAD_RUNNING      = 1,        /* RUNNABLE or running now */
+    THREAD_TIMED_WAIT   = 2,        /* TIMED_WAITING in Object.wait() */
+    THREAD_MONITOR      = 3,        /* BLOCKED on a monitor */
+    THREAD_WAIT         = 4,        /* WAITING in Object.wait() */
+    /* non-JDWP states */
+    THREAD_INITIALIZING = 5,        /* allocated, not yet running */
+    THREAD_STARTING     = 6,        /* started, not yet on thread list */
+    THREAD_NATIVE       = 7,        /* off in a JNI native method */
+    THREAD_VMWAIT       = 8,        /* waiting on a VM resource */
+    THREAD_SUSPENDED    = 9,        /* suspended, usually by GC or debugger */
+};
+
+/* thread priorities, from java.lang.Thread */
+enum {
+    THREAD_MIN_PRIORITY     = 1,
+    THREAD_NORM_PRIORITY    = 5,
+    THREAD_MAX_PRIORITY     = 10,
+};
+
+
+/* initialization */
+bool dvmThreadStartup(void);
+void dvmThreadShutdown(void);
+void dvmSlayDaemons(void);
+
+
+#define kJniLocalRefMin         64
+#define kJniLocalRefMax         512     /* arbitrary; should be plenty */
+#define kInternalRefDefault     32      /* equally arbitrary */
+#define kInternalRefMax         4096    /* mainly a sanity check */
+
+#define kMinStackSize       (512 + STACK_OVERFLOW_RESERVE)
+#define kDefaultStackSize   (16*1024)   /* four 4K pages */
+#define kMaxStackSize       (256*1024 + STACK_OVERFLOW_RESERVE)
+
+/*
+ * Interpreter control struction.  Packed into a long long to enable
+ * atomic updates.
+ */
+union InterpBreak {
+    volatile int64_t   all;
+    struct {
+        uint16_t   subMode;
+        uint8_t    breakFlags;
+        int8_t     unused;   /* for future expansion */
+#ifndef DVM_NO_ASM_INTERP
+        void* curHandlerTable;
+#else
+        int32_t    unused1;
+#endif
+    } ctl;
+};
+
+/*
+ * Our per-thread data.
+ *
+ * These are allocated on the system heap.
+ */
+struct Thread {
+    /*
+     * Interpreter state which must be preserved across nested
+     * interpreter invocations (via JNI callbacks).  Must be the first
+     * element in Thread.
+     */
+    InterpSaveState interpSave;
+
+    /* small unique integer; useful for "thin" locks and debug messages */
+    u4          threadId;
+
+    /*
+     * Begin interpreter state which does not need to be preserved, but should
+     * be located towards the beginning of the Thread structure for
+     * efficiency.
+     */
+
+    /*
+     * interpBreak contains info about the interpreter mode, as well as
+     * a count of the number of times the thread has been suspended.  When
+     * the count drops to zero, the thread resumes.
+     */
+    InterpBreak interpBreak;
+
+    /*
+     * "dbgSuspendCount" is the portion of the suspend count that the
+     * debugger is responsible for.  This has to be tracked separately so
+     * that we can recover correctly if the debugger abruptly disconnects
+     * (suspendCount -= dbgSuspendCount).  The debugger should not be able
+     * to resume GC-suspended threads, because we ignore the debugger while
+     * a GC is in progress.
+     *
+     * Both of these are guarded by gDvm.threadSuspendCountLock.
+     *
+     * Note the non-debug component will rarely be other than 1 or 0 -- (not
+     * sure it's even possible with the way mutexes are currently used.)
+     */
+
+    int suspendCount;
+    int dbgSuspendCount;
+
+    u1*         cardTable;
+
+    /* current limit of stack; flexes for StackOverflowError */
+    const u1*   interpStackEnd;
+
+    /* FP of bottom-most (currently executing) stack frame on interp stack */
+    void*       XcurFrame;
+    /* current exception, or NULL if nothing pending */
+    Object*     exception;
+
+    bool        debugIsMethodEntry;
+    /* interpreter stack size; our stacks are fixed-length */
+    int         interpStackSize;
+    bool        stackOverflowed;
+
+    /* thread handle, as reported by pthread_self() */
+    pthread_t   handle;
+
+    /* Assembly interpreter handler tables */
+#ifndef DVM_NO_ASM_INTERP
+    void*       mainHandlerTable;   // Table of actual instruction handler
+    void*       altHandlerTable;    // Table of breakout handlers
+#else
+    void*       unused0;            // Consume space to keep offsets
+    void*       unused1;            //   the same between builds with
+#endif
+
+    /*
+     * singleStepCount is a countdown timer used with the breakFlag
+     * kInterpSingleStep.  If kInterpSingleStep is set in breakFlags,
+     * singleStepCount will decremented each instruction execution.
+     * Once it reaches zero, the kInterpSingleStep flag in breakFlags
+     * will be cleared.  This can be used to temporarily prevent
+     * execution from re-entering JIT'd code or force inter-instruction
+     * checks by delaying the reset of curHandlerTable to mainHandlerTable.
+     */
+    int         singleStepCount;
+
+#ifdef WITH_JIT
+    struct JitToInterpEntries jitToInterpEntries;
+    /*
+     * Whether the current top VM frame is in the interpreter or JIT cache:
+     *   NULL    : in the interpreter
+     *   non-NULL: entry address of the JIT'ed code (the actual value doesn't
+     *             matter)
+     */
+    void*             inJitCodeCache;
+    unsigned char*    pJitProfTable;
+    int               jitThreshold;
+    const void*       jitResumeNPC;     // Translation return point
+    const u4*         jitResumeNSP;     // Native SP at return point
+    const u2*         jitResumeDPC;     // Dalvik inst following single-step
+    JitState    jitState;
+    int         icRechainCount;
+    const void* pProfileCountdown;
+    const ClassObject* callsiteClass;
+    const Method*     methodToCall;
+#endif
+
+    /* JNI local reference tracking */
+    IndirectRefTable jniLocalRefTable;
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    /* Buffer for register state during self verification */
+    struct ShadowSpace* shadowSpace;
+#endif
+    int         currTraceRun;
+    int         totalTraceLen;  // Number of Dalvik insts in trace
+    const u2*   currTraceHead;  // Start of the trace we're building
+    const u2*   currRunHead;    // Start of run we're building
+    int         currRunLen;     // Length of run in 16-bit words
+    const u2*   lastPC;         // Stage the PC for the threaded interpreter
+    const Method*  traceMethod; // Starting method of current trace
+    intptr_t    threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
+    JitTraceRun trace[MAX_JIT_RUN_LEN];
+#endif
+
+    /*
+     * Thread's current status.  Can only be changed by the thread itself
+     * (i.e. don't mess with this from other threads).
+     */
+    volatile ThreadStatus status;
+
+    /* thread ID, only useful under Linux */
+    pid_t       systemTid;
+
+    /* start (high addr) of interp stack (subtract size to get malloc addr) */
+    u1*         interpStackStart;
+
+    /* the java/lang/Thread that we are associated with */
+    Object*     threadObj;
+
+    /* the JNIEnv pointer associated with this thread */
+    JNIEnv*     jniEnv;
+
+    /* internal reference tracking */
+    ReferenceTable  internalLocalRefTable;
+
+
+    /* JNI native monitor reference tracking (initialized on first use) */
+    ReferenceTable  jniMonitorRefTable;
+
+    /* hack to make JNI_OnLoad work right */
+    Object*     classLoaderOverride;
+
+    /* mutex to guard the interrupted and the waitMonitor members */
+    pthread_mutex_t    waitMutex;
+
+    /* pointer to the monitor lock we're currently waiting on */
+    /* guarded by waitMutex */
+    /* TODO: consider changing this to Object* for better JDWP interaction */
+    Monitor*    waitMonitor;
+
+    /* thread "interrupted" status; stays raised until queried or thrown */
+    /* guarded by waitMutex */
+    bool        interrupted;
+
+    /* links to the next thread in the wait set this thread is part of */
+    struct Thread*     waitNext;
+
+    /* object to sleep on while we are waiting for a monitor */
+    pthread_cond_t     waitCond;
+
+    /*
+     * Set to true when the thread is in the process of throwing an
+     * OutOfMemoryError.
+     */
+    bool        throwingOOME;
+
+    /* links to rest of thread list; grab global lock before traversing */
+    struct Thread* prev;
+    struct Thread* next;
+
+    /* used by threadExitCheck when a thread exits without detaching */
+    int         threadExitCheckCount;
+
+    /* JDWP invoke-during-breakpoint support */
+    DebugInvokeReq  invokeReq;
+
+    /* base time for per-thread CPU timing (used by method profiling) */
+    bool        cpuClockBaseSet;
+    u8          cpuClockBase;
+
+    /* previous stack trace sample and length (used by sampling profiler) */
+    const Method** stackTraceSample;
+    size_t stackTraceSampleLength;
+
+    /* memory allocation profiling state */
+    AllocProfState allocProf;
+
+#ifdef WITH_JNI_STACK_CHECK
+    u4          stackCrc;
+#endif
+
+#if WITH_EXTRA_GC_CHECKS > 1
+    /* PC, saved on every instruction; redundant with StackSaveArea */
+    const u2*   currentPc2;
+#endif
+
+    /* Safepoint callback state */
+    pthread_mutex_t   callbackMutex;
+    SafePointCallback callback;
+    void*             callbackArg;
+
+#if defined(ARCH_IA32) && defined(WITH_JIT)
+    u4 spillRegion[MAX_SPILL_JIT_IA];
+#endif
+};
+
+/* start point for an internal thread; mimics pthread args */
+typedef void* (*InternalThreadStart)(void* arg);
+
+/* args for internal thread creation */
+struct InternalStartArgs {
+    /* inputs */
+    InternalThreadStart func;
+    void*       funcArg;
+    char*       name;
+    Object*     group;
+    bool        isDaemon;
+    /* result */
+    volatile Thread** pThread;
+    volatile int*     pCreateStatus;
+};
+
+/* finish init */
+bool dvmPrepMainForJni(JNIEnv* pEnv);
+bool dvmPrepMainThread(void);
+
+/* utility function to get the tid */
+pid_t dvmGetSysThreadId(void);
+
+/*
+ * Get our Thread* from TLS.
+ *
+ * Returns NULL if this isn't a thread that the VM is aware of.
+ */
+Thread* dvmThreadSelf(void);
+
+/* grab the thread list global lock */
+void dvmLockThreadList(Thread* self);
+/* try to grab the thread list global lock */
+bool dvmTryLockThreadList(void);
+/* release the thread list global lock */
+void dvmUnlockThreadList(void);
+
+/*
+ * Thread suspend/resume, used by the GC and debugger.
+ */
+enum SuspendCause {
+    SUSPEND_NOT = 0,
+    SUSPEND_FOR_GC,
+    SUSPEND_FOR_DEBUG,
+    SUSPEND_FOR_DEBUG_EVENT,
+    SUSPEND_FOR_STACK_DUMP,
+    SUSPEND_FOR_DEX_OPT,
+    SUSPEND_FOR_VERIFY,
+    SUSPEND_FOR_HPROF,
+    SUSPEND_FOR_SAMPLING,
+#if defined(WITH_JIT)
+    SUSPEND_FOR_TBL_RESIZE,  // jit-table resize
+    SUSPEND_FOR_IC_PATCH,    // polymorphic callsite inline-cache patch
+    SUSPEND_FOR_CC_RESET,    // code-cache reset
+    SUSPEND_FOR_REFRESH,     // Reload data cached in interpState
+#endif
+};
+void dvmSuspendThread(Thread* thread);
+void dvmSuspendSelf(bool jdwpActivity);
+void dvmResumeThread(Thread* thread);
+void dvmSuspendAllThreads(SuspendCause why);
+void dvmResumeAllThreads(SuspendCause why);
+void dvmUndoDebuggerSuspensions(void);
+
+/*
+ * Check suspend state.  Grab threadListLock before calling.
+ */
+bool dvmIsSuspended(const Thread* thread);
+
+/*
+ * Wait until a thread has suspended.  (Used by debugger support.)
+ */
+void dvmWaitForSuspend(Thread* thread);
+
+/*
+ * Check to see if we should be suspended now.  If so, suspend ourselves
+ * by sleeping on a condition variable.
+ */
+extern "C" bool dvmCheckSuspendPending(Thread* self);
+
+/*
+ * Fast test for use in the interpreter.  Returns "true" if our suspend
+ * count is nonzero.
+ */
+INLINE bool dvmCheckSuspendQuick(Thread* self) {
+    return (self->interpBreak.ctl.subMode & kSubModeSuspendPending);
+}
+
+/*
+ * Used when changing thread state.  Threads may only change their own.
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * If you're calling this before waiting on a resource (e.g. THREAD_WAIT
+ * or THREAD_MONITOR), do so in the same function as the wait -- this records
+ * the current stack depth for the GC.
+ *
+ * If you're changing to THREAD_RUNNING, this will check for suspension.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus);
+
+/*
+ * Initialize a mutex.
+ */
+INLINE void dvmInitMutex(pthread_mutex_t* pMutex)
+{
+#ifdef CHECK_MUTEX
+    pthread_mutexattr_t attr;
+    int cc;
+
+    pthread_mutexattr_init(&attr);
+    cc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+    assert(cc == 0);
+    pthread_mutex_init(pMutex, &attr);
+    pthread_mutexattr_destroy(&attr);
+#else
+    pthread_mutex_init(pMutex, NULL);       // default=PTHREAD_MUTEX_FAST_NP
+#endif
+}
+
+/*
+ * Grab a plain mutex.
+ */
+INLINE void dvmLockMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_lock(pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Try grabbing a plain mutex.  Returns 0 if successful.
+ */
+INLINE int dvmTryLockMutex(pthread_mutex_t* pMutex)
+{
+    int cc = pthread_mutex_trylock(pMutex);
+    assert(cc == 0 || cc == EBUSY);
+    return cc;
+}
+
+/*
+ * Unlock pthread mutex.
+ */
+INLINE void dvmUnlockMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_unlock(pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Destroy a mutex.
+ */
+INLINE void dvmDestroyMutex(pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_mutex_destroy(pMutex);
+    assert(cc == 0);
+}
+
+INLINE void dvmBroadcastCond(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+    assert(cc == 0);
+}
+
+INLINE void dvmSignalCond(pthread_cond_t* pCond)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+    assert(cc == 0);
+}
+
+INLINE void dvmWaitCond(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+    int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+    assert(cc == 0);
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize);
+
+/*
+ * Create a thread internal to the VM.  It's visible to interpreted code,
+ * but found in the "system" thread group rather than "main".
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+    InternalThreadStart func, void* funcArg);
+
+/*
+ * Attach or detach the current thread from the VM.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon);
+void dvmDetachCurrentThread(void);
+
+/*
+ * Get the "main" or "system" thread group.
+ */
+Object* dvmGetMainThreadGroup(void);
+Object* dvmGetSystemThreadGroup(void);
+
+/*
+ * Given a java/lang/VMThread object, return our Thread.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj);
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle);
+
+/*
+ * Given a thread ID, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId);
+
+/*
+ * Sleep in a thread.  Returns when the sleep timer returns or the thread
+ * is interrupted.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Get the name of a thread.
+ *
+ * For correctness, the caller should hold the thread list lock to ensure
+ * that the thread doesn't go away mid-call.
+ */
+std::string dvmGetThreadName(Thread* thread);
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status);
+
+/*
+ * Return true if a thread is on the internal list.  If it is, the
+ * thread is part of the GC's root set.
+ */
+bool dvmIsOnThreadList(const Thread* thread);
+
+/*
+ * Get/set the JNIEnv field.
+ */
+INLINE JNIEnv* dvmGetThreadJNIEnv(Thread* self) { return self->jniEnv; }
+INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;}
+
+/*
+ * Update the priority value of the underlying pthread.
+ */
+void dvmChangeThreadPriority(Thread* thread, int newPriority);
+
+/* "change flags" values for raise/reset thread priority calls */
+#define kChangedPriority    0x01
+#define kChangedPolicy      0x02
+
+/*
+ * If necessary, raise the thread's priority to nice=0 cgroup=fg.
+ *
+ * Returns bit flags indicating changes made (zero if nothing was done).
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+    SchedPolicy* pSavedThreadPolicy);
+
+/*
+ * Drop the thread priority to what it was before an earlier call to
+ * dvmRaiseThreadPriorityIfNeeded().
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+    int savedThreadPrio, SchedPolicy savedThreadPolicy);
+
+/*
+ * Debug: dump information about a single thread.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning);
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+    bool isRunning);
+
+/*
+ * Debug: dump information about all threads.
+ */
+void dvmDumpAllThreads(bool grabLock);
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock);
+
+/*
+ * Debug: kill a thread to get a debuggerd stack trace.  Leaves the VM
+ * in an uncertain state.
+ */
+void dvmNukeThread(Thread* thread);
+
+/*
+ * Sets the thread's name as pointed to by threadName in task_struct->comm.
+ * Note this field has a limited width, and larger values will be truncated
+ * to this width starting from the end.
+ */
+void dvmSetThreadName(const char *threadName);
+
+#endif  // DALVIK_THREAD_H_
diff --git a/vm/UtfString.cpp b/vm/UtfString.cpp
new file mode 100644
index 0000000..2307fb4
--- /dev/null
+++ b/vm/UtfString.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation, plus java/lang/String convenience
+ * functions.
+ *
+ * In most cases we populate the fields in the String object directly,
+ * rather than going through an instance field lookup.
+ */
+#include "Dalvik.h"
+#include <stdlib.h>
+
+/*
+ * Allocate a new instance of the class String, performing first-use
+ * initialization of the class if necessary. Upon success, the
+ * returned value will have all its fields except hashCode already
+ * filled in, including a reference to a newly-allocated char[] for
+ * the contents, sized as given. Additionally, a reference to the
+ * chars array is stored to the pChars pointer. Callers must
+ * subsequently call dvmReleaseTrackedAlloc() on the result pointer.
+ * This function returns NULL on failure.
+ */
+static StringObject* makeStringObject(u4 charsLength, ArrayObject** pChars)
+{
+    /*
+     * The String class should have already gotten found (but not
+     * necessarily initialized) before making it here. We assert it
+     * explicitly, since historically speaking, we have had bugs with
+     * regard to when the class String gets set up. The assert helps
+     * make any regressions easier to diagnose.
+     */
+    assert(gDvm.classJavaLangString != NULL);
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangString)) {
+        /* Perform first-time use initialization of the class. */
+        if (!dvmInitClass(gDvm.classJavaLangString)) {
+            ALOGE("FATAL: Could not initialize class String");
+            dvmAbort();
+        }
+    }
+
+    Object* result = dvmAllocObject(gDvm.classJavaLangString, ALLOC_DEFAULT);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    ArrayObject* chars = dvmAllocPrimitiveArray('C', charsLength, ALLOC_DEFAULT);
+    if (chars == NULL) {
+        dvmReleaseTrackedAlloc(result, NULL);
+        return NULL;
+    }
+
+    dvmSetFieldInt(result, STRING_FIELDOFF_COUNT, charsLength);
+    dvmSetFieldObject(result, STRING_FIELDOFF_VALUE, (Object*) chars);
+    dvmReleaseTrackedAlloc((Object*) chars, NULL);
+    /* Leave offset and hashCode set to zero. */
+
+    *pChars = chars;
+    return (StringObject*) result;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not yield the same results as the java/lang/String
+ * computeHashCode() function.  (To make sure this doesn't get abused,
+ * I'm initializing the hash code to 1 so they *don't* match up.)
+ *
+ * It would be more correct to invoke dexGetUtf16FromUtf8() here and compute
+ * the hash with the result.  That way, if something encoded the same
+ * character in two different ways, the hash value would be the same.  For
+ * our purposes that isn't necessary.
+ */
+u4 dvmComputeUtf8Hash(const char* utf8Str)
+{
+    u4 hash = 1;
+
+    while (*utf8Str != '\0')
+        hash = hash * 31 + *utf8Str++;
+
+    return hash;
+}
+
+/*
+ * Like "strlen", but for strings encoded with "modified" UTF-8.
+ *
+ * The value returned is the number of characters, which may or may not
+ * be the same as the number of bytes.
+ *
+ * (If this needs optimizing, try: mask against 0xa0, shift right 5,
+ * get increment {1-3} from table of 8 values.)
+ */
+size_t dvmUtf8Len(const char* utf8Str)
+{
+    size_t len = 0;
+    int ic;
+
+    while ((ic = *utf8Str++) != '\0') {
+        len++;
+        if ((ic & 0x80) != 0) {
+            /* two- or three-byte encoding */
+            utf8Str++;
+            if ((ic & 0x20) != 0) {
+                /* three-byte encoding */
+                utf8Str++;
+            }
+        }
+    }
+
+    return len;
+}
+
+/*
+ * Convert a "modified" UTF-8 string to UTF-16.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str)
+{
+    while (*utf8Str != '\0')
+        *utf16Str++ = dexGetUtf16FromUtf8(&utf8Str);
+}
+
+/*
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+static int utf16_utf8ByteLen(const u2* utf16Str, int len)
+{
+    int utf8Len = 0;
+
+    while (len--) {
+        unsigned int uic = *utf16Str++;
+
+        /*
+         * The most common case is (uic > 0 && uic <= 0x7f).
+         */
+        if (uic == 0 || uic > 0x7f) {
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else /*(uic > 0x7f || uic == 0) */
+                utf8Len += 2;
+        } else
+            utf8Len++;
+    }
+    return utf8Len;
+}
+
+/*
+ * Convert a UTF-16 string to UTF-8.
+ *
+ * Make sure you allocate "utf8Str" with the result of utf16_utf8ByteLen(),
+ * not just "len".
+ */
+static void convertUtf16ToUtf8(char* utf8Str, const u2* utf16Str, int len)
+{
+    assert(len >= 0);
+
+    while (len--) {
+        unsigned int uic = *utf16Str++;
+
+        /*
+         * The most common case is (uic > 0 && uic <= 0x7f).
+         */
+        if (uic == 0 || uic > 0x7f) {
+            if (uic > 0x07ff) {
+                *utf8Str++ = (uic >> 12) | 0xe0;
+                *utf8Str++ = ((uic >> 6) & 0x3f) | 0x80;
+                *utf8Str++ = (uic & 0x3f) | 0x80;
+            } else /*(uic > 0x7f || uic == 0)*/ {
+                *utf8Str++ = (uic >> 6) | 0xc0;
+                *utf8Str++ = (uic & 0x3f) | 0x80;
+            }
+        } else {
+            *utf8Str++ = uic;
+        }
+    }
+
+    *utf8Str = '\0';
+}
+
+/*
+ * Use the java/lang/String.computeHashCode() algorithm.
+ */
+static inline u4 computeUtf16Hash(const u2* utf16Str, size_t len)
+{
+    u4 hash = 0;
+
+    while (len--)
+        hash = hash * 31 + *utf16Str++;
+
+    return hash;
+}
+
+u4 dvmComputeStringHash(StringObject* strObj) {
+    int hashCode = dvmGetFieldInt(strObj, STRING_FIELDOFF_HASHCODE);
+    if (hashCode != 0) {
+      return hashCode;
+    }
+    int len = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
+    hashCode = computeUtf16Hash((u2*)(void*)chars->contents + offset, len);
+    dvmSetFieldInt(strObj, STRING_FIELDOFF_HASHCODE, hashCode);
+    return hashCode;
+}
+
+StringObject* dvmCreateStringFromCstr(const char* utf8Str) {
+    assert(utf8Str != NULL);
+    return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(utf8Str));
+}
+
+StringObject* dvmCreateStringFromCstr(const std::string& utf8Str) {
+    return dvmCreateStringFromCstr(utf8Str.c_str());
+}
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+    size_t utf16Length)
+{
+    assert(utf8Str != NULL);
+
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(utf16Length, &chars);
+    if (newObj == NULL) {
+        return NULL;
+    }
+
+    dvmConvertUtf8ToUtf16((u2*)(void*)chars->contents, utf8Str);
+
+    u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, utf16Length);
+    dvmSetFieldInt((Object*) newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+
+    return newObj;
+}
+
+/*
+ * Create a new java/lang/String object, using the given Unicode data.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
+{
+    /* We allow a NULL pointer if the length is zero. */
+    assert(len == 0 || unichars != NULL);
+
+    ArrayObject* chars;
+    StringObject* newObj = makeStringObject(len, &chars);
+    if (newObj == NULL) {
+        return NULL;
+    }
+
+    if (len > 0) memcpy(chars->contents, unichars, len * sizeof(u2));
+
+    u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, len);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+
+    return newObj;
+}
+
+/*
+ * Create a new C string from a java/lang/String object.
+ *
+ * Returns NULL if the object is NULL.
+ */
+char* dvmCreateCstrFromString(const StringObject* jstr)
+{
+    assert(gDvm.classJavaLangString != NULL);
+    if (jstr == NULL) {
+        return NULL;
+    }
+
+    int len = dvmGetFieldInt(jstr, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(jstr, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(jstr, STRING_FIELDOFF_VALUE);
+    const u2* data = (const u2*)(void*)chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    int byteLen = utf16_utf8ByteLen(data, len);
+    char* newStr = (char*) malloc(byteLen+1);
+    if (newStr == NULL) {
+        return NULL;
+    }
+    convertUtf16ToUtf8(newStr, data, len);
+
+    return newStr;
+}
+
+void dvmGetStringUtfRegion(const StringObject* jstr,
+        int start, int len, char* buf)
+{
+    const u2* data = jstr->chars() + start;
+    convertUtf16ToUtf8(buf, data, len);
+}
+
+int StringObject::utfLength() const
+{
+    assert(gDvm.classJavaLangString != NULL);
+
+    int len = dvmGetFieldInt(this, STRING_FIELDOFF_COUNT);
+    int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+    const u2* data = (const u2*)(void*)chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    return utf16_utf8ByteLen(data, len);
+}
+
+int StringObject::length() const
+{
+    return dvmGetFieldInt(this, STRING_FIELDOFF_COUNT);
+}
+
+ArrayObject* StringObject::array() const
+{
+    return (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+}
+
+const u2* StringObject::chars() const
+{
+    int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET);
+    ArrayObject* chars =
+            (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE);
+    return (const u2*)(void*)chars->contents + offset;
+}
+
+
+/*
+ * Compare two String objects.
+ *
+ * This is a dvmHashTableLookup() callback.  The function has already
+ * compared their hash values; we need to do a full compare to ensure
+ * that the strings really match.
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2)
+{
+    const StringObject* strObj1 = (const StringObject*) vstrObj1;
+    const StringObject* strObj2 = (const StringObject*) vstrObj2;
+
+    assert(gDvm.classJavaLangString != NULL);
+
+    /* get offset and length into char array; all values are in 16-bit units */
+    int len1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_COUNT);
+    int offset1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_OFFSET);
+    int len2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_COUNT);
+    int offset2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_OFFSET);
+    if (len1 != len2) {
+        return len1 - len2;
+    }
+
+    ArrayObject* chars1 =
+            (ArrayObject*) dvmGetFieldObject(strObj1, STRING_FIELDOFF_VALUE);
+    ArrayObject* chars2 =
+            (ArrayObject*) dvmGetFieldObject(strObj2, STRING_FIELDOFF_VALUE);
+
+    /* damage here actually indicates a broken java/lang/String */
+    assert(offset1 + len1 <= (int) chars1->length);
+    assert(offset2 + len2 <= (int) chars2->length);
+
+    return memcmp((const u2*)(void*)chars1->contents + offset1,
+                  (const u2*)(void*)chars2->contents + offset2,
+                  len1 * sizeof(u2));
+}
+
+ArrayObject* dvmCreateStringArray(const std::vector<std::string>& strings) {
+    Thread* self = dvmThreadSelf();
+
+    // Allocate an array to hold the String objects.
+    ClassObject* elementClass = dvmFindArrayClassForElement(gDvm.classJavaLangString);
+    ArrayObject* stringArray = dvmAllocArrayByClass(elementClass, strings.size(), ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        // Probably OOM.
+        assert(dvmCheckException(self));
+        return NULL;
+    }
+
+    // Create the individual String objects and add them to the array.
+    for (size_t i = 0; i < strings.size(); i++) {
+        Object* str = (Object*) dvmCreateStringFromCstr(strings[i]);
+        if (str == NULL) {
+            // Probably OOM; drop out now.
+            assert(dvmCheckException(self));
+            dvmReleaseTrackedAlloc((Object*) stringArray, self);
+            return NULL;
+        }
+        dvmSetObjectArrayElement(stringArray, i, str);
+        /* stored in tracked array, okay to release */
+        dvmReleaseTrackedAlloc(str, self);
+    }
+
+    return stringArray;
+}
diff --git a/vm/UtfString.h b/vm/UtfString.h
new file mode 100644
index 0000000..352948c
--- /dev/null
+++ b/vm/UtfString.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation functions, plus convenience
+ * functions for working with java/lang/String.
+ */
+#ifndef DALVIK_STRING_H_
+#define DALVIK_STRING_H_
+
+#include <string>
+#include <vector>
+
+/*
+ * (This is private to UtfString.c, but we cheat a bit and also use it
+ * for InlineNative.c.  Not really worth creating a separate header.)
+ *
+ * We can avoid poking around in gDvm by hard-coding the expected values of
+ * the String field offsets.  This will be annoying if String is in flux
+ * or the VM field layout is changing, so we use defines here to make it
+ * easy to switch back to the gDvm version.
+ *
+ * The values are checked for correctness during startup.
+ */
+//#define USE_GLOBAL_STRING_DEFS
+#ifdef USE_GLOBAL_STRING_DEFS
+# define STRING_FIELDOFF_VALUE      gDvm.offJavaLangString_value
+# define STRING_FIELDOFF_OFFSET     gDvm.offJavaLangString_offset
+# define STRING_FIELDOFF_COUNT      gDvm.offJavaLangString_count
+# define STRING_FIELDOFF_HASHCODE   gDvm.offJavaLangString_hashCode
+#else
+# define STRING_FIELDOFF_VALUE      8
+# define STRING_FIELDOFF_HASHCODE   12
+# define STRING_FIELDOFF_OFFSET     16
+# define STRING_FIELDOFF_COUNT      20
+#endif
+
+/*
+ * Hash function for modified UTF-8 strings.
+ */
+u4 dvmComputeUtf8Hash(const char* str);
+
+/*
+ * Hash function for string objects. Ensures the hash code field is
+ * populated and returns its value.
+ */
+u4 dvmComputeStringHash(StringObject* strObj);
+
+/*
+ * Create a java.lang.String[] from a vector of C++ strings.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the returned array,
+ * but not on the individual elements.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+ArrayObject* dvmCreateStringArray(const std::vector<std::string>& strings);
+
+/*
+ * Create a java/lang/String from a C string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a C++ string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const std::string& utf8Str);
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+    u4 utf16Length);
+
+/*
+ * Compute the number of characters in a "modified UTF-8" string.  This will
+ * match the result from strlen() so long as there are no multi-byte chars.
+ */
+size_t dvmUtf8Len(const char* utf8Str);
+
+/*
+ * Convert a UTF-8 string to UTF-16.  "utf16Str" must have enough room
+ * to hold the output.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a Unicode string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len);
+
+/*
+ * Create a UTF-8 C string from a java/lang/String.  Caller must free
+ * the result.
+ *
+ * Returns NULL if "jstr" is NULL.
+ */
+char* dvmCreateCstrFromString(const StringObject* jstr);
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String.  (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmGetStringUtfRegion(const StringObject* jstr,
+        int start, int len, char* buf);
+
+/*
+ * Compare two string objects.  (This is a dvmHashTableLookup() callback.)
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2);
+
+#endif  // DALVIK_STRING_H_
diff --git a/vm/alloc/Alloc.cpp b/vm/alloc/Alloc.cpp
new file mode 100644
index 0000000..3d40d11
--- /dev/null
+++ b/vm/alloc/Alloc.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Garbage-collecting memory allocator.
+ */
+#include "Dalvik.h"
+#include "Globals.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "cutils/atomic.h"
+#include "cutils/atomic-inline.h"
+
+/*
+ * Initialize the GC universe.
+ *
+ * We're currently using a memory-mapped arena to keep things off of the
+ * main heap.  This needs to be replaced with something real.
+ */
+bool dvmGcStartup()
+{
+    dvmInitMutex(&gDvm.gcHeapLock);
+    pthread_cond_init(&gDvm.gcHeapCond, NULL);
+    return dvmHeapStartup();
+}
+
+/*
+ * Post-zygote heap initialization, including starting
+ * the HeapWorker thread.
+ */
+bool dvmGcStartupAfterZygote()
+{
+    return dvmHeapStartupAfterZygote();
+}
+
+/*
+ * Shutdown the threads internal to the garbage collector.
+ */
+void dvmGcThreadShutdown()
+{
+    dvmHeapThreadShutdown();
+}
+
+/*
+ * Shut the GC down.
+ */
+void dvmGcShutdown()
+{
+    //TODO: grab and destroy the lock
+    dvmHeapShutdown();
+}
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork()
+{
+    return dvmHeapSourceStartupBeforeFork();
+}
+
+bool dvmGcStartupClasses()
+{
+    ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
+    if (klass == NULL) {
+        return false;
+    }
+    Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
+    if (method == NULL) {
+        return false;
+    }
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    JValue unusedResult;
+    dvmCallMethod(self, method, NULL, &unusedResult);
+    return true;
+}
+
+/*
+ * Create a "stock instance" of an exception class.
+ */
+static Object* createStockException(const char* descriptor, const char* msg)
+{
+    Thread* self = dvmThreadSelf();
+    StringObject* msgStr = NULL;
+    ClassObject* clazz;
+    Method* init;
+    Object* obj;
+
+    /* find class, initialize if necessary */
+    clazz = dvmFindSystemClass(descriptor);
+    if (clazz == NULL) {
+        ALOGE("Unable to find %s", descriptor);
+        return NULL;
+    }
+
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
+            "(Ljava/lang/String;)V");
+    if (init == NULL) {
+        ALOGE("Unable to find String-arg constructor for %s", descriptor);
+        return NULL;
+    }
+
+    obj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    if (obj == NULL)
+        return NULL;
+
+    if (msg == NULL) {
+        msgStr = NULL;
+    } else {
+        msgStr = dvmCreateStringFromCstr(msg);
+        if (msgStr == NULL) {
+            ALOGW("Could not allocate message string \"%s\"", msg);
+            dvmReleaseTrackedAlloc(obj, self);
+            return NULL;
+        }
+    }
+
+    JValue unused;
+    dvmCallMethod(self, init, obj, &unused, msgStr);
+    if (dvmCheckException(self)) {
+        dvmReleaseTrackedAlloc((Object*) msgStr, self);
+        dvmReleaseTrackedAlloc(obj, self);
+        return NULL;
+    }
+
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // okay if msgStr NULL
+    return obj;
+}
+
+/*
+ * Create some "stock" exceptions.  These can be thrown when the system is
+ * too screwed up to allocate and initialize anything, or when we don't
+ * need a meaningful stack trace.
+ *
+ * We can't do this during the initial startup because we need to execute
+ * the constructors.
+ */
+bool dvmCreateStockExceptions()
+{
+    /*
+     * Pre-allocate some throwables.  These need to be explicitly added
+     * to the GC's root set (see dvmHeapMarkRootSet()).
+     */
+    gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
+        "[memory exhausted]");
+    dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
+    gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
+        "[pre-allocated]");
+    dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
+    gDvm.noClassDefFoundErrorObj =
+        createStockException("Ljava/lang/NoClassDefFoundError;",
+            "[generic]");
+    dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
+
+    if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
+        gDvm.noClassDefFoundErrorObj == NULL)
+    {
+        ALOGW("Unable to create stock exceptions");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Create an instance of the specified class.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmAllocObject(ClassObject* clazz, int flags)
+{
+    Object* newObj;
+
+    assert(clazz != NULL);
+    assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
+
+    /* allocate on GC heap; memory is zeroed out */
+    newObj = (Object*)dvmMalloc(clazz->objectSize, flags);
+    if (newObj != NULL) {
+        DVM_OBJECT_INIT(newObj, clazz);
+        dvmTrackAllocation(clazz, clazz->objectSize);   /* notify DDMS */
+    }
+
+    return newObj;
+}
+
+/*
+ * Create a copy of an object, for Object.clone().
+ *
+ * We use the size actually allocated, rather than obj->clazz->objectSize,
+ * because the latter doesn't work for array objects.
+ */
+Object* dvmCloneObject(Object* obj, int flags)
+{
+    assert(dvmIsValidObject(obj));
+    ClassObject* clazz = obj->clazz;
+
+    /* Class.java shouldn't let us get here (java.lang.Class is final
+     * and does not implement Clonable), but make extra sure.
+     * A memcpy() clone will wreak havoc on a ClassObject's "innards".
+     */
+    assert(!dvmIsTheClassClass(clazz));
+
+    size_t size;
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+        size = dvmArrayObjectSize((ArrayObject *)obj);
+    } else {
+        size = clazz->objectSize;
+    }
+
+    Object* copy = (Object*)dvmMalloc(size, flags);
+    if (copy == NULL)
+        return NULL;
+
+    DVM_OBJECT_INIT(copy, clazz);
+    size_t offset = sizeof(Object);
+    /* Copy instance data.  We assume memcpy copies by words. */
+    memcpy((char*)copy + offset, (char*)obj + offset, size - offset);
+
+    /* Mark the clone as finalizable if appropriate. */
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
+        dvmSetFinalizable(copy);
+    }
+
+    dvmTrackAllocation(clazz, size);    /* notify DDMS */
+
+    return copy;
+}
+
+
+/*
+ * Track an object that was allocated internally and isn't yet part of the
+ * VM root set.
+ *
+ * We could do this per-thread or globally.  If it's global we don't have
+ * to do the thread lookup but we do have to synchronize access to the list.
+ *
+ * "obj" must not be NULL.
+ *
+ * NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will
+ * usually be NULL since we're being called from dvmMalloc().
+ */
+void dvmAddTrackedAlloc(Object* obj, Thread* self)
+{
+    if (self == NULL)
+        self = dvmThreadSelf();
+
+    assert(obj != NULL);
+    assert(self != NULL);
+    if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
+        ALOGE("threadid=%d: unable to add %p to internal ref table",
+            self->threadId, obj);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+}
+
+/*
+ * Stop tracking an object.
+ *
+ * We allow attempts to delete NULL "obj" so that callers don't have to wrap
+ * calls with "if != NULL".
+ */
+void dvmReleaseTrackedAlloc(Object* obj, Thread* self)
+{
+    if (obj == NULL)
+        return;
+
+    if (self == NULL)
+        self = dvmThreadSelf();
+    assert(self != NULL);
+
+    if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable,
+            self->internalLocalRefTable.table, obj))
+    {
+        ALOGE("threadid=%d: failed to remove %p from internal ref table",
+            self->threadId, obj);
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Explicitly initiate garbage collection.
+ */
+void dvmCollectGarbage()
+{
+    if (gDvm.disableExplicitGc) {
+        return;
+    }
+    dvmLockHeap();
+    dvmWaitForConcurrentGcToComplete();
+    dvmCollectGarbageInternal(GC_EXPLICIT);
+    dvmUnlockHeap();
+}
+
+/*
+ * Run finalization.
+ */
+void dvmRunFinalization() {
+  Thread *self = dvmThreadSelf();
+  assert(self != NULL);
+  JValue unusedResult;
+  assert(gDvm.methJavaLangSystem_runFinalization != NULL);
+  dvmCallMethod(self, gDvm.methJavaLangSystem_runFinalization, NULL, &unusedResult);
+}
+
+struct CountContext {
+    const ClassObject *clazz;
+    size_t count;
+};
+
+static void countInstancesOfClassCallback(Object *obj, void *arg)
+{
+    CountContext *ctx = (CountContext *)arg;
+    assert(ctx != NULL);
+    if (obj->clazz == ctx->clazz) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    dvmLockHeap();
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
+
+static void countAssignableInstancesOfClassCallback(Object *obj, void *arg)
+{
+    CountContext *ctx = (CountContext *)arg;
+    assert(ctx != NULL);
+    if (obj->clazz != NULL && dvmInstanceof(obj->clazz, ctx->clazz)) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    dvmLockHeap();
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
+
+bool dvmIsHeapAddress(void *address)
+{
+    return address != NULL && (((uintptr_t) address & (8-1)) == 0);
+}
+
+bool dvmIsNonMovingObject(const Object* object)
+{
+    return true;
+}
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
new file mode 100644
index 0000000..b838719
--- /dev/null
+++ b/vm/alloc/Alloc.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Garbage-collecting allocator.
+ */
+#ifndef DALVIK_ALLOC_ALLOC_H_
+#define DALVIK_ALLOC_ALLOC_H_
+
+#include <stddef.h>
+
+/* flags for dvmMalloc */
+enum {
+    ALLOC_DEFAULT = 0x00,
+    ALLOC_DONT_TRACK = 0x01,  /* don't add to internal tracking list */
+    ALLOC_NON_MOVING = 0x02,
+};
+
+/*
+ * Initialization.
+ */
+bool dvmGcStartup(void);
+bool dvmCreateStockExceptions(void);
+bool dvmGcStartupAfterZygote(void);
+void dvmGcShutdown(void);
+void dvmGcThreadShutdown(void);
+bool dvmGcStartupClasses(void);
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork(void);
+
+/*
+ * Basic allocation function.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+void* dvmMalloc(size_t size, int flags);
+
+/*
+ * Allocate a new object.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+extern "C" Object* dvmAllocObject(ClassObject* clazz, int flags);
+
+/*
+ * Track an object reference that is currently only visible internally.
+ * This is called automatically by dvmMalloc() unless ALLOC_DONT_TRACK
+ * is set.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+extern "C" void dvmAddTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Remove an object from the internal tracking list.
+ *
+ * Does nothing if "obj" is NULL.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+extern "C" void dvmReleaseTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Returns true iff <obj> points to a zygote allocated object.
+ */
+bool dvmIsZygoteObject(const Object* obj);
+
+/*
+ * Create a copy of an object.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmCloneObject(Object* obj, int flags);
+
+/*
+ * Make the object finalizable.
+ */
+extern "C" void dvmSetFinalizable(Object* obj);
+
+/*
+ * Determine the exact number of GC heap bytes used by an object.  (Internal
+ * to heap code except for debugging.)
+ */
+size_t dvmObjectSizeInHeap(const Object* obj);
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization(void);
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget);
+
+/*
+ * Initiate garbage collection.
+ *
+ * This usually happens automatically, but can also be caused by
+ * Runtime.gc().
+ */
+void dvmCollectGarbage(void);
+
+/*
+ * Calls System.runFinalization().
+ */
+void dvmRunFinalization();
+
+/*
+ * Returns a count of the direct instances of a class.
+ */
+size_t dvmCountInstancesOfClass(const ClassObject *clazz);
+
+/*
+ * Returns a count of the instances of a class and its subclasses.
+ */
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz);
+
+/*
+ * Removes any growth limits from the heap.
+ */
+void dvmClearGrowthLimit(void);
+
+/*
+ * Returns true if the address is aligned appropriately for a heap object.
+ * Does not require the caller to hold the heap lock, and does not take the
+ * heap lock internally.
+ */
+bool dvmIsHeapAddress(void *address);
+
+bool dvmIsNonMovingObject(const Object* object);
+
+#endif  // DALVIK_ALLOC_ALLOC_H_
diff --git a/vm/alloc/CardTable.cpp b/vm/alloc/CardTable.cpp
new file mode 100644
index 0000000..87143dc
--- /dev/null
+++ b/vm/alloc/CardTable.cpp
@@ -0,0 +1,394 @@
+/*
+ * 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.
+ */
+
+#include <sys/mman.h>  /* for PROT_* */
+
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ *
+ * The heap is divided into "cards" of GC_CARD_SIZE bytes, as
+ * determined by GC_CARD_SHIFT. The card table contains one byte of
+ * data per card, to be used by the GC. The value of the byte will be
+ * one of GC_CARD_CLEAN or GC_CARD_DIRTY.
+ *
+ * After any store of a non-NULL object pointer into a heap object,
+ * code is obliged to mark the card dirty. The setters in
+ * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
+ * JIT and fast interpreters also contain code to mark cards as dirty.
+ *
+ * The card table's base [the "biased card table"] gets set to a
+ * rather strange value.  In order to keep the JIT from having to
+ * fabricate or load GC_DIRTY_CARD to store into the card table,
+ * biased base is within the mmap allocation at a point where it's low
+ * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
+ */
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit)
+{
+    size_t length;
+    void *allocBase;
+    u1 *biasedBase;
+    GcHeap *gcHeap = gDvm.gcHeap;
+    int offset;
+    void *heapBase = dvmHeapSourceGetBase();
+    assert(gcHeap != NULL);
+    assert(heapBase != NULL);
+    /* All zeros is the correct initial value; all clean. */
+    assert(GC_CARD_CLEAN == 0);
+
+    /* Set up the card table */
+    length = heapMaximumSize / GC_CARD_SIZE;
+    /* Allocate an extra 256 bytes to allow fixed low-byte of base */
+    allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
+                            "dalvik-card-table");
+    if (allocBase == NULL) {
+        return false;
+    }
+    gcHeap->cardTableBase = (u1*)allocBase;
+    gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE;
+    gcHeap->cardTableMaxLength = length;
+    biasedBase = (u1 *)((uintptr_t)allocBase -
+                       ((uintptr_t)heapBase >> GC_CARD_SHIFT));
+    offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
+    gcHeap->cardTableOffset = offset + (offset < 0 ? 0x100 : 0);
+    biasedBase += gcHeap->cardTableOffset;
+    assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
+    gDvm.biasedCardTableBase = biasedBase;
+
+    return true;
+}
+
+/*
+ * Tears down the entire CardTable.
+ */
+void dvmCardTableShutdown()
+{
+    gDvm.biasedCardTableBase = NULL;
+    munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
+}
+
+void dvmClearCardTable()
+{
+    /*
+     * The goal is to zero out some mmap-allocated pages.  We can accomplish
+     * this with memset() or madvise(MADV_DONTNEED).  The latter has some
+     * useful properties, notably that the pages are returned to the system,
+     * so cards for parts of the heap we haven't expanded into won't be
+     * allocated physical pages.  On the other hand, if we un-map the card
+     * area, we'll have to fault it back in as we resume dirtying objects,
+     * which reduces performance.
+     *
+     * We don't cause any correctness issues by failing to clear cards; we
+     * just take a performance hit during the second pause of the concurrent
+     * collection.  The "advisory" nature of madvise() isn't a big problem.
+     *
+     * What we really want to do is:
+     * (1) zero out all cards that were touched
+     * (2) use madvise() to release any pages that won't be used in the near
+     *     future
+     *
+     * For #1, we don't really know which cards were touched, but we can
+     * approximate it with the "live bits max" value, which tells us the
+     * highest start address at which an object was allocated.  This may
+     * leave vestigial nonzero entries at the end if temporary objects are
+     * created during a concurrent GC, but that should be harmless.  (We
+     * can round up to the end of the card table page to reduce this.)
+     *
+     * For #2, we don't know which pages will be used in the future.  Some
+     * simple experiments suggested that a "typical" app will touch about
+     * 60KB of pages while initializing, but drops down to 20-24KB while
+     * idle.  We can save a few hundred KB system-wide with aggressive
+     * use of madvise().  The cost of mapping those pages back in is paid
+     * outside of the GC pause, which reduces the impact.  (We might be
+     * able to get the benefits by only doing this occasionally, e.g. if
+     * the heap shrinks a lot or we somehow notice that we've been idle.)
+     *
+     * Note that cardTableLength is initially set to the growth limit, and
+     * on request will be expanded to the heap maximum.
+     */
+    assert(gDvm.gcHeap->cardTableBase != NULL);
+
+    if (gDvm.lowMemoryMode) {
+      // zero out cards with madvise(), discarding all pages in the card table
+      madvise(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength, MADV_DONTNEED);
+    } else {
+      // zero out cards with memset(), using liveBits as an estimate
+      const HeapBitmap* liveBits = dvmHeapSourceGetLiveBits();
+      size_t maxLiveCard = (liveBits->max - liveBits->base) / GC_CARD_SIZE;
+      maxLiveCard = ALIGN_UP_TO_PAGE_SIZE(maxLiveCard);
+      if (maxLiveCard > gDvm.gcHeap->cardTableLength) {
+          maxLiveCard = gDvm.gcHeap->cardTableLength;
+      }
+
+      memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, maxLiveCard);
+    }
+}
+
+/*
+ * Returns true iff the address is within the bounds of the card table.
+ */
+bool dvmIsValidCard(const u1 *cardAddr)
+{
+    GcHeap *h = gDvm.gcHeap;
+    u1* begin = h->cardTableBase + h->cardTableOffset;
+    u1* end = &begin[h->cardTableLength];
+    return cardAddr >= begin && cardAddr < end;
+}
+
+/*
+ * Returns the address of the relevant byte in the card table, given
+ * an address on the heap.
+ */
+u1 *dvmCardFromAddr(const void *addr)
+{
+    u1 *biasedBase = gDvm.biasedCardTableBase;
+    u1 *cardAddr = biasedBase + ((uintptr_t)addr >> GC_CARD_SHIFT);
+    assert(dvmIsValidCard(cardAddr));
+    return cardAddr;
+}
+
+/*
+ * Returns the first address in the heap which maps to this card.
+ */
+void *dvmAddrFromCard(const u1 *cardAddr)
+{
+    assert(dvmIsValidCard(cardAddr));
+    uintptr_t offset = cardAddr - gDvm.biasedCardTableBase;
+    return (void *)(offset << GC_CARD_SHIFT);
+}
+
+/*
+ * Dirties the card for the given address.
+ */
+void dvmMarkCard(const void *addr)
+{
+    u1 *cardAddr = dvmCardFromAddr(addr);
+    *cardAddr = GC_CARD_DIRTY;
+}
+
+/*
+ * Returns true if the object is on a dirty card.
+ */
+static bool isObjectDirty(const Object *obj)
+{
+    assert(obj != NULL);
+    assert(dvmIsValidObject(obj));
+    u1 *card = dvmCardFromAddr(obj);
+    return *card == GC_CARD_DIRTY;
+}
+
+/*
+ * Context structure for verifying the card table.
+ */
+struct WhiteReferenceCounter {
+    HeapBitmap *markBits;
+    size_t whiteRefs;
+};
+
+/*
+ * Visitor that counts white referents.
+ */
+static void countWhiteReferenceVisitor(void *addr, void *arg)
+{
+    WhiteReferenceCounter *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    assert(dvmIsValidObject(obj));
+    ctx = (WhiteReferenceCounter *)arg;
+    if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+        return;
+    }
+    ctx->whiteRefs += 1;
+}
+
+/*
+ * Visitor that logs white references.
+ */
+static void dumpWhiteReferenceVisitor(void *addr, void *arg)
+{
+    WhiteReferenceCounter *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    assert(dvmIsValidObject(obj));
+    ctx = (WhiteReferenceCounter*)arg;
+    if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+        return;
+    }
+    ALOGE("object %p is white", obj);
+}
+
+/*
+ * Visitor that signals the caller when a matching reference is found.
+ */
+static void dumpReferencesVisitor(void *pObj, void *arg)
+{
+    Object *obj = *(Object **)pObj;
+    Object *lookingFor = *(Object **)arg;
+    if (lookingFor != NULL && lookingFor == obj) {
+        *(Object **)arg = NULL;
+    }
+}
+
+static void dumpReferencesCallback(Object *obj, void *arg)
+{
+    if (obj == (Object *)arg) {
+        return;
+    }
+    dvmVisitObject(dumpReferencesVisitor, obj, &arg);
+    if (arg == NULL) {
+        ALOGD("Found %p in the heap @ %p", arg, obj);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Root visitor that looks for matching references.
+ */
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
+{
+    Object *obj = *(Object **)ptr;
+    Object *lookingFor = *(Object **)arg;
+    if (obj == lookingFor) {
+        ALOGD("Found %p in a root @ %p", arg, ptr);
+    }
+}
+
+/*
+ * Invokes visitors to search for references to an object.
+ */
+static void dumpReferences(const Object *obj)
+{
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    void *arg = (void *)obj;
+    dvmVisitRoots(dumpReferencesRootVisitor, arg);
+    dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg);
+}
+
+/*
+ * Returns true if the given object is a reference object and the
+ * just the referent is unmarked.
+ */
+static bool isReferentUnmarked(const Object *obj,
+                               const WhiteReferenceCounter* ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    if (ctx->whiteRefs != 1) {
+        return false;
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        size_t offset = gDvm.offJavaLangRefReference_referent;
+        const Object *referent = dvmGetFieldObject(obj, offset);
+        return !dvmHeapBitmapIsObjectBitSet(ctx->markBits, referent);
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Returns true if the given object is a string and has been interned
+ * by the user.
+ */
+static bool isWeakInternedString(const Object *obj)
+{
+    assert(obj != NULL);
+    if (obj->clazz == gDvm.classJavaLangString) {
+        return dvmIsWeakInternedString((StringObject *)obj);
+    } else {
+        return false;
+    }
+}
+
+/*
+ * Returns true if the given object has been pushed on the mark stack
+ * by root marking.
+ */
+static bool isPushedOnMarkStack(const Object *obj)
+{
+    GcMarkStack *stack = &gDvm.gcHeap->markContext.stack;
+    for (const Object **ptr = stack->base; ptr < stack->top; ++ptr) {
+        if (*ptr == obj) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Callback applied to marked objects.  If the object is gray and on
+ * an unmarked card an error is logged and the VM is aborted.  Card
+ * table verification occurs between root marking and weak reference
+ * processing.  We treat objects marked from the roots and weak
+ * references specially as it is permissible for these objects to be
+ * gray and on an unmarked card.
+ */
+static void verifyCardTableCallback(Object *obj, void *arg)
+{
+    WhiteReferenceCounter ctx = { (HeapBitmap *)arg, 0 };
+
+    dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx);
+    if (ctx.whiteRefs == 0) {
+        return;
+    } else if (isObjectDirty(obj)) {
+        return;
+    } else if (isReferentUnmarked(obj, &ctx)) {
+        return;
+    } else if (isWeakInternedString(obj)) {
+        return;
+    } else if (isPushedOnMarkStack(obj)) {
+        return;
+    } else {
+        ALOGE("Verify failed, object %p is gray and on an unmarked card", obj);
+        dvmDumpObject(obj);
+        dvmVisitObject(dumpWhiteReferenceVisitor, obj, &ctx);
+        dumpReferences(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Verifies that gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable()
+{
+    HeapBitmap *markBits = gDvm.gcHeap->markContext.bitmap;
+    dvmHeapBitmapWalk(markBits, verifyCardTableCallback, markBits);
+}
diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h
new file mode 100644
index 0000000..43b0563
--- /dev/null
+++ b/vm/alloc/CardTable.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ */
+
+#ifndef DALVIK_ALLOC_CARDTABLE_H_
+#define DALVIK_ALLOC_CARDTABLE_H_
+
+#define GC_CARD_SHIFT 7
+#define GC_CARD_SIZE (1 << GC_CARD_SHIFT)
+#define GC_CARD_CLEAN 0
+#define GC_CARD_DIRTY 0x70
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit);
+
+/*
+ * Tears down the entire CardTable structure.
+ */
+void dvmCardTableShutdown(void);
+
+/*
+ * Resets all of the bytes in the card table to clean.
+ */
+void dvmClearCardTable(void);
+
+/*
+ * Returns the address of the relevent byte in the card table, given
+ * an address on the heap.
+ */
+u1 *dvmCardFromAddr(const void *addr);
+
+/*
+ * Returns the first address in the heap which maps to this card.
+ */
+void *dvmAddrFromCard(const u1 *card);
+
+/*
+ * Returns true if addr points to a valid card.
+ */
+bool dvmIsValidCard(const u1 *card);
+
+/*
+ * Set the card associated with the given address to GC_CARD_DIRTY.
+ */
+void dvmMarkCard(const void *addr);
+
+/*
+ * Verifies that all gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void);
+
+#endif  // DALVIK_ALLOC_CARDTABLE_H_
diff --git a/vm/alloc/Copying.cpp b/vm/alloc/Copying.cpp
new file mode 100644
index 0000000..195670e
--- /dev/null
+++ b/vm/alloc/Copying.cpp
@@ -0,0 +1,2235 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+
+/*
+ * A "mostly copying", generational, garbage collector.
+ *
+ * TODO: we allocate our own contiguous tract of page frames to back
+ * object allocations.  To cooperate with other heaps active in the
+ * virtual machine we need to move the responsibility of allocating
+ * pages someplace outside of this code.
+ *
+ * The other major data structures that maintain the state of the heap
+ * are the block space table and the block queue.
+ *
+ * The block space table records the state of a block.  We must track
+ * whether a block is:
+ *
+ * - Free or allocated in some space.
+ *
+ * - If the block holds part of a large object allocation, whether the
+ *   block is the initial or a continued block of the allocation.
+ *
+ * - Whether the block is pinned, that is to say whether at least one
+ *   object in the block must remain stationary.  Only needed during a
+ *   GC.
+ *
+ * - Which space the object belongs to.  At present this means
+ *   from-space or to-space.
+ *
+ * The block queue is used during garbage collection.  Unlike Cheney's
+ * algorithm, from-space and to-space are not contiguous.  Therefore,
+ * one cannot maintain the state of the copy with just two pointers.
+ * The block queue exists to thread lists of blocks from the various
+ * spaces together.
+ *
+ * Additionally, we record the free space frontier of the heap, as
+ * well as the address of the first object within a block, which is
+ * required to copy objects following a large object (not currently
+ * implemented).  This is stored in the heap source structure.  This
+ * should be moved elsewhere to support in-line allocations from Java
+ * threads.
+ *
+ * Allocation requests are satisfied by reserving storage from one or
+ * more contiguous blocks.  Objects that are small enough to fit
+ * inside a block are packed together within a block.  Objects that
+ * are larger than a block are allocated from contiguous sequences of
+ * blocks.  When half the available blocks are filled, a garbage
+ * collection occurs.  We "flip" spaces (exchange from- and to-space),
+ * copy live objects into to space, and perform pointer adjustment.
+ *
+ * Copying is made more complicated by the requirement that some
+ * objects must not be moved.  This property is known as "pinning".
+ * These objects must be dealt with specially.  We use Bartlett's
+ * scheme; blocks containing such objects are grayed (promoted) at the
+ * start of a garbage collection.  By virtue of this trick, tracing
+ * from the roots proceeds as usual but all objects on those pages are
+ * considered promoted and therefore not moved.
+ *
+ * TODO: there is sufficient information within the garbage collector
+ * to implement Attardi's scheme for evacuating unpinned objects from
+ * a page that is otherwise pinned.  This would eliminate false
+ * retention caused by the large pinning granularity.
+ *
+ * We need a scheme for medium and large objects.  Ignore that for
+ * now, we can return to this later.
+ *
+ * Eventually we need to worry about promoting objects out of the
+ * copy-collected heap (tenuring) into a less volatile space.  Copying
+ * may not always be the best policy for such spaces.  We should
+ * consider a variant of mark, sweep, compact.
+ *
+ * The block scheme allows us to use VM page faults to maintain a
+ * write barrier.  Consider having a special leaf state for a page.
+ *
+ * Bibliography:
+ *
+ * C. J. Cheney. 1970. A non-recursive list compacting
+ * algorithm. CACM. 13-11 pp677--678.
+ *
+ * Joel F. Bartlett. 1988. Compacting Garbage Collection with
+ * Ambiguous Roots. Digital Equipment Corporation.
+ *
+ * Joel F. Bartlett. 1989. Mostly-Copying Garbage Collection Picks Up
+ * Generations and C++. Digital Equipment Corporation.
+ *
+ * G. May Yip. 1991. Incremental, Generational Mostly-Copying Garbage
+ * Collection in Uncooperative Environments. Digital Equipment
+ * Corporation.
+ *
+ * Giuseppe Attardi, Tito Flagella. 1994. A Customisable Memory
+ * Management Framework. TR-94-010
+ *
+ * Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. A customisable
+ * memory management framework for C++. Software -- Practice and
+ * Experience. 28(11), 1143-1183.
+ *
+ */
+
+#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#if 0
+#define LOG_ALLOC ALOGI
+#define LOG_PIN ALOGI
+#define LOG_PROM ALOGI
+#define LOG_REF ALOGI
+#define LOG_SCAV ALOGI
+#define LOG_TRAN ALOGI
+#define LOG_VER ALOGI
+#else
+#define LOG_ALLOC(...) ((void)0)
+#define LOG_PIN(...) ((void)0)
+#define LOG_PROM(...) ((void)0)
+#define LOG_REF(...) ((void)0)
+#define LOG_SCAV(...) ((void)0)
+#define LOG_TRAN(...) ((void)0)
+#define LOG_VER(...) ((void)0)
+#endif
+
+static void enqueueBlock(HeapSource *heapSource, size_t block);
+static void scavengeReference(Object **obj);
+static bool toSpaceContains(const void *addr);
+static bool fromSpaceContains(const void *addr);
+static size_t sumHeapBitmap(const HeapBitmap *bitmap);
+static size_t objectSize(const Object *obj);
+static void scavengeDataObject(Object *obj);
+static void scavengeBlockQueue();
+
+/*
+ * We use 512-byte blocks.
+ */
+enum { BLOCK_SHIFT = 9 };
+enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
+
+/*
+ * Space identifiers, stored into the blockSpace array.
+ */
+enum {
+    BLOCK_FREE = 0,
+    BLOCK_FROM_SPACE = 1,
+    BLOCK_TO_SPACE = 2,
+    BLOCK_CONTINUED = 7
+};
+
+/*
+ * Alignment for all allocations, in bytes.
+ */
+enum { ALLOC_ALIGNMENT = 8 };
+
+/*
+ * Sentinel value for the queue end.
+ */
+#define QUEUE_TAIL (~(size_t)0)
+
+struct HeapSource {
+
+    /* The base address of backing store. */
+    u1 *blockBase;
+
+    /* Total number of blocks available for allocation. */
+    size_t totalBlocks;
+    size_t allocBlocks;
+
+    /*
+     * The scavenger work queue.  Implemented as an array of index
+     * values into the queue.
+     */
+    size_t *blockQueue;
+
+    /*
+     * Base and limit blocks.  Basically the shifted start address of
+     * the block.  We convert blocks to a relative number when
+     * indexing in the block queue.  TODO: make the block queue base
+     * relative rather than the index into the block queue.
+     */
+    size_t baseBlock, limitBlock;
+
+    size_t queueHead;
+    size_t queueTail;
+    size_t queueSize;
+
+    /* The space of the current block 0 (free), 1 or 2. */
+    char *blockSpace;
+
+    /* Start of free space in the current block. */
+    u1 *allocPtr;
+    /* Exclusive limit of free space in the current block. */
+    u1 *allocLimit;
+
+    HeapBitmap allocBits;
+
+    /*
+     * The starting size of the heap.  This value is the same as the
+     * value provided to the -Xms flag.
+     */
+    size_t minimumSize;
+
+    /*
+     * The maximum size of the heap.  This value is the same as the
+     * -Xmx flag.
+     */
+    size_t maximumSize;
+
+    /*
+     * The current, committed size of the heap.  At present, this is
+     * equivalent to the maximumSize.
+     */
+    size_t currentSize;
+
+    size_t bytesAllocated;
+};
+
+static unsigned long alignDown(unsigned long x, unsigned long n)
+{
+    return x & -n;
+}
+
+static unsigned long alignUp(unsigned long x, unsigned long n)
+{
+    return alignDown(x + (n - 1), n);
+}
+
+static void describeBlocks(const HeapSource *heapSource)
+{
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if ((i % 32) == 0) putchar('\n');
+        printf("%d ", heapSource->blockSpace[i]);
+    }
+    putchar('\n');
+}
+
+/*
+ * Virtual memory interface.
+ */
+
+static void *virtualAlloc(size_t length)
+{
+    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+    int prot = PROT_READ | PROT_WRITE;
+    void *addr = mmap(NULL, length, prot, flags, -1, 0);
+    if (addr == MAP_FAILED) {
+        LOGE_HEAP("mmap: %s", strerror(errno));
+        addr = NULL;
+    }
+    return addr;
+}
+
+static void virtualFree(void *addr, size_t length)
+{
+    assert(addr != NULL);
+    assert((uintptr_t)addr % SYSTEM_PAGE_SIZE == 0);
+    int res = munmap(addr, length);
+    if (res == -1) {
+        LOGE_HEAP("munmap: %s", strerror(errno));
+    }
+}
+
+#ifndef NDEBUG
+static int isValidAddress(const HeapSource *heapSource, const u1 *addr)
+{
+    size_t block;
+
+    block = (uintptr_t)addr >> BLOCK_SHIFT;
+    return heapSource->baseBlock <= block &&
+           heapSource->limitBlock > block;
+}
+#endif
+
+/*
+ * Iterate over the block map looking for a contiguous run of free
+ * blocks.
+ */
+static void *allocateBlocks(HeapSource *heapSource, size_t blocks)
+{
+    size_t allocBlocks = heapSource->allocBlocks;
+    size_t totalBlocks = heapSource->totalBlocks;
+    /* Check underflow. */
+    assert(blocks != 0);
+    /* Check overflow. */
+    if (allocBlocks + blocks > totalBlocks / 2) {
+        return NULL;
+    }
+    /* Scan block map. */
+    for (size_t i = 0; i < totalBlocks; ++i) {
+        /* Check fit. */
+        for (size_t j = 0; j < blocks; ++j) { /* runs over totalBlocks */
+            if (heapSource->blockSpace[i+j] != BLOCK_FREE) {
+                break;
+            }
+        }
+        /* No fit? */
+        if (j != blocks) {
+            i += j;
+            continue;
+        }
+        /* Fit, allocate. */
+        heapSource->blockSpace[i] = BLOCK_TO_SPACE; /* why to-space? */
+        for (size_t j = 1; j < blocks; ++j) {
+            heapSource->blockSpace[i+j] = BLOCK_CONTINUED;
+        }
+        heapSource->allocBlocks += blocks;
+        void *addr = &heapSource->blockBase[i*BLOCK_SIZE];
+        memset(addr, 0, blocks*BLOCK_SIZE);
+        /* Collecting? */
+        if (heapSource->queueHead != QUEUE_TAIL) {
+            LOG_ALLOC("allocateBlocks allocBlocks=%zu,block#=%zu", heapSource->allocBlocks, i);
+            /*
+             * This allocated was on behalf of the transporter when it
+             * shaded a white object gray.  We enqueue the block so
+             * the scavenger can further shade the gray objects black.
+             */
+            enqueueBlock(heapSource, i);
+        }
+
+        return addr;
+    }
+    /* Insufficient space, fail. */
+    ALOGE("Insufficient space, %zu blocks, %zu blocks allocated and %zu bytes allocated",
+         heapSource->totalBlocks,
+         heapSource->allocBlocks,
+         heapSource->bytesAllocated);
+    return NULL;
+}
+
+/* Converts an absolute address to a relative block number. */
+static size_t addressToBlock(const HeapSource *heapSource, const void *addr)
+{
+    assert(heapSource != NULL);
+    assert(isValidAddress(heapSource, addr));
+    return (((uintptr_t)addr) >> BLOCK_SHIFT) - heapSource->baseBlock;
+}
+
+/* Converts a relative block number to an absolute address. */
+static u1 *blockToAddress(const HeapSource *heapSource, size_t block)
+{
+    u1 *addr;
+
+    addr = (u1 *) (((uintptr_t) heapSource->baseBlock + block) * BLOCK_SIZE);
+    assert(isValidAddress(heapSource, addr));
+    return addr;
+}
+
+static void clearBlock(HeapSource *heapSource, size_t block)
+{
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    u1 *addr = heapSource->blockBase + block*BLOCK_SIZE;
+    memset(addr, 0xCC, BLOCK_SIZE);
+    for (size_t i = 0; i < BLOCK_SIZE; i += 8) {
+        dvmHeapBitmapClearObjectBit(&heapSource->allocBits, addr + i);
+    }
+}
+
+static void clearFromSpace(HeapSource *heapSource)
+{
+    assert(heapSource != NULL);
+    size_t i = 0;
+    size_t count = 0;
+    while (i < heapSource->totalBlocks) {
+        if (heapSource->blockSpace[i] != BLOCK_FROM_SPACE) {
+            ++i;
+            continue;
+        }
+        heapSource->blockSpace[i] = BLOCK_FREE;
+        clearBlock(heapSource, i);
+        ++i;
+        ++count;
+        while (i < heapSource->totalBlocks &&
+               heapSource->blockSpace[i] == BLOCK_CONTINUED) {
+            heapSource->blockSpace[i] = BLOCK_FREE;
+            clearBlock(heapSource, i);
+            ++i;
+            ++count;
+        }
+    }
+    LOG_SCAV("freed %zu blocks (%zu bytes)", count, count*BLOCK_SIZE);
+}
+
+/*
+ * Appends the given block to the block queue.  The block queue is
+ * processed in-order by the scavenger.
+ */
+static void enqueueBlock(HeapSource *heapSource, size_t block)
+{
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    if (heapSource->queueHead != QUEUE_TAIL) {
+        heapSource->blockQueue[heapSource->queueTail] = block;
+    } else {
+        heapSource->queueHead = block;
+    }
+    heapSource->blockQueue[block] = QUEUE_TAIL;
+    heapSource->queueTail = block;
+    ++heapSource->queueSize;
+}
+
+/*
+ * Grays all objects within the block corresponding to the given
+ * address.
+ */
+static void promoteBlockByAddr(HeapSource *heapSource, const void *addr)
+{
+    size_t block;
+
+    block = addressToBlock(heapSource, (const u1 *)addr);
+    if (heapSource->blockSpace[block] != BLOCK_TO_SPACE) {
+        // LOG_PROM("promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+        heapSource->blockSpace[block] = BLOCK_TO_SPACE;
+        enqueueBlock(heapSource, block);
+        /* TODO(cshapiro): count continued blocks?*/
+        heapSource->allocBlocks += 1;
+    } else {
+        // LOG_PROM("NOT promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+    }
+}
+
+GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
+{
+    GcHeap* gcHeap;
+    HeapSource *heapSource;
+
+    assert(startSize <= absoluteMaxSize);
+
+    heapSource = calloc(1, sizeof(*heapSource));
+    assert(heapSource != NULL);
+
+    heapSource->minimumSize = alignUp(startSize, BLOCK_SIZE);
+    heapSource->maximumSize = alignUp(absoluteMaxSize, BLOCK_SIZE);
+
+    heapSource->currentSize = heapSource->maximumSize;
+
+    /* Allocate underlying storage for blocks. */
+    heapSource->blockBase = virtualAlloc(heapSource->maximumSize);
+    assert(heapSource->blockBase != NULL);
+    heapSource->baseBlock = (uintptr_t) heapSource->blockBase >> BLOCK_SHIFT;
+    heapSource->limitBlock = ((uintptr_t) heapSource->blockBase + heapSource->maximumSize) >> BLOCK_SHIFT;
+
+    heapSource->allocBlocks = 0;
+    heapSource->totalBlocks = (heapSource->limitBlock - heapSource->baseBlock);
+
+    assert(heapSource->totalBlocks == heapSource->maximumSize / BLOCK_SIZE);
+
+    {
+        size_t size = sizeof(heapSource->blockQueue[0]);
+        heapSource->blockQueue = malloc(heapSource->totalBlocks*size);
+        assert(heapSource->blockQueue != NULL);
+        memset(heapSource->blockQueue, 0xCC, heapSource->totalBlocks*size);
+        heapSource->queueHead = QUEUE_TAIL;
+    }
+
+    /* Byte indicating space residence or free status of block. */
+    {
+        size_t size = sizeof(heapSource->blockSpace[0]);
+        heapSource->blockSpace = calloc(1, heapSource->totalBlocks*size);
+        assert(heapSource->blockSpace != NULL);
+    }
+
+    dvmHeapBitmapInit(&heapSource->allocBits,
+                      heapSource->blockBase,
+                      heapSource->maximumSize,
+                      "blockBase");
+
+    /* Initialize allocation pointers. */
+    heapSource->allocPtr = allocateBlocks(heapSource, 1);
+    heapSource->allocLimit = heapSource->allocPtr + BLOCK_SIZE;
+
+    gcHeap = calloc(1, sizeof(*gcHeap));
+    assert(gcHeap != NULL);
+    gcHeap->heapSource = heapSource;
+
+    return gcHeap;
+}
+
+/*
+ * Perform any required heap initializations after forking from the
+ * zygote process.  This is a no-op for the time being.  Eventually
+ * this will demarcate the shared region of the heap.
+ */
+bool dvmHeapSourceStartupAfterZygote()
+{
+    return true;
+}
+
+bool dvmHeapSourceStartupBeforeFork()
+{
+    assert(!"implemented");
+    return false;
+}
+
+void dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+    if (*gcHeap == NULL || (*gcHeap)->heapSource == NULL)
+        return;
+    free((*gcHeap)->heapSource->blockQueue);
+    free((*gcHeap)->heapSource->blockSpace);
+    virtualFree((*gcHeap)->heapSource->blockBase,
+                (*gcHeap)->heapSource->maximumSize);
+    free((*gcHeap)->heapSource);
+    (*gcHeap)->heapSource = NULL;
+    free(*gcHeap);
+    *gcHeap = NULL;
+}
+
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec,
+                             size_t perHeapStats[],
+                             size_t arrayLen)
+{
+    HeapSource *heapSource;
+    size_t value;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    switch (spec) {
+    case HS_FOOTPRINT:
+        value = heapSource->maximumSize;
+        break;
+    case HS_ALLOWED_FOOTPRINT:
+        value = heapSource->maximumSize;
+        break;
+    case HS_BYTES_ALLOCATED:
+        value = heapSource->bytesAllocated;
+        break;
+    case HS_OBJECTS_ALLOCATED:
+        value = sumHeapBitmap(&heapSource->allocBits);
+        break;
+    default:
+        assert(!"implemented");
+        value = 0;
+    }
+    if (perHeapStats) {
+        *perHeapStats = value;
+    }
+    return value;
+}
+
+/*
+ * Performs a shallow copy of the allocation bitmap into the given
+ * vector of heap bitmaps.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[],
+                                   size_t numHeaps)
+{
+    assert(!"implemented");
+}
+
+HeapBitmap *dvmHeapSourceGetLiveBits()
+{
+    return &gDvm.gcHeap->heapSource->allocBits;
+}
+
+/*
+ * Allocate the specified number of bytes from the heap.  The
+ * allocation cursor points into a block of free storage.  If the
+ * given allocation fits in the remaining space of the block, we
+ * advance the cursor and return a pointer to the free storage.  If
+ * the allocation cannot fit in the current block but is smaller than
+ * a block we request a new block and allocate from it instead.  If
+ * the allocation is larger than a block we must allocate from a span
+ * of contiguous blocks.
+ */
+void *dvmHeapSourceAlloc(size_t length)
+{
+    HeapSource *heapSource;
+    unsigned char *addr;
+    size_t aligned, available, blocks;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    assert(heapSource != NULL);
+    assert(heapSource->allocPtr != NULL);
+    assert(heapSource->allocLimit != NULL);
+
+    aligned = alignUp(length, ALLOC_ALIGNMENT);
+    available = heapSource->allocLimit - heapSource->allocPtr;
+
+    /* Try allocating inside the current block. */
+    if (aligned <= available) {
+        addr = heapSource->allocPtr;
+        heapSource->allocPtr += aligned;
+        heapSource->bytesAllocated += aligned;
+        dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+        return addr;
+    }
+
+    /* Try allocating in a new block. */
+    if (aligned <= BLOCK_SIZE) {
+        addr =  allocateBlocks(heapSource, 1);
+        if (addr != NULL) {
+            heapSource->allocLimit = addr + BLOCK_SIZE;
+            heapSource->allocPtr = addr + aligned;
+            heapSource->bytesAllocated += aligned;
+            dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+            /* TODO(cshapiro): pad out the current block. */
+        }
+        return addr;
+    }
+
+    /* Try allocating in a span of blocks. */
+    blocks = alignUp(aligned, BLOCK_SIZE) / BLOCK_SIZE;
+
+    addr = allocateBlocks(heapSource, blocks);
+    /* Propagate failure upward. */
+    if (addr != NULL) {
+        heapSource->bytesAllocated += aligned;
+        dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+        /* TODO(cshapiro): pad out free space in the last block. */
+    }
+    return addr;
+}
+
+void *dvmHeapSourceAllocAndGrow(size_t size)
+{
+    return dvmHeapSourceAlloc(size);
+}
+
+/* TODO: refactor along with dvmHeapSourceAlloc */
+void *allocateGray(size_t size)
+{
+    HeapSource *heapSource;
+    void *addr;
+    size_t block;
+
+    /* TODO: add a check that we are in a GC. */
+    heapSource = gDvm.gcHeap->heapSource;
+    addr = dvmHeapSourceAlloc(size);
+    assert(addr != NULL);
+    block = addressToBlock(heapSource, (const u1 *)addr);
+    if (heapSource->queueHead == QUEUE_TAIL) {
+        /*
+         * Forcibly append the underlying block to the queue.  This
+         * condition occurs when referents are transported following
+         * the initial trace.
+         */
+        enqueueBlock(heapSource, block);
+        LOG_PROM("forced promoting block %zu %d @ %p", block, heapSource->blockSpace[block], addr);
+    }
+    return addr;
+}
+
+bool dvmHeapSourceContainsAddress(const void *ptr)
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    return dvmHeapBitmapCoversAddress(&heapSource->allocBits, ptr);
+}
+
+/*
+ * Returns true if the given address is within the heap and points to
+ * the header of a live object.
+ */
+bool dvmHeapSourceContains(const void *addr)
+{
+    HeapSource *heapSource;
+    HeapBitmap *bitmap;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    bitmap = &heapSource->allocBits;
+    if (!dvmHeapBitmapCoversAddress(bitmap, addr)) {
+        return false;
+    } else {
+        return dvmHeapBitmapIsObjectBitSet(bitmap, addr);
+    }
+}
+
+bool dvmHeapSourceGetPtrFlag(const void *ptr, HeapSourcePtrFlag flag)
+{
+    assert(!"implemented");
+    return false;
+}
+
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+    assert(!"implemented");
+    return 0;
+}
+
+size_t dvmHeapSourceFootprint()
+{
+    assert(!"implemented");
+    return 0;
+}
+
+/*
+ * Returns the "ideal footprint" which appears to be the number of
+ * bytes currently committed to the heap.  This starts out at the
+ * start size of the heap and grows toward the maximum size.
+ */
+size_t dvmHeapSourceGetIdealFootprint()
+{
+    return gDvm.gcHeap->heapSource->currentSize;
+}
+
+float dvmGetTargetHeapUtilization()
+{
+    return 0.5f;
+}
+
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+    assert(newTarget > 0.0f && newTarget < 1.0f);
+}
+
+/*
+ * Expands the size of the heap after a collection.  At present we
+ * commit the pages for maximum size of the heap so this routine is
+ * just a no-op.  Eventually, we will either allocate or commit pages
+ * on an as-need basis.
+ */
+void dvmHeapSourceGrowForUtilization()
+{
+    /* do nothing */
+}
+
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg)
+{
+    assert(!"implemented");
+}
+
+size_t dvmHeapSourceGetNumHeaps()
+{
+    return 1;
+}
+
+bool dvmTrackExternalAllocation(size_t n)
+{
+    /* do nothing */
+    return true;
+}
+
+void dvmTrackExternalFree(size_t n)
+{
+    /* do nothing */
+}
+
+size_t dvmGetExternalBytesAllocated()
+{
+    assert(!"implemented");
+    return 0;
+}
+
+void dvmHeapSourceFlip()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+
+    /* Reset the block queue. */
+    heapSource->allocBlocks = 0;
+    heapSource->queueSize = 0;
+    heapSource->queueHead = QUEUE_TAIL;
+
+    /* TODO(cshapiro): pad the current (prev) block. */
+
+    heapSource->allocPtr = NULL;
+    heapSource->allocLimit = NULL;
+
+    /* Whiten all allocated blocks. */
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if (heapSource->blockSpace[i] == BLOCK_TO_SPACE) {
+            heapSource->blockSpace[i] = BLOCK_FROM_SPACE;
+        }
+    }
+}
+
+static void room(size_t *alloc, size_t *avail, size_t *total)
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    *total = heapSource->totalBlocks*BLOCK_SIZE;
+    *alloc = heapSource->allocBlocks*BLOCK_SIZE;
+    *avail = *total - *alloc;
+}
+
+static bool isSpaceInternal(u1 *addr, int space)
+{
+    HeapSource *heapSource;
+    u1 *base, *limit;
+    size_t offset;
+    char space2;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    base = heapSource->blockBase;
+    assert(addr >= base);
+    limit = heapSource->blockBase + heapSource->maximumSize;
+    assert(addr < limit);
+    offset = addr - base;
+    space2 = heapSource->blockSpace[offset >> BLOCK_SHIFT];
+    return space == space2;
+}
+
+static bool fromSpaceContains(const void *addr)
+{
+    return isSpaceInternal((u1 *)addr, BLOCK_FROM_SPACE);
+}
+
+static bool toSpaceContains(const void *addr)
+{
+    return isSpaceInternal((u1 *)addr, BLOCK_TO_SPACE);
+}
+
+/*
+ * Notifies the collector that the object at the given address must
+ * remain stationary during the current collection.
+ */
+static void pinObject(const Object *obj)
+{
+    promoteBlockByAddr(gDvm.gcHeap->heapSource, obj);
+}
+
+static size_t sumHeapBitmap(const HeapBitmap *bitmap)
+{
+    size_t sum = 0;
+    for (size_t i = 0; i < bitmap->bitsLen >> 2; ++i) {
+        sum += CLZ(bitmap->bits[i]);
+    }
+    return sum;
+}
+
+/*
+ * Miscellaneous functionality.
+ */
+
+static int isForward(const void *addr)
+{
+    return (uintptr_t)addr & 0x1;
+}
+
+static void setForward(const void *toObj, void *fromObj)
+{
+    *(unsigned long *)fromObj = (uintptr_t)toObj | 0x1;
+}
+
+static void* getForward(const void *fromObj)
+{
+    return (void *)((uintptr_t)fromObj & ~0x1);
+}
+
+/* Beware, uses the same encoding as a forwarding pointers! */
+static int isPermanentString(const StringObject *obj) {
+    return (uintptr_t)obj & 0x1;
+}
+
+static void* getPermanentString(const StringObject *obj)
+{
+    return (void *)((uintptr_t)obj & ~0x1);
+}
+
+
+/*
+ * Scavenging and transporting routines follow.  A transporter grays
+ * an object.  A scavenger blackens an object.  We define these
+ * routines for each fundamental object type.  Dispatch is performed
+ * in scavengeObject.
+ */
+
+/*
+ * Class object scavenging.
+ */
+static void scavengeClassObject(ClassObject *obj)
+{
+    LOG_SCAV("scavengeClassObject(obj=%p)", obj);
+    assert(obj != NULL);
+    assert(obj->obj.clazz != NULL);
+    assert(obj->obj.clazz->descriptor != NULL);
+    assert(!strcmp(obj->obj.clazz->descriptor, "Ljava/lang/Class;"));
+    assert(obj->descriptor != NULL);
+    LOG_SCAV("scavengeClassObject: descriptor='%s',vtableCount=%zu",
+             obj->descriptor, obj->vtableCount);
+    /* Delegate class object and instance field scavenging. */
+    scavengeDataObject((Object *)obj);
+    /* Scavenge the array element class object. */
+    if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+        scavengeReference((Object **)(void *)&obj->elementClass);
+    }
+    /* Scavenge the superclass. */
+    scavengeReference((Object **)(void *)&obj->super);
+    /* Scavenge the class loader. */
+    scavengeReference(&obj->classLoader);
+    /* Scavenge static fields. */
+    for (int i = 0; i < obj->sfieldCount; ++i) {
+        char ch = obj->sfields[i].field.signature[0];
+        if (ch == '[' || ch == 'L') {
+            scavengeReference((Object **)(void *)&obj->sfields[i].value.l);
+        }
+    }
+    /* Scavenge interface class objects. */
+    for (int i = 0; i < obj->interfaceCount; ++i) {
+        scavengeReference((Object **) &obj->interfaces[i]);
+    }
+}
+
+/*
+ * Array object scavenging.
+ */
+static size_t scavengeArrayObject(ArrayObject *array)
+{
+    LOG_SCAV("scavengeArrayObject(array=%p)", array);
+    /* Scavenge the class object. */
+    assert(toSpaceContains(array));
+    assert(array != NULL);
+    assert(array->obj.clazz != NULL);
+    scavengeReference((Object **) array);
+    size_t length = dvmArrayObjectSize(array);
+    /* Scavenge the array contents. */
+    if (IS_CLASS_FLAG_SET(array->obj.clazz, CLASS_ISOBJECTARRAY)) {
+        Object **contents = (Object **)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            scavengeReference(&contents[i]);
+        }
+    }
+    return length;
+}
+
+/*
+ * Reference object scavenging.
+ */
+
+static int getReferenceFlags(const Object *obj)
+{
+    int flags;
+
+    flags = CLASS_ISREFERENCE |
+            CLASS_ISWEAKREFERENCE |
+            CLASS_ISPHANTOMREFERENCE;
+    return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+static int isSoftReference(const Object *obj)
+{
+    return getReferenceFlags(obj) == CLASS_ISREFERENCE;
+}
+
+static int isWeakReference(const Object *obj)
+{
+    return getReferenceFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+#ifndef NDEBUG
+static bool isPhantomReference(const Object *obj)
+{
+    return getReferenceFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+#endif
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * but has not yet been appended to it.
+ */
+static bool isReferenceEnqueuable(const Object *ref)
+{
+    Object *queue, *queueNext;
+
+    queue = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue);
+    queueNext = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext);
+    if (queue == NULL || queueNext != NULL) {
+        /*
+         * There is no queue, or the reference has already
+         * been enqueued.  The Reference.enqueue() method
+         * will do nothing even if we call it.
+         */
+        return false;
+    }
+
+    /*
+     * We need to call enqueue(), but if we called it from
+     * here we'd probably deadlock.  Schedule a call.
+     */
+    return true;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+    assert(ref != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
+        ALOGE("no room for any more reference operations");
+        dvmAbort();
+    }
+}
+
+/*
+ * Sets the referent field of a reference object to NULL.
+ */
+static void clearReference(Object *obj)
+{
+    dvmSetFieldObject(obj, gDvm.offJavaLangRefReference_referent, NULL);
+}
+
+/*
+ * Clears reference objects with white referents.
+ */
+void clearWhiteReferences(Object **list)
+{
+    size_t referentOffset, queueNextOffset;
+    bool doSignal;
+
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    doSignal = false;
+    while (*list != NULL) {
+        Object *ref = *list;
+        JValue *field = dvmFieldPtr(ref, referentOffset);
+        Object *referent = field->l;
+        *list = dvmGetFieldObject(ref, queueNextOffset);
+        dvmSetFieldObject(ref, queueNextOffset, NULL);
+        assert(referent != NULL);
+        if (isForward(referent->clazz)) {
+            field->l = referent = getForward(referent->clazz);
+            continue;
+        }
+        if (fromSpaceContains(referent)) {
+            /* Referent is white, clear it. */
+            clearReference(ref);
+            if (isReferenceEnqueuable(ref)) {
+                enqueueReference(ref);
+                doSignal = true;
+            }
+        }
+    }
+    /*
+     * If we cleared a reference with a reference queue we must notify
+     * the heap worker to append the reference.
+     */
+    if (doSignal) {
+        dvmSignalHeapWorker(false);
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * Blackens referents subject to the soft reference preservation
+ * policy.
+ */
+void preserveSoftReferences(Object **list)
+{
+    Object *ref;
+    Object *prev, *next;
+    size_t referentOffset, queueNextOffset;
+    unsigned counter;
+    bool white;
+
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    counter = 0;
+    prev = next = NULL;
+    ref = *list;
+    while (ref != NULL) {
+        JValue *field = dvmFieldPtr(ref, referentOffset);
+        Object *referent = field->l;
+        next = dvmGetFieldObject(ref, queueNextOffset);
+        assert(referent != NULL);
+        if (isForward(referent->clazz)) {
+            /* Referent is black. */
+            field->l = referent = getForward(referent->clazz);
+            white = false;
+        } else {
+            white = fromSpaceContains(referent);
+        }
+        if (!white && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, gray it. */
+            scavengeReference((Object **)(void *)&field->l);
+            white = true;
+        }
+        if (white) {
+            /* Referent is black, unlink it. */
+            if (prev != NULL) {
+                dvmSetFieldObject(ref, queueNextOffset, NULL);
+                dvmSetFieldObject(prev, queueNextOffset, next);
+            }
+        } else {
+            /* Referent is white, skip over it. */
+            prev = ref;
+        }
+        ref = next;
+    }
+    /*
+     * Restart the trace with the newly gray references added to the
+     * root set.
+     */
+    scavengeBlockQueue();
+}
+
+void processFinalizableReferences()
+{
+    HeapRefTable newPendingRefs;
+    LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
+    Object **ref;
+    Object **lastRef;
+    size_t totalPendCount;
+
+    /*
+     * All strongly, reachable objects are black.
+     * Any white finalizable objects need to be finalized.
+     */
+
+    /* Create a table that the new pending refs will
+     * be added to.
+     */
+    if (!dvmHeapInitHeapRefTable(&newPendingRefs)) {
+        //TODO: mark all finalizable refs and hope that
+        //      we can schedule them next time.  Watch out,
+        //      because we may be expecting to free up space
+        //      by calling finalizers.
+        LOG_REF("no room for pending finalizations");
+        dvmAbort();
+    }
+
+    /*
+     * Walk through finalizableRefs and move any white references to
+     * the list of new pending refs.
+     */
+    totalPendCount = 0;
+    while (finRefs != NULL) {
+        Object **gapRef;
+        size_t newPendCount = 0;
+
+        gapRef = ref = finRefs->refs.table;
+        lastRef = finRefs->refs.nextEntry;
+        while (ref < lastRef) {
+            if (fromSpaceContains(*ref)) {
+                if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+                    //TODO: add the current table and allocate
+                    //      a new, smaller one.
+                    LOG_REF("no room for any more pending finalizations: %zd",
+                            dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+                    dvmAbort();
+                }
+                newPendCount++;
+            } else {
+                /* This ref is black, so will remain on finalizableRefs.
+                 */
+                if (newPendCount > 0) {
+                    /* Copy it up to fill the holes.
+                     */
+                    *gapRef++ = *ref;
+                } else {
+                    /* No holes yet; don't bother copying.
+                     */
+                    gapRef++;
+                }
+            }
+            ref++;
+        }
+        finRefs->refs.nextEntry = gapRef;
+        //TODO: if the table is empty when we're done, free it.
+        totalPendCount += newPendCount;
+        finRefs = finRefs->next;
+    }
+    LOG_REF("%zd finalizers triggered.", totalPendCount);
+    if (totalPendCount == 0) {
+        /* No objects required finalization.
+         * Free the empty temporary table.
+         */
+        dvmClearReferenceTable(&newPendingRefs);
+        return;
+    }
+
+    /* Add the new pending refs to the main list.
+     */
+    if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
+                &newPendingRefs))
+    {
+        LOG_REF("can't insert new pending finalizations");
+        dvmAbort();
+    }
+
+    //TODO: try compacting the main list with a memcpy loop
+
+    /* Blacken the refs we just moved; we don't want them or their
+     * children to get swept yet.
+     */
+    ref = newPendingRefs.table;
+    lastRef = newPendingRefs.nextEntry;
+    assert(ref < lastRef);
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+    while (ref < lastRef) {
+        scavengeReference(ref);
+        ref++;
+    }
+    HPROF_CLEAR_GC_SCAN_STATE();
+    scavengeBlockQueue();
+    dvmSignalHeapWorker(false);
+}
+
+/*
+ * If a reference points to from-space and has been forwarded, we snap
+ * the pointer to its new to-space address.  If the reference points
+ * to an unforwarded from-space address we must enqueue the reference
+ * for later processing.  TODO: implement proper reference processing
+ * and move the referent scavenging elsewhere.
+ */
+static void scavengeReferenceObject(Object *obj)
+{
+    Object *referent;
+    Object **queue;
+    size_t referentOffset, queueNextOffset;
+
+    assert(obj != NULL);
+    LOG_SCAV("scavengeReferenceObject(obj=%p),'%s'", obj, obj->clazz->descriptor);
+    scavengeDataObject(obj);
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    referent = dvmGetFieldObject(obj, referentOffset);
+    if (referent == NULL || toSpaceContains(referent)) {
+        return;
+    }
+    if (isSoftReference(obj)) {
+        queue = &gDvm.gcHeap->softReferences;
+    } else if (isWeakReference(obj)) {
+        queue = &gDvm.gcHeap->weakReferences;
+    } else {
+        assert(isPhantomReference(obj));
+        queue = &gDvm.gcHeap->phantomReferences;
+    }
+    queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+    dvmSetFieldObject(obj, queueNextOffset, *queue);
+    *queue = obj;
+    LOG_SCAV("scavengeReferenceObject: enqueueing %p", obj);
+}
+
+/*
+ * Data object scavenging.
+ */
+static void scavengeDataObject(Object *obj)
+{
+    // LOG_SCAV("scavengeDataObject(obj=%p)", obj);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(obj->clazz->objectSize != 0);
+    assert(toSpaceContains(obj));
+    /* Scavenge the class object. */
+    ClassObject *clazz = obj->clazz;
+    scavengeReference((Object **) obj);
+    /* Scavenge instance fields. */
+    if (clazz->refOffsets != CLASS_WALK_SUPER) {
+        size_t refOffsets = clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object **ref = (Object **)((u1 *)obj + offset);
+            scavengeReference(ref);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (; clazz != NULL; clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                size_t offset = field->byteOffset;
+                Object **ref = (Object **)((u1 *)obj + offset);
+                scavengeReference(ref);
+            }
+        }
+    }
+}
+
+static Object *transportObject(const Object *fromObj)
+{
+    Object *toObj;
+    size_t allocSize, copySize;
+
+    LOG_TRAN("transportObject(fromObj=%p) allocBlocks=%zu",
+                  fromObj,
+                  gDvm.gcHeap->heapSource->allocBlocks);
+    assert(fromObj != NULL);
+    assert(fromSpaceContains(fromObj));
+    allocSize = copySize = objectSize(fromObj);
+    if (LW_HASH_STATE(fromObj->lock) != LW_HASH_STATE_UNHASHED) {
+        /*
+         * The object has been hashed or hashed and moved.  We must
+         * reserve an additional word for a hash code.
+         */
+        allocSize += sizeof(u4);
+    }
+    if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+        /*
+         * The object has its hash code allocated.  Ensure the hash
+         * code is copied along with the instance data.
+         */
+        copySize += sizeof(u4);
+    }
+    /* TODO(cshapiro): don't copy, re-map large data objects. */
+    assert(copySize <= allocSize);
+    toObj = allocateGray(allocSize);
+    assert(toObj != NULL);
+    assert(toSpaceContains(toObj));
+    memcpy(toObj, fromObj, copySize);
+    if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED) {
+        /*
+         * The object has had its hash code exposed.  Append it to the
+         * instance and set a bit so we know to look for it there.
+         */
+        *(u4 *)(((char *)toObj) + copySize) = (u4)fromObj >> 3;
+        toObj->lock |= LW_HASH_STATE_HASHED_AND_MOVED << LW_HASH_STATE_SHIFT;
+    }
+    LOG_TRAN("transportObject: from %p/%zu to %p/%zu (%zu,%zu) %s",
+             fromObj, addressToBlock(gDvm.gcHeap->heapSource,fromObj),
+             toObj, addressToBlock(gDvm.gcHeap->heapSource,toObj),
+             copySize, allocSize, copySize < allocSize ? "DIFFERENT" : "");
+    return toObj;
+}
+
+/*
+ * Generic reference scavenging.
+ */
+
+/*
+ * Given a reference to an object, the scavenge routine will gray the
+ * reference.  Any objects pointed to by the scavenger object will be
+ * transported to new space and a forwarding pointer will be installed
+ * in the header of the object.
+ */
+
+/*
+ * Blacken the given pointer.  If the pointer is in from space, it is
+ * transported to new space.  If the object has a forwarding pointer
+ * installed it has already been transported and the referent is
+ * snapped to the new address.
+ */
+static void scavengeReference(Object **obj)
+{
+    ClassObject *clazz;
+    Object *fromObj, *toObj;
+
+    assert(obj);
+
+    if (*obj == NULL) return;
+
+    assert(dvmIsValidObject(*obj));
+
+    /* The entire block is black. */
+    if (toSpaceContains(*obj)) {
+        LOG_SCAV("scavengeReference skipping pinned object @ %p", *obj);
+        return;
+    }
+    LOG_SCAV("scavengeReference(*obj=%p)", *obj);
+
+    assert(fromSpaceContains(*obj));
+
+    clazz = (*obj)->clazz;
+
+    if (isForward(clazz)) {
+        // LOG_SCAV("forwarding %p @ %p to %p", *obj, obj, (void *)((uintptr_t)clazz & ~0x1));
+        *obj = (Object *)getForward(clazz);
+        return;
+    }
+    fromObj = *obj;
+    if (clazz == NULL) {
+        // LOG_SCAV("scavangeReference %p has a NULL class object", fromObj);
+        assert(!"implemented");
+        toObj = NULL;
+    } else {
+        toObj = transportObject(fromObj);
+    }
+    setForward(toObj, fromObj);
+    *obj = (Object *)toObj;
+}
+
+/*
+ * Generic object scavenging.
+ */
+static void scavengeObject(Object *obj)
+{
+    ClassObject *clazz;
+
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(!((uintptr_t)obj->clazz & 0x1));
+    clazz = obj->clazz;
+    if (dvmIsTheClassClass(clazz)) {
+        scavengeClassObject((ClassObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+        scavengeArrayObject((ArrayObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
+        scavengeReferenceObject(obj);
+    } else {
+        scavengeDataObject(obj);
+    }
+}
+
+/*
+ * External root scavenging routines.
+ */
+
+static void pinHashTableEntries(HashTable *table)
+{
+    LOG_PIN(">>> pinHashTableEntries(table=%p)", table);
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        void *obj = entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        }
+        pinObject(entry->data);
+    }
+    dvmHashTableUnlock(table);
+    LOG_PIN("<<< pinHashTableEntries(table=%p)", table);
+}
+
+static void pinPrimitiveClasses()
+{
+    size_t length = ARRAYSIZE(gDvm.primitiveClass);
+    for (size_t i = 0; i < length; i++) {
+        if (gDvm.primitiveClass[i] != NULL) {
+            pinObject((Object *)gDvm.primitiveClass[i]);
+        }
+    }
+}
+
+/*
+ * Scavenge interned strings.  Permanent interned strings will have
+ * been pinned and are therefore ignored.  Non-permanent strings that
+ * have been forwarded are snapped.  All other entries are removed.
+ */
+static void scavengeInternedStrings()
+{
+    HashTable *table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        Object *obj = (Object *)entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        } else if (!isPermanentString((StringObject *)obj)) {
+            // LOG_SCAV("entry->data=%p", entry->data);
+            LOG_SCAV(">>> string obj=%p", entry->data);
+            /* TODO(cshapiro): detach white string objects */
+            scavengeReference((Object **)(void *)&entry->data);
+            LOG_SCAV("<<< string obj=%p", entry->data);
+        }
+    }
+    dvmHashTableUnlock(table);
+}
+
+static void pinInternedStrings()
+{
+    HashTable *table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        Object *obj = (Object *)entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        } else if (isPermanentString((StringObject *)obj)) {
+            obj = (Object *)getPermanentString((StringObject*)obj);
+            LOG_PROM(">>> pin string obj=%p", obj);
+            pinObject(obj);
+            LOG_PROM("<<< pin string obj=%p", obj);
+        }
+     }
+    dvmHashTableUnlock(table);
+}
+
+/*
+ * At present, reference tables contain references that must not be
+ * moved by the collector.  Instead of scavenging each reference in
+ * the table we pin each referenced object.
+ */
+static void pinReferenceTable(const ReferenceTable *table)
+{
+    assert(table != NULL);
+    assert(table->table != NULL);
+    assert(table->nextEntry != NULL);
+    for (Object **entry = table->table; entry < table->nextEntry; ++entry) {
+        assert(entry != NULL);
+        assert(!isForward(*entry));
+        pinObject(*entry);
+    }
+}
+
+static void scavengeLargeHeapRefTable(LargeHeapRefTable *table)
+{
+    for (; table != NULL; table = table->next) {
+        Object **ref = table->refs.table;
+        for (; ref < table->refs.nextEntry; ++ref) {
+            scavengeReference(ref);
+        }
+    }
+}
+
+/* This code was copied from Thread.c */
+static void scavengeThreadStack(Thread *thread)
+{
+    const u4 *framePtr;
+#if WITH_EXTRA_GC_CHECKS > 1
+    bool first = true;
+#endif
+
+    framePtr = (const u4 *)thread->interpSave.curFrame;
+    while (framePtr != NULL) {
+        const StackSaveArea *saveArea;
+        const Method *method;
+
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = saveArea->method;
+        if (method != NULL && !dvmIsNativeMethod(method)) {
+#ifdef COUNT_PRECISE_METHODS
+            /* the GC is running, so no lock required */
+            if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+                LOG_SCAV("PGC: added %s.%s %p",
+                             method->clazz->descriptor, method->name, method);
+#endif
+#if WITH_EXTRA_GC_CHECKS > 1
+            /*
+             * May also want to enable the memset() in the "invokeMethod"
+             * goto target in the portable interpreter.  That sets the stack
+             * to a pattern that makes referring to uninitialized data
+             * very obvious.
+             */
+
+            if (first) {
+                /*
+                 * First frame, isn't native, check the "alternate" saved PC
+                 * as a sanity check.
+                 *
+                 * It seems like we could check the second frame if the first
+                 * is native, since the PCs should be the same.  It turns out
+                 * this doesn't always work.  The problem is that we could
+                 * have calls in the sequence:
+                 *   interp method #2
+                 *   native method
+                 *   interp method #1
+                 *
+                 * and then GC while in the native method after returning
+                 * from interp method #2.  The currentPc on the stack is
+                 * for interp method #1, but thread->currentPc2 is still
+                 * set for the last thing interp method #2 did.
+                 *
+                 * This can also happen in normal execution:
+                 * - sget-object on not-yet-loaded class
+                 * - class init updates currentPc2
+                 * - static field init is handled by parsing annotations;
+                 *   static String init requires creation of a String object,
+                 *   which can cause a GC
+                 *
+                 * Essentially, any pattern that involves executing
+                 * interpreted code and then causes an allocation without
+                 * executing instructions in the original method will hit
+                 * this.  These are rare enough that the test still has
+                 * some value.
+                 */
+                if (saveArea->xtra.currentPc != thread->currentPc2) {
+                    ALOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p",
+                        saveArea->xtra.currentPc, thread->currentPc2,
+                        method->clazz->descriptor, method->name, method->insns);
+                    if (saveArea->xtra.currentPc != NULL)
+                        ALOGE("  pc inst = 0x%04x", *saveArea->xtra.currentPc);
+                    if (thread->currentPc2 != NULL)
+                        ALOGE("  pc2 inst = 0x%04x", *thread->currentPc2);
+                    dvmDumpThread(thread, false);
+                }
+            } else {
+                /*
+                 * It's unusual, but not impossible, for a non-first frame
+                 * to be at something other than a method invocation.  For
+                 * example, if we do a new-instance on a nonexistent class,
+                 * we'll have a lot of class loader activity on the stack
+                 * above the frame with the "new" operation.  Could also
+                 * happen while we initialize a Throwable when an instruction
+                 * fails.
+                 *
+                 * So there's not much we can do here to verify the PC,
+                 * except to verify that it's a GC point.
+                 */
+            }
+            assert(saveArea->xtra.currentPc != NULL);
+#endif
+
+            const RegisterMap* pMap;
+            const u1* regVector;
+
+            Method* nonConstMethod = (Method*) method;  // quiet gcc
+            pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+
+            //LOG_SCAV("PGC: %s.%s", method->clazz->descriptor, method->name);
+
+            if (pMap != NULL) {
+                /* found map, get registers for this address */
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+                /*
+                if (regVector == NULL) {
+                    LOG_SCAV("PGC: map but no entry for %s.%s addr=0x%04x",
+                                 method->clazz->descriptor, method->name, addr);
+                } else {
+                    LOG_SCAV("PGC: found map for %s.%s 0x%04x (t=%d)",
+                                 method->clazz->descriptor, method->name, addr,
+                                 thread->threadId);
+                }
+                */
+            } else {
+                /*
+                 * No map found.  If precise GC is disabled this is
+                 * expected -- we don't create pointers to the map data even
+                 * if it's present -- but if it's enabled it means we're
+                 * unexpectedly falling back on a conservative scan, so it's
+                 * worth yelling a little.
+                 */
+                if (gDvm.preciseGc) {
+                    LOG_SCAV("PGC: no map for %s.%s", method->clazz->descriptor, method->name);
+                }
+                regVector = NULL;
+            }
+            if (regVector == NULL) {
+                /*
+                 * There are no roots to scavenge.  Skip over the entire frame.
+                 */
+                framePtr += method->registersSize;
+            } else {
+                /*
+                 * Precise scan.  v0 is at the lowest address on the
+                 * interpreted stack, and is the first bit in the register
+                 * vector, so we can walk through the register map and
+                 * memory in the same direction.
+                 *
+                 * A '1' bit indicates a live reference.
+                 */
+                u2 bits = 1 << 1;
+                for (int i = method->registersSize - 1; i >= 0; i--) {
+                    u4 rval = *framePtr;
+
+                    bits >>= 1;
+                    if (bits == 1) {
+                        /* set bit 9 so we can tell when we're empty */
+                        bits = *regVector++ | 0x0100;
+                    }
+
+                    if (rval != 0 && (bits & 0x01) != 0) {
+                        /*
+                         * Non-null, register marked as live reference.  This
+                         * should always be a valid object.
+                         */
+#if WITH_EXTRA_GC_CHECKS > 0
+                        if ((rval & 0x3) != 0 || !dvmIsValidObject((Object*) rval)) {
+                            /* this is very bad */
+                            ALOGE("PGC: invalid ref in reg %d: 0x%08x",
+                                method->registersSize-1 - i, rval);
+                        } else
+#endif
+                        {
+
+                            // LOG_SCAV("stack reference %u@%p", *framePtr, framePtr);
+                            /* dvmMarkObjectNonNull((Object *)rval); */
+                            scavengeReference((Object **) framePtr);
+                        }
+                    } else {
+                        /*
+                         * Null or non-reference, do nothing at all.
+                         */
+#if WITH_EXTRA_GC_CHECKS > 1
+                        if (dvmIsValidObject((Object*) rval)) {
+                            /* this is normal, but we feel chatty */
+                            ALOGD("PGC: ignoring valid ref in reg %d: 0x%08x",
+                                 method->registersSize-1 - i, rval);
+                        }
+#endif
+                    }
+                    ++framePtr;
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+        /* else this is a break frame and there is nothing to gray, or
+         * this is a native method and the registers are just the "ins",
+         * copied from various registers in the caller's set.
+         */
+
+#if WITH_EXTRA_GC_CHECKS > 1
+        first = false;
+#endif
+
+        /* Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+               saveArea->prevFrame == NULL);
+        framePtr = saveArea->prevFrame;
+    }
+}
+
+static void scavengeThread(Thread *thread)
+{
+    // LOG_SCAV("scavengeThread(thread=%p)", thread);
+
+    // LOG_SCAV("Scavenging threadObj=%p", thread->threadObj);
+    scavengeReference(&thread->threadObj);
+
+    // LOG_SCAV("Scavenging exception=%p", thread->exception);
+    scavengeReference(&thread->exception);
+
+    scavengeThreadStack(thread);
+}
+
+static void scavengeThreadList()
+{
+    Thread *thread;
+
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        scavengeThread(thread);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+static void pinThreadStack(const Thread *thread)
+{
+    const u4 *framePtr;
+    const StackSaveArea *saveArea;
+    Method *method;
+    const char *shorty;
+    Object *obj;
+
+    saveArea = NULL;
+    framePtr = (const u4 *)thread->interpSave.curFrame;
+    for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = (Method *)saveArea->method;
+        if (method != NULL && dvmIsNativeMethod(method)) {
+            /*
+             * This is native method, pin its arguments.
+             *
+             * For purposes of graying references, we don't need to do
+             * anything here, because all of the native "ins" were copied
+             * from registers in the caller's stack frame and won't be
+             * changed (an interpreted method can freely use registers
+             * with parameters like any other register, but natives don't
+             * work that way).
+             *
+             * However, we need to ensure that references visible to
+             * native methods don't move around.  We can do a precise scan
+             * of the arguments by examining the method signature.
+             */
+            LOG_PIN("+++ native scan %s.%s",
+                    method->clazz->descriptor, method->name);
+            assert(method->registersSize == method->insSize);
+            if (!dvmIsStaticMethod(method)) {
+                /* grab the "this" pointer */
+                obj = (Object *)*framePtr++;
+                if (obj == NULL) {
+                    /*
+                     * This can happen for the "fake" entry frame inserted
+                     * for threads created outside the VM.  There's no actual
+                     * call so there's no object.  If we changed the fake
+                     * entry method to be declared "static" then this
+                     * situation should never occur.
+                     */
+                } else {
+                    assert(dvmIsValidObject(obj));
+                    pinObject(obj);
+                }
+            }
+            shorty = method->shorty+1;      // skip return value
+            for (int i = method->registersSize - 1; i >= 0; i--, framePtr++) {
+                switch (*shorty++) {
+                case 'L':
+                    obj = (Object *)*framePtr;
+                    if (obj != NULL) {
+                        assert(dvmIsValidObject(obj));
+                        pinObject(obj);
+                    }
+                    break;
+                case 'D':
+                case 'J':
+                    framePtr++;
+                    break;
+                default:
+                    /* 32-bit non-reference value */
+                    obj = (Object *)*framePtr;          // debug, remove
+                    if (dvmIsValidObject(obj)) {        // debug, remove
+                        /* if we see a lot of these, our scan might be off */
+                        LOG_PIN("+++ did NOT pin obj %p", obj);
+                    }
+                    break;
+                }
+            }
+        } else if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+
+            ALOGI("conservative : %s.%s", method->clazz->descriptor, method->name);
+
+            if (pMap != NULL) {
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+            }
+            if (regVector == NULL) {
+                /*
+                 * No register info for this frame, conservatively pin.
+                 */
+                for (int i = 0; i < method->registersSize; ++i) {
+                    u4 regValue = framePtr[i];
+                    if (regValue != 0 && (regValue & 0x3) == 0 && dvmIsValidObject((Object *)regValue)) {
+                        pinObject((Object *)regValue);
+                    }
+                }
+            }
+        }
+        /*
+         * Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+               saveArea->prevFrame == NULL);
+    }
+}
+
+static void pinThread(const Thread *thread)
+{
+    assert(thread != NULL);
+    LOG_PIN("pinThread(thread=%p)", thread);
+
+    LOG_PIN("Pin native method arguments");
+    pinThreadStack(thread);
+
+    LOG_PIN("Pin internalLocalRefTable");
+    pinReferenceTable(&thread->internalLocalRefTable);
+
+    LOG_PIN("Pin jniLocalRefTable");
+    pinReferenceTable(&thread->jniLocalRefTable);
+
+    /* Can the check be pushed into the promote routine? */
+    if (thread->jniMonitorRefTable.table) {
+        LOG_PIN("Pin jniMonitorRefTable");
+        pinReferenceTable(&thread->jniMonitorRefTable);
+    }
+}
+
+static void pinThreadList()
+{
+    Thread *thread;
+
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        pinThread(thread);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Heap block scavenging.
+ */
+
+/*
+ * Scavenge objects in the current block.  Scavenging terminates when
+ * the pointer reaches the highest address in the block or when a run
+ * of zero words that continues to the highest address is reached.
+ */
+static void scavengeBlock(HeapSource *heapSource, size_t block)
+{
+    u1 *cursor;
+    u1 *end;
+    size_t size;
+
+    LOG_SCAV("scavengeBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+    cursor = blockToAddress(heapSource, block);
+    end = cursor + BLOCK_SIZE;
+    LOG_SCAV("scavengeBlock start=%p, end=%p", cursor, end);
+
+    /* Parse and scavenge the current block. */
+    size = 0;
+    while (cursor < end) {
+        u4 word = *(u4 *)cursor;
+        if (word != 0) {
+            scavengeObject((Object *)cursor);
+            size = objectSize((Object *)cursor);
+            size = alignUp(size, ALLOC_ALIGNMENT);
+            cursor += size;
+        } else {
+            /* Check for padding. */
+            while (*(u4 *)cursor == 0) {
+                cursor += 4;
+                if (cursor == end) break;
+            }
+            /* Punt if something went wrong. */
+            assert(cursor == end);
+        }
+    }
+}
+
+static size_t objectSize(const Object *obj)
+{
+    size_t size;
+
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        size = dvmClassObjectSize((ClassObject *)obj);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        size = dvmArrayObjectSize((ArrayObject *)obj);
+    } else {
+        assert(obj->clazz->objectSize != 0);
+        size = obj->clazz->objectSize;
+    }
+    if (LW_HASH_STATE(obj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+        size += sizeof(u4);
+    }
+    return size;
+}
+
+static void verifyBlock(HeapSource *heapSource, size_t block)
+{
+    u1 *cursor;
+    u1 *end;
+    size_t size;
+
+    // LOG_VER("verifyBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+    cursor = blockToAddress(heapSource, block);
+    end = cursor + BLOCK_SIZE;
+    // LOG_VER("verifyBlock start=%p, end=%p", cursor, end);
+
+    /* Parse and scavenge the current block. */
+    size = 0;
+    while (cursor < end) {
+        u4 word = *(u4 *)cursor;
+        if (word != 0) {
+            dvmVerifyObject((Object *)cursor);
+            size = objectSize((Object *)cursor);
+            size = alignUp(size, ALLOC_ALIGNMENT);
+            cursor += size;
+        } else {
+            /* Check for padding. */
+            while (*(unsigned long *)cursor == 0) {
+                cursor += 4;
+                if (cursor == end) break;
+            }
+            /* Punt if something went wrong. */
+            assert(cursor == end);
+        }
+    }
+}
+
+static void describeBlockQueue(const HeapSource *heapSource)
+{
+    size_t block, count;
+    char space;
+
+    block = heapSource->queueHead;
+    count = 0;
+    LOG_SCAV(">>> describeBlockQueue(heapSource=%p)", heapSource);
+    /* Count the number of blocks enqueued. */
+    while (block != QUEUE_TAIL) {
+        block = heapSource->blockQueue[block];
+        ++count;
+    }
+    LOG_SCAV("blockQueue %zu elements, enqueued %zu",
+                 count, heapSource->queueSize);
+    block = heapSource->queueHead;
+    while (block != QUEUE_TAIL) {
+        space = heapSource->blockSpace[block];
+        LOG_SCAV("block=%zu@%p,space=%zu", block, blockToAddress(heapSource,block), space);
+        block = heapSource->blockQueue[block];
+    }
+
+    LOG_SCAV("<<< describeBlockQueue(heapSource=%p)", heapSource);
+}
+
+/*
+ * Blackens promoted objects.
+ */
+static void scavengeBlockQueue()
+{
+    HeapSource *heapSource;
+    size_t block;
+
+    LOG_SCAV(">>> scavengeBlockQueue()");
+    heapSource = gDvm.gcHeap->heapSource;
+    describeBlockQueue(heapSource);
+    while (heapSource->queueHead != QUEUE_TAIL) {
+        block = heapSource->queueHead;
+        LOG_SCAV("Dequeueing block %zu", block);
+        scavengeBlock(heapSource, block);
+        heapSource->queueHead = heapSource->blockQueue[block];
+        LOG_SCAV("New queue head is %zu", heapSource->queueHead);
+    }
+    LOG_SCAV("<<< scavengeBlockQueue()");
+}
+
+/*
+ * Scan the block list and verify all blocks that are marked as being
+ * in new space.  This should be parametrized so we can invoke this
+ * routine outside of the context of a collection.
+ */
+static void verifyNewSpace()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    size_t c0 = 0, c1 = 0, c2 = 0, c7 = 0;
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        switch (heapSource->blockSpace[i]) {
+        case BLOCK_FREE: ++c0; break;
+        case BLOCK_TO_SPACE: ++c1; break;
+        case BLOCK_FROM_SPACE: ++c2; break;
+        case BLOCK_CONTINUED: ++c7; break;
+        default: assert(!"reached");
+        }
+    }
+    LOG_VER("Block Demographics: "
+            "Free=%zu,ToSpace=%zu,FromSpace=%zu,Continued=%zu",
+            c0, c1, c2, c7);
+    for (size_t i = 0; i < heapSource->totalBlocks; ++i) {
+        if (heapSource->blockSpace[i] != BLOCK_TO_SPACE) {
+            continue;
+        }
+        verifyBlock(heapSource, i);
+    }
+}
+
+void describeHeap()
+{
+    HeapSource *heapSource = gDvm.gcHeap->heapSource;
+    describeBlocks(heapSource);
+}
+
+/*
+ * The collection interface.  Collection has a few distinct phases.
+ * The first is flipping AKA condemning AKA whitening the heap.  The
+ * second is to promote all objects which are pointed to by pinned or
+ * ambiguous references.  The third phase is tracing from the stacks,
+ * registers and various globals.  Lastly, a verification of the heap
+ * is performed.  The last phase should be optional.
+ */
+void dvmScavengeRoots()  /* Needs a new name badly */
+{
+    GcHeap *gcHeap;
+
+    {
+        size_t alloc, unused, total;
+
+        room(&alloc, &unused, &total);
+        LOG_SCAV("BEFORE GC: %zu alloc, %zu free, %zu total.",
+                     alloc, unused, total);
+    }
+
+    gcHeap = gDvm.gcHeap;
+    dvmHeapSourceFlip();
+
+    /*
+     * Promote blocks with stationary objects.
+     */
+    pinThreadList();
+    pinReferenceTable(&gDvm.jniGlobalRefTable);
+    pinReferenceTable(&gDvm.jniPinRefTable);
+    pinHashTableEntries(gDvm.loadedClasses);
+    pinHashTableEntries(gDvm.dbgRegistry);
+    pinPrimitiveClasses();
+    pinInternedStrings();
+
+    // describeBlocks(gcHeap->heapSource);
+
+    /*
+     * Create first, open new-space page right here.
+     */
+
+    /* Reset allocation to an unallocated block. */
+    gDvm.gcHeap->heapSource->allocPtr = allocateBlocks(gDvm.gcHeap->heapSource, 1);
+    gDvm.gcHeap->heapSource->allocLimit = gDvm.gcHeap->heapSource->allocPtr + BLOCK_SIZE;
+    /*
+     * Hack: promote the empty block allocated above.  If the
+     * promotions that occurred above did not actually gray any
+     * objects, the block queue may be empty.  We must force a
+     * promotion to be safe.
+     */
+    promoteBlockByAddr(gDvm.gcHeap->heapSource, gDvm.gcHeap->heapSource->allocPtr);
+
+    /*
+     * Scavenge blocks and relocate movable objects.
+     */
+
+    LOG_SCAV("Scavenging gDvm.threadList");
+    scavengeThreadList();
+
+    LOG_SCAV("Scavenging gDvm.gcHeap->referenceOperations");
+    scavengeLargeHeapRefTable(gcHeap->referenceOperations);
+
+    LOG_SCAV("Scavenging gDvm.gcHeap->pendingFinalizationRefs");
+    scavengeLargeHeapRefTable(gcHeap->pendingFinalizationRefs);
+
+    LOG_SCAV("Scavenging random global stuff");
+    scavengeReference(&gDvm.outOfMemoryObj);
+    scavengeReference(&gDvm.internalErrorObj);
+    scavengeReference(&gDvm.noClassDefFoundErrorObj);
+
+    // LOG_SCAV("Scavenging gDvm.internedString");
+    scavengeInternedStrings();
+
+    LOG_SCAV("Root scavenge has completed.");
+
+    scavengeBlockQueue();
+
+    // LOG_SCAV("Re-snap global class pointers.");
+    // scavengeGlobals();
+
+    LOG_SCAV("New space scavenge has completed.");
+
+    /*
+     * Process reference objects in strength order.
+     */
+
+    LOG_REF("Processing soft references...");
+    preserveSoftReferences(&gDvm.gcHeap->softReferences);
+    clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+    LOG_REF("Processing weak references...");
+    clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+    LOG_REF("Finding finalizations...");
+    processFinalizableReferences();
+
+    LOG_REF("Processing f-reachable soft references...");
+    clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+    LOG_REF("Processing f-reachable weak references...");
+    clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+    LOG_REF("Processing phantom references...");
+    clearWhiteReferences(&gDvm.gcHeap->phantomReferences);
+
+    /*
+     * Verify the stack and heap.
+     */
+    dvmVerifyRoots();
+    verifyNewSpace();
+
+    //describeBlocks(gcHeap->heapSource);
+
+    clearFromSpace(gcHeap->heapSource);
+
+    {
+        size_t alloc, rem, total;
+
+        room(&alloc, &rem, &total);
+        LOG_SCAV("AFTER GC: %zu alloc, %zu free, %zu total.", alloc, rem, total);
+    }
+}
+
+/*
+ * Interface compatibility routines.
+ */
+
+void dvmClearWhiteRefs(Object **list)
+{
+    /* do nothing */
+    assert(*list == NULL);
+}
+
+void dvmHandleSoftRefs(Object **list)
+{
+    /* do nothing */
+    assert(*list == NULL);
+}
+
+bool dvmHeapBeginMarkStep(GcMode mode)
+{
+    /* do nothing */
+    return true;
+}
+
+void dvmHeapFinishMarkStep()
+{
+    /* do nothing */
+}
+
+void dvmHeapMarkRootSet()
+{
+    /* do nothing */
+}
+
+void dvmHeapScanMarkedObjects()
+{
+    dvmScavengeRoots();
+}
+
+void dvmHeapScheduleFinalizations()
+{
+    /* do nothing */
+}
+
+void dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
+{
+    *numFreed = 0;
+    *sizeFreed = 0;
+    /* do nothing */
+}
+
+void dvmMarkDirtyObjects()
+{
+    assert(!"implemented");
+}
+
+void dvmHeapSourceThreadShutdown()
+{
+    /* do nothing */
+}
diff --git a/vm/alloc/DdmHeap.cpp b/vm/alloc/DdmHeap.cpp
new file mode 100644
index 0000000..91f1e4b
--- /dev/null
+++ b/vm/alloc/DdmHeap.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * DDM-related heap functions
+ */
+#include <sys/time.h>
+#include <time.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/DlMalloc.h"
+#include "alloc/HeapSource.h"
+
+#define DEFAULT_HEAP_ID  1
+
+enum HpifWhen {
+    HPIF_WHEN_NEVER = 0,
+    HPIF_WHEN_NOW = 1,
+    HPIF_WHEN_NEXT_GC = 2,
+    HPIF_WHEN_EVERY_GC = 3
+};
+
+/*
+ * Chunk HPIF (client --> server)
+ *
+ * Heap Info. General information about the heap,
+ * suitable for a summary display.
+ *
+ *   [u4]: number of heaps
+ *
+ *   For each heap:
+ *     [u4]: heap ID
+ *     [u8]: timestamp in ms since Unix epoch
+ *     [u1]: capture reason (same as 'when' value from server)
+ *     [u4]: max heap size in bytes (-Xmx)
+ *     [u4]: current heap size in bytes
+ *     [u4]: current number of bytes allocated
+ *     [u4]: current number of objects allocated
+ */
+#define HPIF_SIZE(numHeaps) \
+        (sizeof(u4) + (numHeaps) * (5 * sizeof(u4) + sizeof(u1) + sizeof(u8)))
+void dvmDdmSendHeapInfo(int reason, bool shouldLock)
+{
+    struct timeval now;
+    u8 nowMs;
+    u1 *buf, *b;
+
+    buf = (u1 *)malloc(HPIF_SIZE(1));
+    if (buf == NULL) {
+        return;
+    }
+    b = buf;
+
+    /* If there's a one-shot 'when', reset it.
+     */
+    if (reason == gDvm.gcHeap->ddmHpifWhen) {
+        if (shouldLock && ! dvmLockHeap()) {
+            ALOGW("%s(): can't lock heap to clear when", __func__);
+            goto skip_when;
+        }
+        if (reason == gDvm.gcHeap->ddmHpifWhen) {
+            if (gDvm.gcHeap->ddmHpifWhen == HPIF_WHEN_NEXT_GC) {
+                gDvm.gcHeap->ddmHpifWhen = HPIF_WHEN_NEVER;
+            }
+        }
+        if (shouldLock) {
+            dvmUnlockHeap();
+        }
+    }
+skip_when:
+
+    /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+     */
+    if (gettimeofday(&now, NULL) < 0) {
+        nowMs = 0;
+    } else {
+        nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+    }
+
+    /* number of heaps */
+    set4BE(b, 1); b += 4;
+
+    /* For each heap (of which there is one) */
+    {
+        /* heap ID */
+        set4BE(b, DEFAULT_HEAP_ID); b += 4;
+
+        /* timestamp */
+        set8BE(b, nowMs); b += 8;
+
+        /* 'when' value */
+        *b++ = (u1)reason;
+
+        /* max allowed heap size in bytes */
+        set4BE(b, dvmHeapSourceGetMaximumSize()); b += 4;
+
+        /* current heap size in bytes */
+        set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4;
+
+        /* number of bytes allocated */
+        set4BE(b, dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0)); b += 4;
+
+        /* number of objects allocated */
+        set4BE(b, dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0)); b += 4;
+    }
+    assert((intptr_t)b == (intptr_t)buf + (intptr_t)HPIF_SIZE(1));
+
+    dvmDbgDdmSendChunk(CHUNK_TYPE("HPIF"), b - buf, buf);
+}
+
+bool dvmDdmHandleHpifChunk(int when)
+{
+    switch (when) {
+    case HPIF_WHEN_NOW:
+        dvmDdmSendHeapInfo(when, true);
+        break;
+    case HPIF_WHEN_NEVER:
+    case HPIF_WHEN_NEXT_GC:
+    case HPIF_WHEN_EVERY_GC:
+        if (dvmLockHeap()) {
+            gDvm.gcHeap->ddmHpifWhen = when;
+            dvmUnlockHeap();
+        } else {
+            ALOGI("%s(): can't lock heap to set when", __func__);
+            return false;
+        }
+        break;
+    default:
+        ALOGI("%s(): bad when value 0x%08x", __func__, when);
+        return false;
+    }
+
+    return true;
+}
+
+enum HpsgSolidity {
+    SOLIDITY_FREE = 0,
+    SOLIDITY_HARD = 1,
+    SOLIDITY_SOFT = 2,
+    SOLIDITY_WEAK = 3,
+    SOLIDITY_PHANTOM = 4,
+    SOLIDITY_FINALIZABLE = 5,
+    SOLIDITY_SWEEP = 6,
+};
+
+enum HpsgKind {
+    KIND_OBJECT = 0,
+    KIND_CLASS_OBJECT = 1,
+    KIND_ARRAY_1 = 2,
+    KIND_ARRAY_2 = 3,
+    KIND_ARRAY_4 = 4,
+    KIND_ARRAY_8 = 5,
+    KIND_UNKNOWN = 6,
+    KIND_NATIVE = 7,
+};
+
+#define HPSG_PARTIAL (1<<7)
+#define HPSG_STATE(solidity, kind) \
+    ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
+
+struct HeapChunkContext {
+    void* startOfNextMemoryChunk;
+    u1 *buf;
+    u1 *p;
+    u1 *pieceLenField;
+    size_t bufLen;
+    size_t totalAllocationUnits;
+    int type;
+    bool merge;
+    bool needHeader;
+};
+
+#define ALLOCATION_UNIT_SIZE 8
+
+static void flush_hpsg_chunk(HeapChunkContext *ctx)
+{
+    if (ctx->pieceLenField == NULL && ctx->needHeader) {
+        /* Already flushed */
+        return;
+    }
+    /* Patch the "length of piece" field.
+     */
+    assert(ctx->buf <= ctx->pieceLenField &&
+            ctx->pieceLenField <= ctx->p);
+    set4BE(ctx->pieceLenField, ctx->totalAllocationUnits);
+
+    /* Send the chunk.
+     */
+    dvmDbgDdmSendChunk(ctx->type, ctx->p - ctx->buf, ctx->buf);
+
+    /* Reset the context.
+     */
+    ctx->p = ctx->buf;
+    ctx->totalAllocationUnits = 0;
+    ctx->needHeader = true;
+    ctx->pieceLenField = NULL;
+}
+
+static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t length) {
+    /* Make sure there's enough room left in the buffer.
+     * We need to use two bytes for every fractional 256
+     * allocation units used by the chunk and 17 bytes for
+     * any header.
+     */
+    {
+        size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17;
+        size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+        if (bytesLeft < needed) {
+            flush_hpsg_chunk(ctx);
+        }
+        bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+        if (bytesLeft < needed) {
+            ALOGW("chunk is too big to transmit (length=%zd, %zd bytes)",
+                  length, needed);
+            return;
+        }
+    }
+    if (ctx->needHeader) {
+        /*
+         * Start a new HPSx chunk.
+         */
+
+        /* [u4]: heap ID */
+        set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4;
+
+        /* [u1]: size of allocation unit, in bytes */
+        *ctx->p++ = 8;
+
+        /* [u4]: virtual address of segment start */
+        set4BE(ctx->p, (uintptr_t)ptr); ctx->p += 4;
+
+        /* [u4]: offset of this piece (relative to the virtual address) */
+        set4BE(ctx->p, 0); ctx->p += 4;
+
+        /* [u4]: length of piece, in allocation units
+         * We won't know this until we're done, so save the offset
+         * and stuff in a dummy value.
+         */
+        ctx->pieceLenField = ctx->p;
+        set4BE(ctx->p, 0x55555555); ctx->p += 4;
+
+        ctx->needHeader = false;
+    }
+    /* Write out the chunk description.
+     */
+    length /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
+    ctx->totalAllocationUnits += length;
+    while (length > 256) {
+        *ctx->p++ = state | HPSG_PARTIAL;
+        *ctx->p++ = 255;     // length - 1
+        length -= 256;
+    }
+    *ctx->p++ = state;
+    *ctx->p++ = length - 1;
+}
+
+/*
+ * Called by dlmalloc_inspect_all. If used_bytes != 0 then start is
+ * the start of a malloc-ed piece of memory of size used_bytes. If
+ * start is 0 then start is the beginning of any free space not
+ * including dlmalloc's book keeping and end the start of the next
+ * dlmalloc chunk. Regions purely containing book keeping don't
+ * callback.
+ */
+static void heap_chunk_callback(void* start, void* end, size_t used_bytes,
+                                void* arg)
+{
+    u1 state;
+    HeapChunkContext *ctx = (HeapChunkContext *)arg;
+    UNUSED_PARAMETER(end);
+
+    if (used_bytes == 0) {
+        if (start == NULL) {
+            // Reset for start of new heap.
+            ctx->startOfNextMemoryChunk = NULL;
+            flush_hpsg_chunk(ctx);
+        }
+        // Only process in use memory so that free region information
+        // also includes dlmalloc book keeping.
+        return;
+    }
+
+    /* If we're looking at the native heap, we'll just return
+     * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
+     */
+    bool native = ctx->type == CHUNK_TYPE("NHSG");
+
+    if (ctx->startOfNextMemoryChunk != NULL) {
+        // Transmit any pending free memory. Native free memory of
+        // over kMaxFreeLen could be because of the use of mmaps, so
+        // don't report. If not free memory then start a new segment.
+        bool flush = true;
+        if (start > ctx->startOfNextMemoryChunk) {
+            const size_t kMaxFreeLen = 2 * SYSTEM_PAGE_SIZE;
+            void* freeStart = ctx->startOfNextMemoryChunk;
+            void* freeEnd = start;
+            size_t freeLen = (char*)freeEnd - (char*)freeStart;
+            if (!native || freeLen < kMaxFreeLen) {
+                append_chunk(ctx, HPSG_STATE(SOLIDITY_FREE, 0),
+                             freeStart, freeLen);
+                flush = false;
+            }
+        }
+        if (flush) {
+            ctx->startOfNextMemoryChunk = NULL;
+            flush_hpsg_chunk(ctx);
+        }
+    }
+    const Object *obj = (const Object *)start;
+
+    /* It's an allocated chunk.  Figure out what it is.
+     */
+//TODO: if ctx.merge, see if this chunk is different from the last chunk.
+//      If it's the same, we should combine them.
+    if (!native && dvmIsValidObject(obj)) {
+        ClassObject *clazz = obj->clazz;
+        if (clazz == NULL) {
+            /* The object was probably just created
+             * but hasn't been initialized yet.
+             */
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+        } else if (dvmIsTheClassClass(clazz)) {
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+        } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+            if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+                state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+            } else {
+                switch (clazz->elementClass->primitiveType) {
+                case PRIM_BOOLEAN:
+                case PRIM_BYTE:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+                    break;
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+                    break;
+                case PRIM_INT:
+                case PRIM_FLOAT:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+                    break;
+                case PRIM_DOUBLE:
+                case PRIM_LONG:
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+                    break;
+                default:
+                    assert(!"Unknown GC heap object type");
+                    state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+                    break;
+                }
+            }
+        } else {
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+        }
+    } else {
+        obj = NULL; // it's not actually an object
+        state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+    }
+    append_chunk(ctx, state, start, used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD);
+    ctx->startOfNextMemoryChunk =
+        (char*)start + used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD;
+}
+
+enum HpsgWhen {
+    HPSG_WHEN_NEVER = 0,
+    HPSG_WHEN_EVERY_GC = 1,
+};
+enum HpsgWhat {
+    HPSG_WHAT_MERGED_OBJECTS = 0,
+    HPSG_WHAT_DISTINCT_OBJECTS = 1,
+};
+
+/*
+ * Maximum chunk size.  Obtain this from the formula:
+ *
+ * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
+ */
+#define HPSx_CHUNK_SIZE (16384 - 16)
+
+static void walkHeap(bool merge, bool native)
+{
+    HeapChunkContext ctx;
+
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.bufLen = HPSx_CHUNK_SIZE;
+    ctx.buf = (u1 *)malloc(ctx.bufLen);
+    if (ctx.buf == NULL) {
+        return;
+    }
+
+    ctx.merge = merge;
+    if (native) {
+        ctx.type = CHUNK_TYPE("NHSG");
+    } else {
+        if (ctx.merge) {
+            ctx.type = CHUNK_TYPE("HPSG");
+        } else {
+            ctx.type = CHUNK_TYPE("HPSO");
+        }
+    }
+
+    ctx.p = ctx.buf;
+    ctx.needHeader = true;
+    if (native) {
+#ifdef USE_DLMALLOC
+        dlmalloc_inspect_all(heap_chunk_callback, (void*)&ctx);
+#endif
+    } else {
+        dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
+    }
+    if (ctx.p > ctx.buf) {
+        flush_hpsg_chunk(&ctx);
+    }
+
+    free(ctx.buf);
+}
+
+void dvmDdmSendHeapSegments(bool shouldLock, bool native)
+{
+    u1 heapId[sizeof(u4)];
+    GcHeap *gcHeap = gDvm.gcHeap;
+    int when, what;
+    bool merge;
+
+    /* Don't even grab the lock if there's nothing to do when we're called.
+     */
+    if (!native) {
+        when = gcHeap->ddmHpsgWhen;
+        what = gcHeap->ddmHpsgWhat;
+        if (when == HPSG_WHEN_NEVER) {
+            return;
+        }
+    } else {
+        when = gcHeap->ddmNhsgWhen;
+        what = gcHeap->ddmNhsgWhat;
+        if (when == HPSG_WHEN_NEVER) {
+            return;
+        }
+    }
+    if (shouldLock && !dvmLockHeap()) {
+        ALOGW("Can't lock heap for DDM HPSx dump");
+        return;
+    }
+
+    /* Figure out what kind of chunks we'll be sending.
+     */
+    if (what == HPSG_WHAT_MERGED_OBJECTS) {
+        merge = true;
+    } else if (what == HPSG_WHAT_DISTINCT_OBJECTS) {
+        merge = false;
+    } else {
+        assert(!"bad HPSG.what value");
+        return;
+    }
+
+    /* First, send a heap start chunk.
+     */
+    set4BE(heapId, DEFAULT_HEAP_ID);
+    dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
+        sizeof(u4), heapId);
+
+    /* Send a series of heap segment chunks.
+     */
+    walkHeap(merge, native);
+
+    /* Finally, send a heap end chunk.
+     */
+    dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
+        sizeof(u4), heapId);
+
+    if (shouldLock) {
+        dvmUnlockHeap();
+    }
+}
+
+bool dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native)
+{
+    ALOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)", when, what,
+         native);
+    switch (when) {
+    case HPSG_WHEN_NEVER:
+    case HPSG_WHEN_EVERY_GC:
+        break;
+    default:
+        ALOGI("%s(): bad when value 0x%08x", __func__, when);
+        return false;
+    }
+
+    switch (what) {
+    case HPSG_WHAT_MERGED_OBJECTS:
+    case HPSG_WHAT_DISTINCT_OBJECTS:
+        break;
+    default:
+        ALOGI("%s(): bad what value 0x%08x", __func__, what);
+        return false;
+    }
+
+    if (dvmLockHeap()) {
+        if (!native) {
+            gDvm.gcHeap->ddmHpsgWhen = when;
+            gDvm.gcHeap->ddmHpsgWhat = what;
+        } else {
+            gDvm.gcHeap->ddmNhsgWhen = when;
+            gDvm.gcHeap->ddmNhsgWhat = what;
+        }
+//TODO: if what says we should dump immediately, signal (or do) it from here
+        dvmUnlockHeap();
+    } else {
+        ALOGI("%s(): can't lock heap to set when/what", __func__);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/alloc/DdmHeap.h b/vm/alloc/DdmHeap.h
new file mode 100644
index 0000000..32be78d
--- /dev/null
+++ b/vm/alloc/DdmHeap.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * DDM-specific internal heap functions.
+ */
+#ifndef DALVIK_ALLOC_DDMHEAP_H_
+#define DALVIK_ALLOC_DDMHEAP_H_
+
+/*
+ * Sends the current heap info to the DDM server.
+ * Should be called after a GC when gcHeap->ddmHpifWhen
+ * is non-zero.
+ */
+void dvmDdmSendHeapInfo(int reason, bool shouldLock);
+
+/*
+ * Walks through the heap and sends a series of
+ * HPST/NHST, HPSG/HPSO/NHSG, and HPEN/NHEN chunks that describe
+ * the contents of the GC or native heap.
+ *
+ * @param shouldLock If true, grab the heap lock.  If false,
+ *                   the heap lock must already be held.
+ * @param heap       If false, dump the GC heap; if true, dump the
+ *                   native heap.
+ */
+void dvmDdmSendHeapSegments(bool shouldLock, bool native);
+
+#endif  // DALVIK_ALLOC_DDMHEAP_H_
diff --git a/vm/alloc/DlMalloc.cpp b/vm/alloc/DlMalloc.cpp
new file mode 100644
index 0000000..1571801
--- /dev/null
+++ b/vm/alloc/DlMalloc.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include "DlMalloc.h"
+
+#include <stdint.h>
+#include "Common.h"
+
+/* Dalvik specific morecore implementation defined in HeapSource.cpp. */
+#define MORECORE(x) dvmHeapSourceMorecore(m, x)
+extern void* dvmHeapSourceMorecore(void* mspace, intptr_t increment);
+
+/* Custom heap error handling. */
+#define PROCEED_ON_ERROR 0
+static void heap_error(const char* msg, const char* function, void* p);
+#define CORRUPTION_ERROR_ACTION(m) \
+    heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__, NULL)
+#define USAGE_ERROR_ACTION(m,p) \
+    heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
+
+/*
+ * Ugly inclusion of C file so that Dalvik specific #defines configure
+ * dlmalloc for our use for mspaces (regular dlmalloc is still declared
+ * in bionic).
+ */
+#include "../../../bionic/libc/upstream-dlmalloc/malloc.c"
+
+
+static void heap_error(const char* msg, const char* function, void* p) {
+    ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: DALVIK: %s IN %s addr=%p", msg,
+         function, p);
+    /* So that we can get a memory dump around p */
+    *((int **) 0xdeadbaad) = (int *) p;
+}
diff --git a/vm/alloc/DlMalloc.h b/vm/alloc/DlMalloc.h
new file mode 100644
index 0000000..f076656
--- /dev/null
+++ b/vm/alloc/DlMalloc.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef DALVIK_VM_ALLOC_DLMALLOC_H_
+#define DALVIK_VM_ALLOC_DLMALLOC_H_
+
+/* Configure dlmalloc for mspaces. */
+#define HAVE_MMAP 0
+#define HAVE_MREMAP 0
+#define HAVE_MORECORE 1
+#define MSPACES 1
+#define NO_MALLINFO 1
+#define ONLY_MSPACES 1
+#define MALLOC_INSPECT_ALL 1
+
+/* Include the proper definitions. */
+#include "../../../bionic/libc/upstream-dlmalloc/malloc.h"
+
+/*
+ * Define dlmalloc routines from bionic that cannot be included
+ * directly because of redefining symbols from the include above.
+ */
+extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
+                                     void* arg);
+extern "C" int  dlmalloc_trim(size_t);
+extern "C" void* dlmem2chunk(void* mem);
+
+#endif  // DALVIK_VM_ALLOC_DLMALLOC_H_
diff --git a/vm/alloc/Heap.cpp b/vm/alloc/Heap.cpp
new file mode 100644
index 0000000..2de20ef
--- /dev/null
+++ b/vm/alloc/Heap.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+/*
+ * Garbage-collecting memory allocator.
+ */
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/Verify.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "os/os.h"
+
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <cutils/trace.h>
+
+static const GcSpec kGcForMallocSpec = {
+    true,  /* isPartial */
+    false,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_FOR_ALLOC"
+};
+
+const GcSpec *GC_FOR_MALLOC = &kGcForMallocSpec;
+
+static const GcSpec kGcConcurrentSpec  = {
+    true,  /* isPartial */
+    true,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_CONCURRENT"
+};
+
+const GcSpec *GC_CONCURRENT = &kGcConcurrentSpec;
+
+static const GcSpec kGcExplicitSpec = {
+    false,  /* isPartial */
+    true,  /* isConcurrent */
+    true,  /* doPreserve */
+    "GC_EXPLICIT"
+};
+
+const GcSpec *GC_EXPLICIT = &kGcExplicitSpec;
+
+static const GcSpec kGcBeforeOomSpec = {
+    false,  /* isPartial */
+    false,  /* isConcurrent */
+    false,  /* doPreserve */
+    "GC_BEFORE_OOM"
+};
+
+const GcSpec *GC_BEFORE_OOM = &kGcBeforeOomSpec;
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup()
+{
+    GcHeap *gcHeap;
+
+    if (gDvm.heapGrowthLimit == 0) {
+        gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
+    }
+
+    gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
+                                  gDvm.heapMaximumSize,
+                                  gDvm.heapGrowthLimit);
+    if (gcHeap == NULL) {
+        return false;
+    }
+    gcHeap->ddmHpifWhen = 0;
+    gcHeap->ddmHpsgWhen = 0;
+    gcHeap->ddmHpsgWhat = 0;
+    gcHeap->ddmNhsgWhen = 0;
+    gcHeap->ddmNhsgWhat = 0;
+    gDvm.gcHeap = gcHeap;
+
+    /* Set up the lists we'll use for cleared reference objects.
+     */
+    gcHeap->clearedReferences = NULL;
+
+    if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
+        LOGE_HEAP("card table startup failed.");
+        return false;
+    }
+
+    return true;
+}
+
+bool dvmHeapStartupAfterZygote()
+{
+    return dvmHeapSourceStartupAfterZygote();
+}
+
+void dvmHeapShutdown()
+{
+//TODO: make sure we're locked
+    if (gDvm.gcHeap != NULL) {
+        dvmCardTableShutdown();
+        /* Destroy the heap.  Any outstanding pointers will point to
+         * unmapped memory (unless/until someone else maps it).  This
+         * frees gDvm.gcHeap as a side-effect.
+         */
+        dvmHeapSourceShutdown(&gDvm.gcHeap);
+    }
+}
+
+/*
+ * Shutdown any threads internal to the heap.
+ */
+void dvmHeapThreadShutdown()
+{
+    dvmHeapSourceThreadShutdown();
+}
+
+/*
+ * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
+ * we're going to have to wait on the mutex.
+ */
+bool dvmLockHeap()
+{
+    if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
+        Thread *self;
+        ThreadStatus oldStatus;
+
+        self = dvmThreadSelf();
+        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(&gDvm.gcHeapLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+
+    return true;
+}
+
+void dvmUnlockHeap()
+{
+    dvmUnlockMutex(&gDvm.gcHeapLock);
+}
+
+/* Do a full garbage collection, which may grow the
+ * heap as a side-effect if the live set is large.
+ */
+static void gcForMalloc(bool clearSoftReferences)
+{
+    if (gDvm.allocProf.enabled) {
+        Thread* self = dvmThreadSelf();
+        gDvm.allocProf.gcCount++;
+        if (self != NULL) {
+            self->allocProf.gcCount++;
+        }
+    }
+    /* This may adjust the soft limit as a side-effect.
+     */
+    const GcSpec *spec = clearSoftReferences ? GC_BEFORE_OOM : GC_FOR_MALLOC;
+    dvmCollectGarbageInternal(spec);
+}
+
+/* Try as hard as possible to allocate some memory.
+ */
+static void *tryMalloc(size_t size)
+{
+    void *ptr;
+
+//TODO: figure out better heuristics
+//    There will be a lot of churn if someone allocates a bunch of
+//    big objects in a row, and we hit the frag case each time.
+//    A full GC for each.
+//    Maybe we grow the heap in bigger leaps
+//    Maybe we skip the GC if the size is large and we did one recently
+//      (number of allocations ago) (watch for thread effects)
+//    DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
+//      (or, at least, there are only 0-5 objects swept each time)
+
+    ptr = dvmHeapSourceAlloc(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    /*
+     * The allocation failed.  If the GC is running, block until it
+     * completes and retry.
+     */
+    if (gDvm.gcHeap->gcRunning) {
+        /*
+         * The GC is concurrently tracing the heap.  Release the heap
+         * lock, wait for the GC to complete, and retrying allocating.
+         */
+        dvmWaitForConcurrentGcToComplete();
+    } else {
+      /*
+       * Try a foreground GC since a concurrent GC is not currently running.
+       */
+      gcForMalloc(false);
+    }
+
+    ptr = dvmHeapSourceAlloc(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    /* Even that didn't work;  this is an exceptional state.
+     * Try harder, growing the heap if necessary.
+     */
+    ptr = dvmHeapSourceAllocAndGrow(size);
+    if (ptr != NULL) {
+        size_t newHeapSize;
+
+        newHeapSize = dvmHeapSourceGetIdealFootprint();
+//TODO: may want to grow a little bit more so that the amount of free
+//      space is equal to the old free space + the utilization slop for
+//      the new allocation.
+        LOGI_HEAP("Grow heap (frag case) to "
+                "%zu.%03zuMB for %zu-byte allocation",
+                FRACTIONAL_MB(newHeapSize), size);
+        return ptr;
+    }
+
+    /* Most allocations should have succeeded by now, so the heap
+     * is really full, really fragmented, or the requested size is
+     * really big.  Do another GC, collecting SoftReferences this
+     * time.  The VM spec requires that all SoftReferences have
+     * been collected and cleared before throwing an OOME.
+     */
+//TODO: wait for the finalizers from the previous GC to finish
+    LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation",
+            size);
+    gcForMalloc(true);
+    ptr = dvmHeapSourceAllocAndGrow(size);
+    if (ptr != NULL) {
+        return ptr;
+    }
+//TODO: maybe wait for finalizers and try one last time
+
+    LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
+//TODO: tell the HeapSource to dump its state
+    dvmDumpThread(dvmThreadSelf(), false);
+
+    return NULL;
+}
+
+/* Throw an OutOfMemoryError if there's a thread to attach it to.
+ * Avoid recursing.
+ *
+ * The caller must not be holding the heap lock, or else the allocations
+ * in dvmThrowException() will deadlock.
+ */
+static void throwOOME()
+{
+    Thread *self;
+
+    if ((self = dvmThreadSelf()) != NULL) {
+        /* If the current (failing) dvmMalloc() happened as part of thread
+         * creation/attachment before the thread became part of the root set,
+         * we can't rely on the thread-local trackedAlloc table, so
+         * we can't keep track of a real allocated OOME object.  But, since
+         * the thread is in the process of being created, it won't have
+         * a useful stack anyway, so we may as well make things easier
+         * by throwing the (stackless) pre-built OOME.
+         */
+        if (dvmIsOnThreadList(self) && !self->throwingOOME) {
+            /* Let ourselves know that we tried to throw an OOM
+             * error in the normal way in case we run out of
+             * memory trying to allocate it inside dvmThrowException().
+             */
+            self->throwingOOME = true;
+
+            /* Don't include a description string;
+             * one fewer allocation.
+             */
+            dvmThrowOutOfMemoryError(NULL);
+        } else {
+            /*
+             * This thread has already tried to throw an OutOfMemoryError,
+             * which probably means that we're running out of memory
+             * while recursively trying to throw.
+             *
+             * To avoid any more allocation attempts, "throw" a pre-built
+             * OutOfMemoryError object (which won't have a useful stack trace).
+             *
+             * Note that since this call can't possibly allocate anything,
+             * we don't care about the state of self->throwingOOME
+             * (which will usually already be set).
+             */
+            dvmSetException(self, gDvm.outOfMemoryObj);
+        }
+        /* We're done with the possible recursion.
+         */
+        self->throwingOOME = false;
+    }
+}
+
+/*
+ * Allocate storage on the GC heap.  We guarantee 8-byte alignment.
+ *
+ * The new storage is zeroed out.
+ *
+ * Note that, in rare cases, this could get called while a GC is in
+ * progress.  If a non-VM thread tries to attach itself through JNI,
+ * it will need to allocate some objects.  If this becomes annoying to
+ * deal with, we can block it at the source, but holding the allocation
+ * mutex should be enough.
+ *
+ * In rare circumstances (JNI AttachCurrentThread) we can be called
+ * from a non-VM thread.
+ *
+ * Use ALLOC_DONT_TRACK when we either don't want to track an allocation
+ * (because it's being done for the interpreter "new" operation and will
+ * be part of the root set immediately) or we can't (because this allocation
+ * is for a brand new thread).
+ *
+ * Returns NULL and throws an exception on failure.
+ *
+ * TODO: don't do a GC if the debugger thinks all threads are suspended
+ */
+void* dvmMalloc(size_t size, int flags)
+{
+    void *ptr;
+
+    dvmLockHeap();
+
+    /* Try as hard as possible to allocate some memory.
+     */
+    ptr = tryMalloc(size);
+    if (ptr != NULL) {
+        /* We've got the memory.
+         */
+        if (gDvm.allocProf.enabled) {
+            Thread* self = dvmThreadSelf();
+            gDvm.allocProf.allocCount++;
+            gDvm.allocProf.allocSize += size;
+            if (self != NULL) {
+                self->allocProf.allocCount++;
+                self->allocProf.allocSize += size;
+            }
+        }
+    } else {
+        /* The allocation failed.
+         */
+
+        if (gDvm.allocProf.enabled) {
+            Thread* self = dvmThreadSelf();
+            gDvm.allocProf.failedAllocCount++;
+            gDvm.allocProf.failedAllocSize += size;
+            if (self != NULL) {
+                self->allocProf.failedAllocCount++;
+                self->allocProf.failedAllocSize += size;
+            }
+        }
+    }
+
+    dvmUnlockHeap();
+
+    if (ptr != NULL) {
+        /*
+         * If caller hasn't asked us not to track it, add it to the
+         * internal tracking list.
+         */
+        if ((flags & ALLOC_DONT_TRACK) == 0) {
+            dvmAddTrackedAlloc((Object*)ptr, NULL);
+        }
+    } else {
+        /*
+         * The allocation failed; throw an OutOfMemoryError.
+         */
+        throwOOME();
+    }
+
+    return ptr;
+}
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj)
+{
+    /* Don't bother if it's NULL or not 8-byte aligned.
+     */
+    if (obj != NULL && ((uintptr_t)obj & (8-1)) == 0) {
+        /* Even if the heap isn't locked, this shouldn't return
+         * any false negatives.  The only mutation that could
+         * be happening is allocation, which means that another
+         * thread could be in the middle of a read-modify-write
+         * to add a new bit for a new object.  However, that
+         * RMW will have completed by the time any other thread
+         * could possibly see the new pointer, so there is no
+         * danger of dvmIsValidObject() being called on a valid
+         * pointer whose bit isn't set.
+         *
+         * Freeing will only happen during the sweep phase, which
+         * only happens while the heap is locked.
+         */
+        return dvmHeapSourceContains(obj);
+    }
+    return false;
+}
+
+size_t dvmObjectSizeInHeap(const Object *obj)
+{
+    return dvmHeapSourceChunkSize(obj);
+}
+
+static void verifyRootsAndHeap()
+{
+    dvmVerifyRoots();
+    dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
+}
+
+/*
+ * Initiate garbage collection.
+ *
+ * NOTES:
+ * - If we don't hold gDvm.threadListLock, it's possible for a thread to
+ *   be added to the thread list while we work.  The thread should NOT
+ *   start executing, so this is only interesting when we start chasing
+ *   thread stacks.  (Before we do so, grab the lock.)
+ *
+ * We are not allowed to GC when the debugger has suspended the VM, which
+ * is awkward because debugger requests can cause allocations.  The easiest
+ * way to enforce this is to refuse to GC on an allocation made by the
+ * JDWP thread -- we have to expand the heap or fail.
+ */
+void dvmCollectGarbageInternal(const GcSpec* spec)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    u4 gcEnd = 0;
+    u4 rootStart = 0 , rootEnd = 0;
+    u4 dirtyStart = 0, dirtyEnd = 0;
+    size_t numObjectsFreed, numBytesFreed;
+    size_t currAllocated, currFootprint;
+    size_t percentFree;
+    int oldThreadPriority = INT_MAX;
+
+    /* The heap lock must be held.
+     */
+
+    if (gcHeap->gcRunning) {
+        LOGW_HEAP("Attempted recursive GC");
+        return;
+    }
+
+    // Trace the beginning of the top-level GC.
+    if (spec == GC_FOR_MALLOC) {
+        ATRACE_BEGIN("GC (alloc)");
+    } else if (spec == GC_CONCURRENT) {
+        ATRACE_BEGIN("GC (concurrent)");
+    } else if (spec == GC_EXPLICIT) {
+        ATRACE_BEGIN("GC (explicit)");
+    } else if (spec == GC_BEFORE_OOM) {
+        ATRACE_BEGIN("GC (before OOM)");
+    } else {
+        ATRACE_BEGIN("GC (unknown)");
+    }
+
+    gcHeap->gcRunning = true;
+
+    rootStart = dvmGetRelativeTimeMsec();
+    ATRACE_BEGIN("GC: Threads Suspended"); // Suspend A
+    dvmSuspendAllThreads(SUSPEND_FOR_GC);
+
+    /*
+     * If we are not marking concurrently raise the priority of the
+     * thread performing the garbage collection.
+     */
+    if (!spec->isConcurrent) {
+        oldThreadPriority = os_raiseThreadPriority();
+    }
+    if (gDvm.preVerify) {
+        LOGV_HEAP("Verifying roots and heap before GC");
+        verifyRootsAndHeap();
+    }
+
+    dvmMethodTraceGCBegin();
+
+    /* Set up the marking context.
+     */
+    if (!dvmHeapBeginMarkStep(spec->isPartial)) {
+        ATRACE_END(); // Suspend A
+        ATRACE_END(); // Top-level GC
+        LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting");
+        dvmAbort();
+    }
+
+    /* Mark the set of objects that are strongly reachable from the roots.
+     */
+    LOGD_HEAP("Marking...");
+    dvmHeapMarkRootSet();
+
+    /* dvmHeapScanMarkedObjects() will build the lists of known
+     * instances of the Reference classes.
+     */
+    assert(gcHeap->softReferences == NULL);
+    assert(gcHeap->weakReferences == NULL);
+    assert(gcHeap->finalizerReferences == NULL);
+    assert(gcHeap->phantomReferences == NULL);
+    assert(gcHeap->clearedReferences == NULL);
+
+    if (spec->isConcurrent) {
+        /*
+         * Resume threads while tracing from the roots.  We unlock the
+         * heap to allow mutator threads to allocate from free space.
+         */
+        dvmClearCardTable();
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        ATRACE_END(); // Suspend A
+        rootEnd = dvmGetRelativeTimeMsec();
+    }
+
+    /* Recursively mark any objects that marked objects point to strongly.
+     * If we're not collecting soft references, soft-reachable
+     * objects will also be marked.
+     */
+    LOGD_HEAP("Recursing...");
+    dvmHeapScanMarkedObjects();
+
+    if (spec->isConcurrent) {
+        /*
+         * Re-acquire the heap lock and perform the final thread
+         * suspension.
+         */
+        dirtyStart = dvmGetRelativeTimeMsec();
+        dvmLockHeap();
+        ATRACE_BEGIN("GC: Threads Suspended"); // Suspend B
+        dvmSuspendAllThreads(SUSPEND_FOR_GC);
+        /*
+         * As no barrier intercepts root updates, we conservatively
+         * assume all roots may be gray and re-mark them.
+         */
+        dvmHeapReMarkRootSet();
+        /*
+         * With the exception of reference objects and weak interned
+         * strings, all gray objects should now be on dirty cards.
+         */
+        if (gDvm.verifyCardTable) {
+            dvmVerifyCardTable();
+        }
+        /*
+         * Recursively mark gray objects pointed to by the roots or by
+         * heap objects dirtied during the concurrent mark.
+         */
+        dvmHeapReScanMarkedObjects();
+    }
+
+    /*
+     * All strongly-reachable objects have now been marked.  Process
+     * weakly-reachable objects discovered while tracing.
+     */
+    dvmHeapProcessReferences(&gcHeap->softReferences,
+                             spec->doPreserve == false,
+                             &gcHeap->weakReferences,
+                             &gcHeap->finalizerReferences,
+                             &gcHeap->phantomReferences);
+
+#if defined(WITH_JIT)
+    /*
+     * Patching a chaining cell is very cheap as it only updates 4 words. It's
+     * the overhead of stopping all threads and synchronizing the I/D cache
+     * that makes it expensive.
+     *
+     * Therefore we batch those work orders in a queue and go through them
+     * when threads are suspended for GC.
+     */
+    dvmCompilerPerformSafePointChecks();
+#endif
+
+    LOGD_HEAP("Sweeping...");
+
+    dvmHeapSweepSystemWeaks();
+
+    /*
+     * Live objects have a bit set in the mark bitmap, swap the mark
+     * and live bitmaps.  The sweep can proceed concurrently viewing
+     * the new live bitmap as the old mark bitmap, and vice versa.
+     */
+    dvmHeapSourceSwapBitmaps();
+
+    if (gDvm.postVerify) {
+        LOGV_HEAP("Verifying roots and heap after GC");
+        verifyRootsAndHeap();
+    }
+
+    if (spec->isConcurrent) {
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        ATRACE_END(); // Suspend B
+        dirtyEnd = dvmGetRelativeTimeMsec();
+    }
+    dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent,
+                                &numObjectsFreed, &numBytesFreed);
+    LOGD_HEAP("Cleaning up...");
+    dvmHeapFinishMarkStep();
+    if (spec->isConcurrent) {
+        dvmLockHeap();
+    }
+
+    LOGD_HEAP("Done.");
+
+    /* Now's a good time to adjust the heap size, since
+     * we know what our utilization is.
+     *
+     * This doesn't actually resize any memory;
+     * it just lets the heap grow more when necessary.
+     */
+    dvmHeapSourceGrowForUtilization();
+
+    currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+    currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+
+    dvmMethodTraceGCEnd();
+    LOGV_HEAP("GC finished");
+
+    gcHeap->gcRunning = false;
+
+    LOGV_HEAP("Resuming threads");
+
+    if (spec->isConcurrent) {
+        /*
+         * Wake-up any threads that blocked after a failed allocation
+         * request.
+         */
+        dvmBroadcastCond(&gDvm.gcHeapCond);
+    }
+
+    if (!spec->isConcurrent) {
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        ATRACE_END(); // Suspend A
+        dirtyEnd = dvmGetRelativeTimeMsec();
+        /*
+         * Restore the original thread scheduling priority if it was
+         * changed at the start of the current garbage collection.
+         */
+        if (oldThreadPriority != INT_MAX) {
+            os_lowerThreadPriority(oldThreadPriority);
+        }
+    }
+
+    /*
+     * Move queue of pending references back into Java.
+     */
+    dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
+
+    gcEnd = dvmGetRelativeTimeMsec();
+    percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
+    if (!spec->isConcurrent) {
+        u4 markSweepTime = dirtyEnd - rootStart;
+        u4 gcTime = gcEnd - rootStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums, total %ums",
+             spec->reason,
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             markSweepTime, gcTime);
+    } else {
+        u4 rootTime = rootEnd - rootStart;
+        u4 dirtyTime = dirtyEnd - dirtyStart;
+        u4 gcTime = gcEnd - rootStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums+%ums, total %ums",
+             spec->reason,
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             rootTime, dirtyTime, gcTime);
+    }
+    if (gcHeap->ddmHpifWhen != 0) {
+        LOGD_HEAP("Sending VM heap info to DDM");
+        dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
+    }
+    if (gcHeap->ddmHpsgWhen != 0) {
+        LOGD_HEAP("Dumping VM heap to DDM");
+        dvmDdmSendHeapSegments(false, false);
+    }
+    if (gcHeap->ddmNhsgWhen != 0) {
+        LOGD_HEAP("Dumping native heap to DDM");
+        dvmDdmSendHeapSegments(false, true);
+    }
+
+    ATRACE_END(); // Top-level GC
+}
+
+/*
+ * If the concurrent GC is running, wait for it to finish.  The caller
+ * must hold the heap lock.
+ *
+ * Note: the second dvmChangeStatus() could stall if we were in RUNNING
+ * on entry, and some other thread has asked us to suspend.  In that
+ * case we will be suspended with the heap lock held, which can lead to
+ * deadlock if the other thread tries to do something with the managed heap.
+ * For example, the debugger might suspend us and then execute a method that
+ * allocates memory.  We can avoid this situation by releasing the lock
+ * before self-suspending.  (The developer can work around this specific
+ * situation by single-stepping the VM.  Alternatively, we could disable
+ * concurrent GC when the debugger is attached, but that might change
+ * behavior more than is desirable.)
+ *
+ * This should not be a problem in production, because any GC-related
+ * activity will grab the lock before issuing a suspend-all.  (We may briefly
+ * suspend when the GC thread calls dvmUnlockHeap before dvmResumeAllThreads,
+ * but there's no risk of deadlock.)
+ */
+bool dvmWaitForConcurrentGcToComplete()
+{
+    ATRACE_BEGIN("GC: Wait For Concurrent");
+    bool waited = gDvm.gcHeap->gcRunning;
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    u4 start = dvmGetRelativeTimeMsec();
+    while (gDvm.gcHeap->gcRunning) {
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+    u4 end = dvmGetRelativeTimeMsec();
+    if (end - start > 0) {
+        ALOGD("WAIT_FOR_CONCURRENT_GC blocked %ums", end - start);
+    }
+    ATRACE_END();
+    return waited;
+}
diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h
new file mode 100644
index 0000000..19e48cd
--- /dev/null
+++ b/vm/alloc/Heap.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Internal heap functions
+ */
+#ifndef DALVIK_ALLOC_HEAP_H_
+#define DALVIK_ALLOC_HEAP_H_
+
+struct GcSpec {
+  /* If true, only the application heap is threatened. */
+  bool isPartial;
+  /* If true, the trace is run concurrently with the mutator. */
+  bool isConcurrent;
+  /* Toggles for the soft reference clearing policy. */
+  bool doPreserve;
+  /* A name for this garbage collection mode. */
+  const char *reason;
+};
+
+/* Not enough space for an "ordinary" Object to be allocated. */
+extern const GcSpec *GC_FOR_MALLOC;
+
+/* Automatic GC triggered by exceeding a heap occupancy threshold. */
+extern const GcSpec *GC_CONCURRENT;
+
+/* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
+extern const GcSpec *GC_EXPLICIT;
+
+/* Final attempt to reclaim memory before throwing an OOM. */
+extern const GcSpec *GC_BEFORE_OOM;
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup(void);
+
+/*
+ * Initialization that needs to wait until after leaving zygote mode.
+ * This needs to be called before the first allocation or GC that
+ * happens after forking.
+ */
+bool dvmHeapStartupAfterZygote(void);
+
+/*
+ * Tear down the GC heap.
+ *
+ * Frees all memory allocated via dvmMalloc() as
+ * a side-effect.
+ */
+void dvmHeapShutdown(void);
+
+/*
+ * Stops any threads internal to the garbage collector.  Called before
+ * the heap itself is shutdown.
+ */
+void dvmHeapThreadShutdown(void);
+
+#if 0       // needs to be in Alloc.h so debug code can find it.
+/*
+ * Returns a number of bytes greater than or
+ * equal to the size of the named object in the heap.
+ *
+ * Specifically, it returns the size of the heap
+ * chunk which contains the object.
+ */
+size_t dvmObjectSizeInHeap(const Object *obj);
+#endif
+
+/*
+ * Run the garbage collector without doing any locking.
+ */
+void dvmCollectGarbageInternal(const GcSpec *spec);
+
+/*
+ * Blocks the calling thread until the garbage collector is inactive.
+ * The caller must hold the heap lock as this call releases and
+ * re-acquires the heap lock.  After returning, no garbage collection
+ * will be in progress and the heap lock will be held by the caller.
+ */
+bool dvmWaitForConcurrentGcToComplete(void);
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj);
+
+#endif  // DALVIK_ALLOC_HEAP_H_
diff --git a/vm/alloc/HeapBitmap.cpp b/vm/alloc/HeapBitmap.cpp
new file mode 100644
index 0000000..d6a737b
--- /dev/null
+++ b/vm/alloc/HeapBitmap.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "Dalvik.h"
+#include "HeapBitmap.h"
+#include <sys/mman.h>   /* for PROT_* */
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+                       const char *name)
+{
+    void *bits;
+    size_t bitsLen;
+
+    assert(hb != NULL);
+    assert(name != NULL);
+    bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
+    bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);
+    if (bits == NULL) {
+        ALOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
+        return false;
+    }
+    hb->bits = (unsigned long *)bits;
+    hb->bitsLen = hb->allocLen = bitsLen;
+    hb->base = (uintptr_t)base;
+    hb->max = hb->base - 1;
+    return true;
+}
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void dvmHeapBitmapDelete(HeapBitmap *hb)
+{
+    assert(hb != NULL);
+
+    if (hb->bits != NULL) {
+        munmap((char *)hb->bits, hb->allocLen);
+    }
+    memset(hb, 0, sizeof(*hb));
+}
+
+/*
+ * Fill the bitmap with zeroes.  Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void dvmHeapBitmapZero(HeapBitmap *hb)
+{
+    assert(hb != NULL);
+
+    if (hb->bits != NULL) {
+        /* This returns the memory to the system.
+         * Successive page faults will return zeroed memory.
+         */
+        madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
+        hb->max = hb->base - 1;
+    }
+}
+
+/*
+ * Return true iff <obj> is within the range of pointers that this
+ * bitmap could potentially cover, even if a bit has not been set
+ * for it.
+ */
+bool dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj)
+{
+    assert(hb != NULL);
+    if (obj != NULL) {
+        const uintptr_t offset = (uintptr_t)obj - hb->base;
+        const size_t index = HB_OFFSET_TO_INDEX(offset);
+        return index < hb->bitsLen / sizeof(*hb->bits);
+    }
+    return false;
+}
+
+/*
+ * Visits set bits in address order.  The callback is not permitted to
+ * change the bitmap bits or max during the traversal.
+ */
+void dvmHeapBitmapWalk(const HeapBitmap *bitmap, BitmapCallback *callback,
+                       void *arg)
+{
+    assert(bitmap != NULL);
+    assert(bitmap->bits != NULL);
+    assert(callback != NULL);
+    uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+    for (uintptr_t i = 0; i <= end; ++i) {
+        unsigned long word = bitmap->bits[i];
+        if (UNLIKELY(word != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+            while (word != 0) {
+                const int shift = CLZ(word);
+                Object* obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(obj, arg);
+                word &= ~(highBit >> shift);
+            }
+        }
+    }
+}
+
+/*
+ * Similar to dvmHeapBitmapWalk but the callback routine is permitted
+ * to change the bitmap bits and max during traversal.  Used by the
+ * the root marking scan exclusively.
+ *
+ * The callback is invoked with a finger argument.  The finger is a
+ * pointer to an address not yet visited by the traversal.  If the
+ * callback sets a bit for an address at or above the finger, this
+ * address will be visited by the traversal.  If the callback sets a
+ * bit for an address below the finger, this address will not be
+ * visited.
+ */
+void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           BitmapScanCallback *callback, void *arg)
+{
+    assert(bitmap != NULL);
+    assert(bitmap->bits != NULL);
+    assert(callback != NULL);
+    uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+    uintptr_t i;
+    for (i = 0; i <= end; ++i) {
+        unsigned long word = bitmap->bits[i];
+        if (UNLIKELY(word != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+            void *finger = (void *)(HB_INDEX_TO_OFFSET(i + 1) + bitmap->base);
+            while (word != 0) {
+                const int shift = CLZ(word);
+                Object *obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(obj, finger, arg);
+                word &= ~(highBit >> shift);
+            }
+            end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+        }
+    }
+}
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects.  Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is not permitted to increase the max of either bitmap.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            uintptr_t base, uintptr_t max,
+                            BitmapSweepCallback *callback, void *callbackArg)
+{
+    assert(liveHb != NULL);
+    assert(liveHb->bits != NULL);
+    assert(markHb != NULL);
+    assert(markHb->bits != NULL);
+    assert(liveHb->base == markHb->base);
+    assert(liveHb->bitsLen == markHb->bitsLen);
+    assert(callback != NULL);
+    assert(base <= max);
+    assert(base >= liveHb->base);
+    assert(max <= liveHb->max);
+    if (liveHb->max < liveHb->base) {
+        /* Easy case; both are obviously empty.
+         */
+        return;
+    }
+    void *pointerBuf[4 * HB_BITS_PER_WORD];
+    void **pb = pointerBuf;
+    size_t start = HB_OFFSET_TO_INDEX(base - liveHb->base);
+    size_t end = HB_OFFSET_TO_INDEX(max - liveHb->base);
+    unsigned long *live = liveHb->bits;
+    unsigned long *mark = markHb->bits;
+    for (size_t i = start; i <= end; i++) {
+        unsigned long garbage = live[i] & ~mark[i];
+        if (UNLIKELY(garbage != 0)) {
+            unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+            uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + liveHb->base;
+            while (garbage != 0) {
+                int shift = CLZ(garbage);
+                garbage &= ~(highBit >> shift);
+                *pb++ = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+            }
+            /* Make sure that there are always enough slots available */
+            /* for an entire word of 1s. */
+            if (pb >= &pointerBuf[NELEM(pointerBuf) - HB_BITS_PER_WORD]) {
+                (*callback)(pb - pointerBuf, pointerBuf, callbackArg);
+                pb = pointerBuf;
+            }
+        }
+    }
+    if (pb > pointerBuf) {
+        (*callback)(pb - pointerBuf, pointerBuf, callbackArg);
+    }
+}
diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h
new file mode 100644
index 0000000..9f2a0a1
--- /dev/null
+++ b/vm/alloc/HeapBitmap.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef DALVIK_HEAP_BITMAP_H_
+#define DALVIK_HEAP_BITMAP_H_
+
+#include <limits.h>
+#include <stdint.h>
+
+#define HB_OBJECT_ALIGNMENT 8
+#define HB_BITS_PER_WORD (sizeof(unsigned long) * CHAR_BIT)
+
+/* <offset> is the difference from .base to a pointer address.
+ * <index> is the index of .bits that contains the bit representing
+ *         <offset>.
+ */
+#define HB_OFFSET_TO_INDEX(offset_) \
+    ((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT / HB_BITS_PER_WORD)
+#define HB_INDEX_TO_OFFSET(index_) \
+    ((uintptr_t)(index_) * HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD)
+
+#define HB_OFFSET_TO_BYTE_INDEX(offset_) \
+  (HB_OFFSET_TO_INDEX(offset_) * sizeof(*((HeapBitmap *)0)->bits))
+
+/* Pack the bits in backwards so they come out in address order
+ * when using CLZ.
+ */
+#define HB_OFFSET_TO_MASK(offset_) \
+    (1 << \
+        (31-(((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT) % HB_BITS_PER_WORD)))
+
+struct HeapBitmap {
+    /* The bitmap data, which points to an mmap()ed area of zeroed
+     * anonymous memory.
+     */
+    unsigned long *bits;
+
+    /* The size of the used memory pointed to by bits, in bytes.  This
+     * value changes when the bitmap is shrunk.
+     */
+    size_t bitsLen;
+
+    /* The real size of the memory pointed to by bits.  This is the
+     * number of bytes we requested from the allocator and does not
+     * change.
+     */
+    size_t allocLen;
+
+    /* The base address, which corresponds to the first bit in
+     * the bitmap.
+     */
+    uintptr_t base;
+
+    /* The highest pointer value ever returned by an allocation
+     * from this heap.  I.e., the highest address that may correspond
+     * to a set bit.  If there are no bits set, (max < base).
+     */
+    uintptr_t max;
+};
+
+/*
+ * Callback types used by the walking routines.
+ */
+typedef void BitmapCallback(Object *obj, void *arg);
+typedef void BitmapScanCallback(Object *obj, void *finger, void *arg);
+typedef void BitmapSweepCallback(size_t numPtrs, void **ptrs, void *arg);
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+        const char *name);
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void dvmHeapBitmapDelete(HeapBitmap *hb);
+
+/*
+ * Fill the bitmap with zeroes.  Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void dvmHeapBitmapZero(HeapBitmap *hb);
+
+/*
+ * Returns true if the address range of the bitmap covers the object
+ * address.
+ */
+bool dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj);
+
+/*
+ * Applies the callback function to each set address in the bitmap.
+ */
+void dvmHeapBitmapWalk(const HeapBitmap *bitmap,
+                       BitmapCallback *callback, void *callbackArg);
+
+/*
+ * Like dvmHeapBitmapWalk but takes a callback function with a finger
+ * address.
+ */
+void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           BitmapScanCallback *callback, void *arg);
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects.  Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is not permitted to increase the max of either bitmap.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            uintptr_t base, uintptr_t max,
+                            BitmapSweepCallback *callback, void *callbackArg);
+
+#endif  // DALVIK_HEAP_BITMAP_H_
diff --git a/vm/alloc/HeapBitmapInlines.h b/vm/alloc/HeapBitmapInlines.h
new file mode 100644
index 0000000..a5e9be4
--- /dev/null
+++ b/vm/alloc/HeapBitmapInlines.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef DALVIK_HEAP_BITMAPINLINES_H_
+#define DALVIK_HEAP_BITMAPINLINES_H_
+
+static unsigned long dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+static void dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+static void dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj) __attribute__((used));
+
+/*
+ * Internal function; do not call directly.
+ */
+static unsigned long _heapBitmapModifyObjectBit(HeapBitmap *hb, const void *obj,
+                                                bool setBit, bool returnOld)
+{
+    const uintptr_t offset = (uintptr_t)obj - hb->base;
+    const size_t index = HB_OFFSET_TO_INDEX(offset);
+    const unsigned long mask = HB_OFFSET_TO_MASK(offset);
+
+    assert(hb->bits != NULL);
+    assert((uintptr_t)obj >= hb->base);
+    assert(index < hb->bitsLen / sizeof(*hb->bits));
+    if (setBit) {
+        if ((uintptr_t)obj > hb->max) {
+            hb->max = (uintptr_t)obj;
+        }
+        if (returnOld) {
+            unsigned long *p = hb->bits + index;
+            const unsigned long word = *p;
+            *p |= mask;
+            return word & mask;
+        } else {
+            hb->bits[index] |= mask;
+        }
+    } else {
+        hb->bits[index] &= ~mask;
+    }
+    return false;
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and returns the previous value
+ * of that bit (as zero or non-zero). Does no range checking to see if
+ * <obj> is outside of the coverage of the bitmap.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+static unsigned long dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb,
+                                                        const void *obj)
+{
+    return _heapBitmapModifyObjectBit(hb, obj, true, true);
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and widens the range of seen
+ * pointers if necessary.  Does no range checking.
+ */
+static void dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj)
+{
+    _heapBitmapModifyObjectBit(hb, obj, true, false);
+}
+
+/*
+ * Clears the bit corresponding to <obj>.  Does no range checking.
+ */
+static void dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj)
+{
+    _heapBitmapModifyObjectBit(hb, obj, false, false);
+}
+
+/*
+ * Returns the current value of the bit corresponding to <obj>,
+ * as zero or non-zero.  Does no range checking.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+static unsigned long dvmHeapBitmapIsObjectBitSet(const HeapBitmap *hb,
+                                                 const void *obj)
+{
+    assert(dvmHeapBitmapCoversAddress(hb, obj));
+    assert(hb->bits != NULL);
+    assert((uintptr_t)obj >= hb->base);
+    if ((uintptr_t)obj <= hb->max) {
+        const uintptr_t offset = (uintptr_t)obj - hb->base;
+        return hb->bits[HB_OFFSET_TO_INDEX(offset)] & HB_OFFSET_TO_MASK(offset);
+    } else {
+        return 0;
+    }
+}
+
+#endif  // DALVIK_HEAP_BITMAPINLINES_H_
diff --git a/vm/alloc/HeapDebug.cpp b/vm/alloc/HeapDebug.cpp
new file mode 100644
index 0000000..9bd6799
--- /dev/null
+++ b/vm/alloc/HeapDebug.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "Dalvik.h"
+#include "HeapSource.h"
+
+int dvmGetHeapDebugInfo(HeapDebugInfoType info)
+{
+    switch (info) {
+    case kVirtualHeapSize:
+        return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+    case kVirtualHeapAllocated:
+        return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+    case kVirtualHeapMaximumSize:
+        return dvmHeapSourceGetMaximumSize();
+    default:
+        return -1;
+    }
+}
diff --git a/vm/alloc/HeapDebug.h b/vm/alloc/HeapDebug.h
new file mode 100644
index 0000000..de4d65b
--- /dev/null
+++ b/vm/alloc/HeapDebug.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef DALVIK_HEAPDEBUG_H_
+#define DALVIK_HEAPDEBUG_H_
+
+enum HeapDebugInfoType {
+    kVirtualHeapSize = 0,
+    kNativeHeapSize = 1,
+    kVirtualHeapAllocated = 2,
+    kNativeHeapAllocated = 3,
+    kVirtualHeapMaximumSize = 4
+};
+
+/* Return the specified value.
+ * Returns -1 if the type is unknown.
+ */
+int dvmGetHeapDebugInfo(HeapDebugInfoType info);
+
+#endif  // DALVIK_HEAPDEBUG_H_
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
new file mode 100644
index 0000000..576bf7d
--- /dev/null
+++ b/vm/alloc/HeapInternal.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Types and macros used internally by the heap.
+ */
+#ifndef DALVIK_ALLOC_HEAP_INTERNAL_H_
+#define DALVIK_ALLOC_HEAP_INTERNAL_H_
+
+#include "MarkSweep.h"
+
+struct HeapSource;
+
+struct GcHeap {
+    HeapSource *heapSource;
+
+    /* Linked lists of subclass instances of java/lang/ref/Reference
+     * that we find while recursing.  The "next" pointers are hidden
+     * in the Reference objects' pendingNext fields.  These lists are
+     * cleared and rebuilt each time the GC runs.
+     */
+    Object *softReferences;
+    Object *weakReferences;
+    Object *finalizerReferences;
+    Object *phantomReferences;
+
+    /* The list of Reference objects that need to be enqueued.
+     */
+    Object *clearedReferences;
+
+    /* The current state of the mark step.
+     * Only valid during a GC.
+     */
+    GcMarkContext markContext;
+
+    /* GC's card table */
+    u1* cardTableBase;
+    size_t cardTableLength;
+    size_t cardTableMaxLength;
+    size_t cardTableOffset;
+
+    /* Is the GC running?  Used to avoid recursive calls to GC.
+     */
+    bool gcRunning;
+
+    /*
+     * Debug control values
+     */
+    int ddmHpifWhen;
+    int ddmHpsgWhen;
+    int ddmHpsgWhat;
+    int ddmNhsgWhen;
+    int ddmNhsgWhat;
+};
+
+bool dvmLockHeap(void);
+void dvmUnlockHeap(void);
+
+/*
+ * Logging helpers
+ */
+
+#define HEAP_LOG_TAG      LOG_TAG "-heap"
+
+#if LOG_NDEBUG
+#define LOGV_HEAP(...)    ((void)0)
+#define LOGD_HEAP(...)    ((void)0)
+#else
+#define LOGV_HEAP(...)    ALOG(LOG_VERBOSE, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGD_HEAP(...)    ALOG(LOG_DEBUG, HEAP_LOG_TAG, __VA_ARGS__)
+#endif
+#define LOGI_HEAP(...) \
+    do { \
+        if (!gDvm.zygote) { ALOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__); } \
+    } while (0)
+
+#define LOGW_HEAP(...)    ALOG(LOG_WARN, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGE_HEAP(...)    ALOG(LOG_ERROR, HEAP_LOG_TAG, __VA_ARGS__)
+
+#define FRACTIONAL_MB(n)    (n) / (1024 * 1024), \
+                            ((((n) % (1024 * 1024)) / 1024) * 1000) / 1024
+#define FRACTIONAL_PCT(n,max)    ((n) * 100) / (max), \
+                                 (((n) * 1000) / (max)) % 10
+
+#endif  // DALVIK_ALLOC_HEAP_INTERNAL_H_
diff --git a/vm/alloc/HeapSource.cpp b/vm/alloc/HeapSource.cpp
new file mode 100644
index 0000000..421efae
--- /dev/null
+++ b/vm/alloc/HeapSource.cpp
@@ -0,0 +1,1599 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <cutils/ashmem.h>
+
+#include "Dalvik.h"
+#include "alloc/DlMalloc.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+
+static void dvmHeapSourceUpdateMaxNativeFootprint();
+static void snapIdealFootprint();
+static void setIdealFootprint(size_t max);
+static size_t getMaximumSize(const HeapSource *hs);
+static void trimHeaps();
+
+#define HEAP_UTILIZATION_MAX        1024
+
+/* How long to wait after a GC before performing a heap trim
+ * operation to reclaim unused pages.
+ */
+#define HEAP_TRIM_IDLE_TIME_MS (5 * 1000)
+
+/* Start a concurrent collection when free memory falls under this
+ * many bytes.
+ */
+#define CONCURRENT_START (128 << 10)
+
+/* The next GC will not be concurrent when free memory after a GC is
+ * under this many bytes.
+ */
+#define CONCURRENT_MIN_FREE (CONCURRENT_START + (128 << 10))
+
+#define HS_BOILERPLATE() \
+    do { \
+        assert(gDvm.gcHeap != NULL); \
+        assert(gDvm.gcHeap->heapSource != NULL); \
+        assert(gHs == gDvm.gcHeap->heapSource); \
+    } while (0)
+
+struct Heap {
+    /* The mspace to allocate from.
+     */
+    mspace msp;
+
+    /* The largest size that this heap is allowed to grow to.
+     */
+    size_t maximumSize;
+
+    /* Number of bytes allocated from this mspace for objects,
+     * including any overhead.  This value is NOT exact, and
+     * should only be used as an input for certain heuristics.
+     */
+    size_t bytesAllocated;
+
+    /* Number of bytes allocated from this mspace at which a
+     * concurrent garbage collection will be started.
+     */
+    size_t concurrentStartBytes;
+
+    /* Number of objects currently allocated from this mspace.
+     */
+    size_t objectsAllocated;
+
+    /*
+     * The lowest address of this heap, inclusive.
+     */
+    char *base;
+
+    /*
+     * The highest address of this heap, exclusive.
+     */
+    char *limit;
+
+    /*
+     * If the heap has an mspace, the current high water mark in
+     * allocations requested via dvmHeapSourceMorecore.
+     */
+    char *brk;
+};
+
+struct HeapSource {
+    /* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
+     */
+    size_t targetUtilization;
+
+    /* The starting heap size.
+     */
+    size_t startSize;
+
+    /* The largest that the heap source as a whole is allowed to grow.
+     */
+    size_t maximumSize;
+
+    /*
+     * The largest size we permit the heap to grow.  This value allows
+     * the user to limit the heap growth below the maximum size.  This
+     * is a work around until we can dynamically set the maximum size.
+     * This value can range between the starting size and the maximum
+     * size but should never be set below the current footprint of the
+     * heap.
+     */
+    size_t growthLimit;
+
+    /* The desired max size of the heap source as a whole.
+     */
+    size_t idealSize;
+
+    /* The maximum number of bytes allowed to be allocated from the
+     * active heap before a GC is forced.  This is used to "shrink" the
+     * heap in lieu of actual compaction.
+     */
+    size_t softLimit;
+
+    /* Minimum number of free bytes. Used with the target utilization when
+     * setting the softLimit. Never allows less bytes than this to be free
+     * when the heap size is below the maximum size or growth limit.
+     */
+    size_t minFree;
+
+    /* Maximum number of free bytes. Used with the target utilization when
+     * setting the softLimit. Never allows more bytes than this to be free
+     * when the heap size is below the maximum size or growth limit.
+     */
+    size_t maxFree;
+
+    /* The heaps; heaps[0] is always the active heap,
+     * which new objects should be allocated from.
+     */
+    Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+    /* The current number of heaps.
+     */
+    size_t numHeaps;
+
+    /* True if zygote mode was active when the HeapSource was created.
+     */
+    bool sawZygote;
+
+    /*
+     * The base address of the virtual memory reservation.
+     */
+    char *heapBase;
+
+    /*
+     * The length in bytes of the virtual memory reservation.
+     */
+    size_t heapLength;
+
+    /*
+     * The live object bitmap.
+     */
+    HeapBitmap liveBits;
+
+    /*
+     * The mark bitmap.
+     */
+    HeapBitmap markBits;
+
+    /*
+     * Native allocations.
+     */
+    int32_t nativeBytesAllocated;
+    size_t nativeFootprintGCWatermark;
+    size_t nativeFootprintLimit;
+    bool nativeNeedToRunFinalization;
+
+    /*
+     * State for the GC daemon.
+     */
+    bool hasGcThread;
+    pthread_t gcThread;
+    bool gcThreadShutdown;
+    pthread_mutex_t gcThreadMutex;
+    pthread_cond_t gcThreadCond;
+    bool gcThreadTrimNeeded;
+};
+
+#define hs2heap(hs_) (&((hs_)->heaps[0]))
+
+/*
+ * Returns true iff a soft limit is in effect for the active heap.
+ */
+static bool isSoftLimited(const HeapSource *hs)
+{
+    /* softLimit will be either SIZE_MAX or the limit for the
+     * active mspace.  idealSize can be greater than softLimit
+     * if there is more than one heap.  If there is only one
+     * heap, a non-SIZE_MAX softLimit should always be the same
+     * as idealSize.
+     */
+    return hs->softLimit <= hs->idealSize;
+}
+
+/*
+ * Returns approximately the maximum number of bytes allowed to be
+ * allocated from the active heap before a GC is forced.
+ */
+static size_t getAllocLimit(const HeapSource *hs)
+{
+    if (isSoftLimited(hs)) {
+        return hs->softLimit;
+    } else {
+        return mspace_footprint_limit(hs2heap(hs)->msp);
+    }
+}
+
+/*
+ * Returns the current footprint of all heaps.  If includeActive
+ * is false, don't count the heap at index 0.
+ */
+static size_t oldHeapOverhead(const HeapSource *hs, bool includeActive)
+{
+    size_t footprint = 0;
+    size_t i;
+
+    if (includeActive) {
+        i = 0;
+    } else {
+        i = 1;
+    }
+    for (/* i = i */; i < hs->numHeaps; i++) {
+//TODO: include size of bitmaps?  If so, don't use bitsLen, listen to .max
+        footprint += mspace_footprint(hs->heaps[i].msp);
+    }
+    return footprint;
+}
+
+/*
+ * Returns the heap that <ptr> could have come from, or NULL
+ * if it could not have come from any heap.
+ */
+static Heap *ptr2heap(const HeapSource *hs, const void *ptr)
+{
+    const size_t numHeaps = hs->numHeaps;
+
+    if (ptr != NULL) {
+        for (size_t i = 0; i < numHeaps; i++) {
+            const Heap *const heap = &hs->heaps[i];
+
+            if ((const char *)ptr >= heap->base && (const char *)ptr < heap->limit) {
+                return (Heap *)heap;
+            }
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Functions to update heapSource->bytesAllocated when an object
+ * is allocated or freed.  mspace_usable_size() will give
+ * us a much more accurate picture of heap utilization than
+ * the requested byte sizes would.
+ *
+ * These aren't exact, and should not be treated as such.
+ */
+static void countAllocation(Heap *heap, const void *ptr)
+{
+    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+
+    heap->bytesAllocated += mspace_usable_size(ptr) +
+            HEAP_SOURCE_CHUNK_OVERHEAD;
+    heap->objectsAllocated++;
+    HeapSource* hs = gDvm.gcHeap->heapSource;
+    dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr);
+
+    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+}
+
+static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
+{
+    size_t delta = mspace_usable_size(ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
+    assert(delta > 0);
+    if (delta < heap->bytesAllocated) {
+        heap->bytesAllocated -= delta;
+    } else {
+        heap->bytesAllocated = 0;
+    }
+    HeapSource* hs = gDvm.gcHeap->heapSource;
+    dvmHeapBitmapClearObjectBit(&hs->liveBits, ptr);
+    if (heap->objectsAllocated > 0) {
+        heap->objectsAllocated--;
+    }
+    *numBytes += delta;
+}
+
+static HeapSource *gHs = NULL;
+
+static mspace createMspace(void* begin, size_t morecoreStart, size_t startingSize)
+{
+    // Clear errno to allow strerror on error.
+    errno = 0;
+    // Allow access to inital pages that will hold mspace.
+    mprotect(begin, morecoreStart, PROT_READ | PROT_WRITE);
+    // Create mspace using our backing storage starting at begin and with a footprint of
+    // morecoreStart. Don't use an internal dlmalloc lock. When morecoreStart bytes of memory are
+    // exhausted morecore will be called.
+    mspace msp = create_mspace_with_base(begin, morecoreStart, false /*locked*/);
+    if (msp != NULL) {
+        // Do not allow morecore requests to succeed beyond the starting size of the heap.
+        mspace_set_footprint_limit(msp, startingSize);
+    } else {
+        ALOGE("create_mspace_with_base failed %s", strerror(errno));
+    }
+    return msp;
+}
+
+/*
+ * Service request from DlMalloc to increase heap size.
+ */
+void* dvmHeapSourceMorecore(void* mspace, intptr_t increment)
+{
+    Heap* heap = NULL;
+    for (size_t i = 0; i < gHs->numHeaps; i++) {
+        if (gHs->heaps[i].msp == mspace) {
+            heap = &gHs->heaps[i];
+            break;
+        }
+    }
+    if (heap == NULL) {
+        ALOGE("Failed to find heap for mspace %p", mspace);
+        dvmAbort();
+    }
+    char* original_brk = heap->brk;
+    if (increment != 0) {
+        char* new_brk = original_brk + increment;
+        if (increment > 0) {
+            // Should never be asked to increase the allocation beyond the capacity of the space.
+            // Enforced by mspace_set_footprint_limit.
+            assert(new_brk <= heap->limit);
+            mprotect(original_brk, increment, PROT_READ | PROT_WRITE);
+        } else {
+            // Should never be asked for negative footprint (ie before base).
+            assert(original_brk + increment > heap->base);
+            // Advise we don't need the pages and protect them.
+            size_t size = -increment;
+            madvise(new_brk, size, MADV_DONTNEED);
+            mprotect(new_brk, size, PROT_NONE);
+        }
+        // Update brk.
+        heap->brk = new_brk;
+    }
+    return original_brk;
+}
+
+const size_t kInitialMorecoreStart = SYSTEM_PAGE_SIZE;
+/*
+ * Add the initial heap.  Returns false if the initial heap was
+ * already added to the heap source.
+ */
+static bool addInitialHeap(HeapSource *hs, mspace msp, size_t maximumSize)
+{
+    assert(hs != NULL);
+    assert(msp != NULL);
+    if (hs->numHeaps != 0) {
+        return false;
+    }
+    hs->heaps[0].msp = msp;
+    hs->heaps[0].maximumSize = maximumSize;
+    hs->heaps[0].concurrentStartBytes = SIZE_MAX;
+    hs->heaps[0].base = hs->heapBase;
+    hs->heaps[0].limit = hs->heapBase + maximumSize;
+    hs->heaps[0].brk = hs->heapBase + kInitialMorecoreStart;
+    hs->numHeaps = 1;
+    return true;
+}
+
+/*
+ * A helper for addNewHeap(). Remap the new heap so that it will have
+ * a separate ashmem region with possibly a different name, etc. In
+ * practice, this is used to give the app heap a separate ashmem
+ * region from the zygote heap's.
+ */
+static bool remapNewHeap(HeapSource* hs, Heap* newHeap)
+{
+  char* newHeapBase = newHeap->base;
+  size_t rem_size = hs->heapBase + hs->heapLength - newHeapBase;
+  munmap(newHeapBase, rem_size);
+  int fd = ashmem_create_region("dalvik-heap", rem_size);
+  if (fd == -1) {
+    ALOGE("Unable to create an ashmem region for the new heap");
+    return false;
+  }
+  void* addr = mmap(newHeapBase, rem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0);
+  int ret = close(fd);
+  if (addr == MAP_FAILED) {
+    ALOGE("Unable to map an ashmem region for the new heap");
+    return false;
+  }
+  if (ret == -1) {
+    ALOGE("Unable to close fd for the ashmem region for the new heap");
+    munmap(newHeapBase, rem_size);
+    return false;
+  }
+  return true;
+}
+
+/*
+ * Adds an additional heap to the heap source.  Returns false if there
+ * are too many heaps or insufficient free space to add another heap.
+ */
+static bool addNewHeap(HeapSource *hs)
+{
+    Heap heap;
+
+    assert(hs != NULL);
+    if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
+        ALOGE("Attempt to create too many heaps (%zd >= %zd)",
+                hs->numHeaps, HEAP_SOURCE_MAX_HEAP_COUNT);
+        dvmAbort();
+        return false;
+    }
+
+    memset(&heap, 0, sizeof(heap));
+
+    /*
+     * Heap storage comes from a common virtual memory reservation.
+     * The new heap will start on the page after the old heap.
+     */
+    char *base = hs->heaps[0].brk;
+    size_t overhead = base - hs->heaps[0].base;
+    assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
+
+    if (overhead + hs->minFree >= hs->maximumSize) {
+        LOGE_HEAP("No room to create any more heaps "
+                  "(%zd overhead, %zd max)",
+                  overhead, hs->maximumSize);
+        return false;
+    }
+    size_t morecoreStart = SYSTEM_PAGE_SIZE;
+    heap.maximumSize = hs->growthLimit - overhead;
+    heap.concurrentStartBytes = hs->minFree - CONCURRENT_START;
+    heap.base = base;
+    heap.limit = heap.base + heap.maximumSize;
+    heap.brk = heap.base + morecoreStart;
+    if (!remapNewHeap(hs, &heap)) {
+      return false;
+    }
+    heap.msp = createMspace(base, morecoreStart, hs->minFree);
+    if (heap.msp == NULL) {
+        return false;
+    }
+
+    /* Don't let the soon-to-be-old heap grow any further.
+     */
+    hs->heaps[0].maximumSize = overhead;
+    hs->heaps[0].limit = base;
+    mspace_set_footprint_limit(hs->heaps[0].msp, overhead);
+
+    /* Put the new heap in the list, at heaps[0].
+     * Shift existing heaps down.
+     */
+    memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));
+    hs->heaps[0] = heap;
+    hs->numHeaps++;
+
+    return true;
+}
+
+/*
+ * The garbage collection daemon.  Initiates a concurrent collection
+ * when signaled.  Also periodically trims the heaps when a few seconds
+ * have elapsed since the last concurrent GC.
+ */
+static void *gcDaemonThread(void* arg)
+{
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+    dvmLockMutex(&gHs->gcThreadMutex);
+    while (gHs->gcThreadShutdown != true) {
+        bool trim = false;
+        if (gHs->gcThreadTrimNeeded) {
+            int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex,
+                    HEAP_TRIM_IDLE_TIME_MS, 0);
+            if (result == ETIMEDOUT) {
+                /* Timed out waiting for a GC request, schedule a heap trim. */
+                trim = true;
+            }
+        } else {
+            dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
+        }
+
+        // Many JDWP requests cause allocation. We can't take the heap lock and wait to
+        // transition to runnable so we can start a GC if a debugger is connected, because
+        // we don't know that the JDWP thread isn't about to allocate and require the
+        // heap lock itself, leading to deadlock. http://b/8191824.
+        if (gDvm.debuggerConnected) {
+            continue;
+        }
+
+        dvmLockHeap();
+        /*
+         * Another thread may have started a concurrent garbage
+         * collection before we were scheduled.  Check for this
+         * condition before proceeding.
+         */
+        if (!gDvm.gcHeap->gcRunning) {
+            dvmChangeStatus(NULL, THREAD_RUNNING);
+            if (trim) {
+                trimHeaps();
+                gHs->gcThreadTrimNeeded = false;
+            } else {
+                dvmCollectGarbageInternal(GC_CONCURRENT);
+                gHs->gcThreadTrimNeeded = true;
+            }
+            dvmChangeStatus(NULL, THREAD_VMWAIT);
+        }
+        dvmUnlockHeap();
+    }
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+    return NULL;
+}
+
+static bool gcDaemonStartup()
+{
+    dvmInitMutex(&gHs->gcThreadMutex);
+    dvmInitCondForTimedWait(&gHs->gcThreadCond);
+    gHs->gcThreadShutdown = false;
+    gHs->hasGcThread = dvmCreateInternalThread(&gHs->gcThread, "GC",
+                                               gcDaemonThread, NULL);
+    return gHs->hasGcThread;
+}
+
+static void gcDaemonShutdown()
+{
+    if (gHs->hasGcThread) {
+        dvmLockMutex(&gHs->gcThreadMutex);
+        gHs->gcThreadShutdown = true;
+        dvmSignalCond(&gHs->gcThreadCond);
+        dvmUnlockMutex(&gHs->gcThreadMutex);
+        pthread_join(gHs->gcThread, NULL);
+    }
+}
+
+/*
+ * Create a stack big enough for the worst possible case, where the
+ * heap is perfectly full of the smallest object.
+ * TODO: be better about memory usage; use a smaller stack with
+ *       overflow detection and recovery.
+ */
+static bool allocMarkStack(GcMarkStack *stack, size_t maximumSize)
+{
+    const char *name = "dalvik-mark-stack";
+    void *addr;
+
+    assert(stack != NULL);
+    stack->length = maximumSize * sizeof(Object*) /
+        (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+    addr = dvmAllocRegion(stack->length, PROT_READ | PROT_WRITE, name);
+    if (addr == NULL) {
+        return false;
+    }
+    stack->base = (const Object **)addr;
+    stack->limit = (const Object **)((char *)addr + stack->length);
+    stack->top = NULL;
+    madvise(stack->base, stack->length, MADV_DONTNEED);
+    return true;
+}
+
+static void freeMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    munmap(stack->base, stack->length);
+    memset(stack, 0, sizeof(*stack));
+}
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.  Returns a GcHeap structure
+ * allocated from the heap source.
+ */
+GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
+                             size_t growthLimit)
+{
+    GcHeap *gcHeap = NULL;
+    HeapSource *hs = NULL;
+    mspace msp;
+    size_t length;
+    void *base;
+
+    assert(gHs == NULL);
+
+    if (!(startSize <= growthLimit && growthLimit <= maximumSize)) {
+        ALOGE("Bad heap size parameters (start=%zd, max=%zd, limit=%zd)",
+             startSize, maximumSize, growthLimit);
+        return NULL;
+    }
+
+    /*
+     * Allocate a contiguous region of virtual memory to subdivided
+     * among the heaps managed by the garbage collector.
+     */
+    length = ALIGN_UP_TO_PAGE_SIZE(maximumSize);
+    base = dvmAllocRegion(length, PROT_NONE, gDvm.zygote ? "dalvik-zygote" : "dalvik-heap");
+    if (base == NULL) {
+        dvmAbort();
+    }
+
+    /* Create an unlocked dlmalloc mspace to use as
+     * a heap source.
+     */
+    msp = createMspace(base, kInitialMorecoreStart, startSize);
+    if (msp == NULL) {
+        dvmAbort();
+    }
+
+    gcHeap = (GcHeap *)calloc(1, sizeof(*gcHeap));
+    if (gcHeap == NULL) {
+        LOGE_HEAP("Can't allocate heap descriptor");
+        dvmAbort();
+    }
+
+    hs = (HeapSource *)calloc(1, sizeof(*hs));
+    if (hs == NULL) {
+        LOGE_HEAP("Can't allocate heap source");
+        dvmAbort();
+    }
+
+    hs->targetUtilization = gDvm.heapTargetUtilization * HEAP_UTILIZATION_MAX;
+    hs->minFree = gDvm.heapMinFree;
+    hs->maxFree = gDvm.heapMaxFree;
+    hs->startSize = startSize;
+    hs->maximumSize = maximumSize;
+    hs->growthLimit = growthLimit;
+    hs->idealSize = startSize;
+    hs->softLimit = SIZE_MAX;    // no soft limit at first
+    hs->numHeaps = 0;
+    hs->sawZygote = gDvm.zygote;
+    hs->nativeBytesAllocated = 0;
+    hs->nativeFootprintGCWatermark = startSize;
+    hs->nativeFootprintLimit = startSize * 2;
+    hs->nativeNeedToRunFinalization = false;
+    hs->hasGcThread = false;
+    hs->heapBase = (char *)base;
+    hs->heapLength = length;
+
+    if (hs->maxFree > hs->maximumSize) {
+      hs->maxFree = hs->maximumSize;
+    }
+    if (hs->minFree < CONCURRENT_START) {
+      hs->minFree = CONCURRENT_START;
+    } else if (hs->minFree > hs->maxFree) {
+      hs->minFree = hs->maxFree;
+    }
+
+    if (!addInitialHeap(hs, msp, growthLimit)) {
+        LOGE_HEAP("Can't add initial heap");
+        dvmAbort();
+    }
+    if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
+        LOGE_HEAP("Can't create liveBits");
+        dvmAbort();
+    }
+    if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
+        LOGE_HEAP("Can't create markBits");
+        dvmHeapBitmapDelete(&hs->liveBits);
+        dvmAbort();
+    }
+    if (!allocMarkStack(&gcHeap->markContext.stack, hs->maximumSize)) {
+        ALOGE("Can't create markStack");
+        dvmHeapBitmapDelete(&hs->markBits);
+        dvmHeapBitmapDelete(&hs->liveBits);
+        dvmAbort();
+    }
+    gcHeap->markContext.bitmap = &hs->markBits;
+    gcHeap->heapSource = hs;
+
+    gHs = hs;
+    return gcHeap;
+}
+
+bool dvmHeapSourceStartupAfterZygote()
+{
+    return gDvm.concurrentMarkSweep ? gcDaemonStartup() : true;
+}
+
+/*
+ * This is called while in zygote mode, right before we fork() for the
+ * first time.  We create a heap for all future zygote process allocations,
+ * in an attempt to avoid touching pages in the zygote heap.  (This would
+ * probably be unnecessary if we had a compacting GC -- the source of our
+ * troubles is small allocations filling in the gaps from larger ones.)
+ */
+bool dvmHeapSourceStartupBeforeFork()
+{
+    HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
+
+    HS_BOILERPLATE();
+
+    assert(gDvm.zygote);
+
+    if (!gDvm.newZygoteHeapAllocated) {
+       /* Ensure heaps are trimmed to minimize footprint pre-fork.
+        */
+        trimHeaps();
+        /* Create a new heap for post-fork zygote allocations.  We only
+         * try once, even if it fails.
+         */
+        ALOGV("Splitting out new zygote heap");
+        gDvm.newZygoteHeapAllocated = true;
+        return addNewHeap(hs);
+    }
+    return true;
+}
+
+void dvmHeapSourceThreadShutdown()
+{
+    if (gDvm.gcHeap != NULL && gDvm.concurrentMarkSweep) {
+        gcDaemonShutdown();
+    }
+}
+
+/*
+ * Tears down the entire GcHeap structure and all of the substructures
+ * attached to it.  This call has the side effect of setting the given
+ * gcHeap pointer and gHs to NULL.
+ */
+void dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+    assert(gcHeap != NULL);
+    if (*gcHeap != NULL && (*gcHeap)->heapSource != NULL) {
+        HeapSource *hs = (*gcHeap)->heapSource;
+        dvmHeapBitmapDelete(&hs->liveBits);
+        dvmHeapBitmapDelete(&hs->markBits);
+        freeMarkStack(&(*gcHeap)->markContext.stack);
+        munmap(hs->heapBase, hs->heapLength);
+        free(hs);
+        gHs = NULL;
+        free(*gcHeap);
+        *gcHeap = NULL;
+    }
+}
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase()
+{
+    return gHs->heapBase;
+}
+
+/*
+ * Returns a high water mark, between base and limit all objects must have been
+ * allocated.
+ */
+void *dvmHeapSourceGetLimit()
+{
+    HeapSource *hs = gHs;
+    void *max_brk = hs->heaps[0].brk;
+
+#ifndef NDEBUG
+    for (size_t i = 1; i < hs->numHeaps; i++) {
+        Heap *const heap = &hs->heaps[i];
+        void *heap_brk = heap->brk;
+        assert (max_brk > heap_brk);
+    }
+#endif
+    return max_brk;
+}
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec, size_t perHeapStats[],
+                             size_t arrayLen)
+{
+    HeapSource *hs = gHs;
+    size_t value = 0;
+    size_t total = 0;
+
+    HS_BOILERPLATE();
+
+    assert(arrayLen >= hs->numHeaps || perHeapStats == NULL);
+    for (size_t i = 0; i < hs->numHeaps; i++) {
+        Heap *const heap = &hs->heaps[i];
+
+        switch (spec) {
+        case HS_FOOTPRINT:
+            value = heap->brk - heap->base;
+            assert(value == mspace_footprint(heap->msp));
+            break;
+        case HS_ALLOWED_FOOTPRINT:
+            value = mspace_footprint_limit(heap->msp);
+            break;
+        case HS_BYTES_ALLOCATED:
+            value = heap->bytesAllocated;
+            break;
+        case HS_OBJECTS_ALLOCATED:
+            value = heap->objectsAllocated;
+            break;
+        default:
+            // quiet gcc
+            break;
+        }
+        if (perHeapStats) {
+            perHeapStats[i] = value;
+        }
+        total += value;
+    }
+    return total;
+}
+
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    assert(numHeaps <= hs->numHeaps);
+    for (size_t i = 0; i < numHeaps; ++i) {
+        base[i] = (uintptr_t)hs->heaps[i].base;
+        max[i] = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+    }
+}
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits()
+{
+    HS_BOILERPLATE();
+
+    return &gHs->liveBits;
+}
+
+/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits()
+{
+    HS_BOILERPLATE();
+
+    return &gHs->markBits;
+}
+
+void dvmHeapSourceSwapBitmaps()
+{
+    HeapBitmap tmp = gHs->liveBits;
+    gHs->liveBits = gHs->markBits;
+    gHs->markBits = tmp;
+}
+
+void dvmHeapSourceZeroMarkBitmap()
+{
+    HS_BOILERPLATE();
+
+    dvmHeapBitmapZero(&gHs->markBits);
+}
+
+void dvmMarkImmuneObjects(const char *immuneLimit)
+{
+    /*
+     * Copy the contents of the live bit vector for immune object
+     * range into the mark bit vector.
+     */
+    /* The only values generated by dvmHeapSourceGetImmuneLimit() */
+    assert(immuneLimit == gHs->heaps[0].base ||
+           immuneLimit == NULL);
+    assert(gHs->liveBits.base == gHs->markBits.base);
+    assert(gHs->liveBits.bitsLen == gHs->markBits.bitsLen);
+    /* heap[0] is never immune */
+    assert(gHs->heaps[0].base >= immuneLimit);
+    assert(gHs->heaps[0].limit > immuneLimit);
+
+    for (size_t i = 1; i < gHs->numHeaps; ++i) {
+        if (gHs->heaps[i].base < immuneLimit) {
+            assert(gHs->heaps[i].limit <= immuneLimit);
+            /* Compute the number of words to copy in the bitmap. */
+            size_t index = HB_OFFSET_TO_INDEX(
+                (uintptr_t)gHs->heaps[i].base - gHs->liveBits.base);
+            /* Compute the starting offset in the live and mark bits. */
+            char *src = (char *)(gHs->liveBits.bits + index);
+            char *dst = (char *)(gHs->markBits.bits + index);
+            /* Compute the number of bytes of the live bitmap to copy. */
+            size_t length = HB_OFFSET_TO_BYTE_INDEX(
+                gHs->heaps[i].limit - gHs->heaps[i].base);
+            /* Do the copy. */
+            memcpy(dst, src, length);
+            /* Make sure max points to the address of the highest set bit. */
+            if (gHs->markBits.max < (uintptr_t)gHs->heaps[i].limit) {
+                gHs->markBits.max = (uintptr_t)gHs->heaps[i].limit;
+            }
+        }
+    }
+}
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void* dvmHeapSourceAlloc(size_t n)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+    if (heap->bytesAllocated + n > hs->softLimit) {
+        /*
+         * This allocation would push us over the soft limit; act as
+         * if the heap is full.
+         */
+        LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation",
+                  FRACTIONAL_MB(hs->softLimit), n);
+        return NULL;
+    }
+    void* ptr;
+    if (gDvm.lowMemoryMode) {
+        /* This is only necessary because mspace_calloc always memsets the
+         * allocated memory to 0. This is bad for memory usage since it leads
+         * to dirty zero pages. If low memory mode is enabled, we use
+         * mspace_malloc which doesn't memset the allocated memory and madvise
+         * the page aligned region back to the kernel.
+         */
+        ptr = mspace_malloc(heap->msp, n);
+        if (ptr == NULL) {
+            return NULL;
+        }
+        uintptr_t zero_begin = (uintptr_t)ptr;
+        uintptr_t zero_end = (uintptr_t)ptr + n;
+        /* Calculate the page aligned region.
+         */
+        uintptr_t begin = ALIGN_UP_TO_PAGE_SIZE(zero_begin);
+        uintptr_t end = zero_end & ~(uintptr_t)(SYSTEM_PAGE_SIZE - 1);
+        /* If our allocation spans more than one page, we attempt to madvise.
+         */
+        if (begin < end) {
+            /* madvise the page aligned region to kernel.
+             */
+            madvise((void*)begin, end - begin, MADV_DONTNEED);
+            /* Zero the region after the page aligned region.
+             */
+            memset((void*)end, 0, zero_end - end);
+            /* Zero out the region before the page aligned region.
+             */
+            zero_end = begin;
+        }
+        memset((void*)zero_begin, 0, zero_end - zero_begin);
+    } else {
+        ptr = mspace_calloc(heap->msp, 1, n);
+        if (ptr == NULL) {
+            return NULL;
+        }
+    }
+
+    countAllocation(heap, ptr);
+    /*
+     * Check to see if a concurrent GC should be initiated.
+     */
+    if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
+        /*
+         * The garbage collector thread is already running or has yet
+         * to be started.  Do nothing.
+         */
+        return ptr;
+    }
+    if (heap->bytesAllocated > heap->concurrentStartBytes) {
+        /*
+         * We have exceeded the allocation threshold.  Wake up the
+         * garbage collector.
+         */
+        dvmSignalCond(&gHs->gcThreadCond);
+    }
+    return ptr;
+}
+
+/* Remove any hard limits, try to allocate, and shrink back down.
+ * Last resort when trying to allocate an object.
+ */
+static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
+{
+    /* Grow as much as possible, but don't let the real footprint
+     * go over the absolute max.
+     */
+    size_t max = heap->maximumSize;
+
+    mspace_set_footprint_limit(heap->msp, max);
+    void* ptr = dvmHeapSourceAlloc(n);
+
+    /* Shrink back down as small as possible.  Our caller may
+     * readjust max_allowed to a more appropriate value.
+     */
+    mspace_set_footprint_limit(heap->msp,
+                               mspace_footprint(heap->msp));
+    return ptr;
+}
+
+/*
+ * Allocates <n> bytes of zeroed data, growing as much as possible
+ * if necessary.
+ */
+void* dvmHeapSourceAllocAndGrow(size_t n)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+    void* ptr = dvmHeapSourceAlloc(n);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    size_t oldIdealSize = hs->idealSize;
+    if (isSoftLimited(hs)) {
+        /* We're soft-limited.  Try removing the soft limit to
+         * see if we can allocate without actually growing.
+         */
+        hs->softLimit = SIZE_MAX;
+        ptr = dvmHeapSourceAlloc(n);
+        if (ptr != NULL) {
+            /* Removing the soft limit worked;  fix things up to
+             * reflect the new effective ideal size.
+             */
+            snapIdealFootprint();
+            return ptr;
+        }
+        // softLimit intentionally left at SIZE_MAX.
+    }
+
+    /* We're not soft-limited.  Grow the heap to satisfy the request.
+     * If this call fails, no footprints will have changed.
+     */
+    ptr = heapAllocAndGrow(hs, heap, n);
+    if (ptr != NULL) {
+        /* The allocation succeeded.  Fix up the ideal size to
+         * reflect any footprint modifications that had to happen.
+         */
+        snapIdealFootprint();
+    } else {
+        /* We just couldn't do it.  Restore the original ideal size,
+         * fixing up softLimit if necessary.
+         */
+        setIdealFootprint(oldIdealSize);
+    }
+    return ptr;
+}
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage. The list must contain addresses all in
+ * the same mspace, and must be in increasing order. This implies that
+ * there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
+{
+    HS_BOILERPLATE();
+
+    if (numPtrs == 0) {
+        return 0;
+    }
+
+    assert(ptrs != NULL);
+    assert(*ptrs != NULL);
+    Heap* heap = ptr2heap(gHs, *ptrs);
+    size_t numBytes = 0;
+    if (heap != NULL) {
+        mspace msp = heap->msp;
+        // Calling mspace_free on shared heaps disrupts sharing too
+        // much. For heap[0] -- the 'active heap' -- we call
+        // mspace_free, but on the other heaps we only do some
+        // accounting.
+        if (heap == gHs->heaps) {
+            // Count freed objects.
+            for (size_t i = 0; i < numPtrs; i++) {
+                assert(ptrs[i] != NULL);
+                assert(ptr2heap(gHs, ptrs[i]) == heap);
+                countFree(heap, ptrs[i], &numBytes);
+            }
+            // Bulk free ptrs.
+            mspace_bulk_free(msp, ptrs, numPtrs);
+        } else {
+            // This is not an 'active heap'. Only do the accounting.
+            for (size_t i = 0; i < numPtrs; i++) {
+                assert(ptrs[i] != NULL);
+                assert(ptr2heap(gHs, ptrs[i]) == heap);
+                countFree(heap, ptrs[i], &numBytes);
+            }
+        }
+    }
+    return numBytes;
+}
+
+/*
+ * Returns true iff <ptr> is in the heap source.
+ */
+bool dvmHeapSourceContainsAddress(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    return (dvmHeapSourceGetBase() <= ptr) && (ptr <= dvmHeapSourceGetLimit());
+}
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool dvmHeapSourceContains(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    if (dvmHeapSourceContainsAddress(ptr)) {
+        return dvmHeapBitmapIsObjectBitSet(&gHs->liveBits, ptr) != 0;
+    }
+    return false;
+}
+
+bool dvmIsZygoteObject(const Object* obj)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    if (dvmHeapSourceContains(obj) && hs->sawZygote) {
+        Heap *heap = ptr2heap(hs, obj);
+        if (heap != NULL) {
+            /* If the object is not in the active heap, we assume that
+             * it was allocated as part of zygote.
+             */
+            return heap != hs->heaps;
+        }
+    }
+    /* The pointer is outside of any known heap, or we are not
+     * running in zygote mode.
+     */
+    return false;
+}
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+    HS_BOILERPLATE();
+
+    Heap* heap = ptr2heap(gHs, ptr);
+    if (heap != NULL) {
+        return mspace_usable_size(ptr);
+    }
+    return 0;
+}
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t dvmHeapSourceFootprint()
+{
+    HS_BOILERPLATE();
+
+//TODO: include size of bitmaps?
+    return oldHeapOverhead(gHs, true);
+}
+
+static size_t getMaximumSize(const HeapSource *hs)
+{
+    return hs->growthLimit;
+}
+
+/*
+ * Returns the current maximum size of the heap source respecting any
+ * growth limits.
+ */
+size_t dvmHeapSourceGetMaximumSize()
+{
+    HS_BOILERPLATE();
+    return getMaximumSize(gHs);
+}
+
+/*
+ * Removes any growth limits.  Allows the user to allocate up to the
+ * maximum heap size.
+ */
+void dvmClearGrowthLimit()
+{
+    HS_BOILERPLATE();
+    dvmLockHeap();
+    dvmWaitForConcurrentGcToComplete();
+    gDvm.gcHeap->cardTableLength = gDvm.gcHeap->cardTableMaxLength;
+    gHs->growthLimit = gHs->maximumSize;
+    size_t overhead = oldHeapOverhead(gHs, false);
+    gHs->heaps[0].maximumSize = gHs->maximumSize - overhead;
+    gHs->heaps[0].limit = gHs->heaps[0].base + gHs->heaps[0].maximumSize;
+    dvmUnlockHeap();
+}
+
+/*
+ * Return the real bytes used by old heaps plus the soft usage of the
+ * current heap.  When a soft limit is in effect, this is effectively
+ * what it's compared against (though, in practice, it only looks at
+ * the current heap).
+ */
+static size_t getSoftFootprint(bool includeActive)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t ret = oldHeapOverhead(hs, false);
+    if (includeActive) {
+        ret += hs->heaps[0].bytesAllocated;
+    }
+
+    return ret;
+}
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t dvmHeapSourceGetIdealFootprint()
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    return hs->idealSize;
+}
+
+/*
+ * Sets the soft limit, handling any necessary changes to the allowed
+ * footprint of the active heap.
+ */
+static void setSoftLimit(HeapSource *hs, size_t softLimit)
+{
+    /* Compare against the actual footprint, rather than the
+     * max_allowed, because the heap may not have grown all the
+     * way to the allowed size yet.
+     */
+    mspace msp = hs->heaps[0].msp;
+    size_t currentHeapSize = mspace_footprint(msp);
+    if (softLimit < currentHeapSize) {
+        /* Don't let the heap grow any more, and impose a soft limit.
+         */
+        mspace_set_footprint_limit(msp, currentHeapSize);
+        hs->softLimit = softLimit;
+    } else {
+        /* Let the heap grow to the requested max, and remove any
+         * soft limit, if set.
+         */
+        mspace_set_footprint_limit(msp, softLimit);
+        hs->softLimit = SIZE_MAX;
+    }
+}
+
+/*
+ * Sets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.  Clamps to the appropriate maximum
+ * value.
+ */
+static void setIdealFootprint(size_t max)
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t maximumSize = getMaximumSize(hs);
+    if (max > maximumSize) {
+        LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB",
+                FRACTIONAL_MB(max),
+                FRACTIONAL_MB(maximumSize));
+        max = maximumSize;
+    }
+
+    /* Convert max into a size that applies to the active heap.
+     * Old heaps will count against the ideal size.
+     */
+    size_t overhead = getSoftFootprint(false);
+    size_t activeMax;
+    if (overhead < max) {
+        activeMax = max - overhead;
+    } else {
+        activeMax = 0;
+    }
+
+    setSoftLimit(hs, activeMax);
+    hs->idealSize = max;
+}
+
+/*
+ * Make the ideal footprint equal to the current footprint.
+ */
+static void snapIdealFootprint()
+{
+    HS_BOILERPLATE();
+
+    setIdealFootprint(getSoftFootprint(true));
+}
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization()
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    return (float)hs->targetUtilization / (float)HEAP_UTILIZATION_MAX;
+}
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    /* Clamp it to a reasonable range.
+     */
+    // TODO: This may need some tuning.
+    if (newTarget < 0.2) {
+        newTarget = 0.2;
+    } else if (newTarget > 0.8) {
+        newTarget = 0.8;
+    }
+
+    hs->targetUtilization =
+            (size_t)(newTarget * (float)HEAP_UTILIZATION_MAX);
+    ALOGV("Set heap target utilization to %zd/%d (%f)",
+            hs->targetUtilization, HEAP_UTILIZATION_MAX, newTarget);
+}
+
+/*
+ * Given the size of a live set, returns the ideal heap size given
+ * the current target utilization and MIN/MAX values.
+ */
+static size_t getUtilizationTarget(const HeapSource* hs, size_t liveSize)
+{
+    /* Use the current target utilization ratio to determine the
+     * ideal heap size based on the size of the live set.
+     */
+    size_t targetSize = (liveSize / hs->targetUtilization) * HEAP_UTILIZATION_MAX;
+
+    /* Cap the amount of free space, though, so we don't end up
+     * with, e.g., 8MB of free space when the live set size hits 8MB.
+     */
+    if (targetSize > liveSize + hs->maxFree) {
+        targetSize = liveSize + hs->maxFree;
+    } else if (targetSize < liveSize + hs->minFree) {
+        targetSize = liveSize + hs->minFree;
+    }
+    return targetSize;
+}
+
+/*
+ * Given the current contents of the active heap, increase the allowed
+ * heap footprint to match the target utilization ratio.  This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization()
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    Heap* heap = hs2heap(hs);
+
+    /* Use the current target utilization ratio to determine the
+     * ideal heap size based on the size of the live set.
+     * Note that only the active heap plays any part in this.
+     *
+     * Avoid letting the old heaps influence the target free size,
+     * because they may be full of objects that aren't actually
+     * in the working set.  Just look at the allocated size of
+     * the current heap.
+     */
+    size_t currentHeapUsed = heap->bytesAllocated;
+    size_t targetHeapSize = getUtilizationTarget(hs, currentHeapUsed);
+
+    /* The ideal size includes the old heaps; add overhead so that
+     * it can be immediately subtracted again in setIdealFootprint().
+     * If the target heap size would exceed the max, setIdealFootprint()
+     * will clamp it to a legal value.
+     */
+    size_t overhead = getSoftFootprint(false);
+    setIdealFootprint(targetHeapSize + overhead);
+
+    size_t freeBytes = getAllocLimit(hs);
+    if (freeBytes < CONCURRENT_MIN_FREE) {
+        /* Not enough free memory to allow a concurrent GC. */
+        heap->concurrentStartBytes = SIZE_MAX;
+    } else {
+        heap->concurrentStartBytes = freeBytes - CONCURRENT_START;
+    }
+
+    /* Mark that we need to run finalizers and update the native watermarks
+     * next time we attempt to register a native allocation.
+     */
+    gHs->nativeNeedToRunFinalization = true;
+}
+
+/*
+ * Return free pages to the system.
+ * TODO: move this somewhere else, especially the native heap part.
+ */
+static void releasePagesInRange(void* start, void* end, size_t used_bytes,
+                                void* releasedBytes)
+{
+    if (used_bytes == 0) {
+        /*
+         * We have a range of memory we can try to madvise()
+         * back. Linux requires that the madvise() start address is
+         * page-aligned.  We also align the end address.
+         */
+        start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
+        end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
+        if (end > start) {
+            size_t length = (char *)end - (char *)start;
+            madvise(start, length, MADV_DONTNEED);
+            *(size_t *)releasedBytes += length;
+        }
+    }
+}
+
+/*
+ * Return unused memory to the system if possible.
+ */
+static void trimHeaps()
+{
+    HS_BOILERPLATE();
+
+    HeapSource *hs = gHs;
+    size_t heapBytes = 0;
+    for (size_t i = 0; i < hs->numHeaps; i++) {
+        Heap *heap = &hs->heaps[i];
+
+        /* Return the wilderness chunk to the system. */
+        mspace_trim(heap->msp, 0);
+
+        /* Return any whole free pages to the system. */
+        mspace_inspect_all(heap->msp, releasePagesInRange, &heapBytes);
+    }
+
+    /* Same for the native heap. */
+    size_t nativeBytes = 0;
+#ifdef USE_DLMALLOC
+    dlmalloc_trim(0);
+    dlmalloc_inspect_all(releasePagesInRange, &nativeBytes);
+#endif
+
+    LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes",
+            heapBytes, nativeBytes, heapBytes + nativeBytes);
+}
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg)
+{
+    HS_BOILERPLATE();
+
+    /* Walk the heaps from oldest to newest.
+     */
+//TODO: do this in address order
+    HeapSource *hs = gHs;
+    for (size_t i = hs->numHeaps; i > 0; --i) {
+        mspace_inspect_all(hs->heaps[i-1].msp, callback, arg);
+        callback(NULL, NULL, 0, arg);  // Indicate end of a heap.
+    }
+}
+
+/*
+ * Gets the number of heaps available in the heap source.
+ *
+ * Caller must hold the heap lock, because gHs caches a field
+ * in gDvm.gcHeap.
+ */
+size_t dvmHeapSourceGetNumHeaps()
+{
+    HS_BOILERPLATE();
+
+    return gHs->numHeaps;
+}
+
+void *dvmHeapSourceGetImmuneLimit(bool isPartial)
+{
+    if (isPartial) {
+        return hs2heap(gHs)->base;
+    } else {
+        return NULL;
+    }
+}
+
+static void dvmHeapSourceUpdateMaxNativeFootprint()
+{
+    /* Use the current target utilization ratio to determine the new native GC
+     * watermarks.
+     */
+    size_t nativeSize = gHs->nativeBytesAllocated;
+    size_t targetSize =
+        (nativeSize / gHs->targetUtilization) * HEAP_UTILIZATION_MAX;
+
+    if (targetSize > nativeSize + gHs->maxFree) {
+        targetSize = nativeSize + gHs->maxFree;
+    } else if (targetSize < nativeSize + gHs->minFree) {
+        targetSize = nativeSize + gHs->minFree;
+    }
+    gHs->nativeFootprintGCWatermark = targetSize;
+    gHs->nativeFootprintLimit = 2 * targetSize - nativeSize;
+}
+
+void dvmHeapSourceRegisterNativeAllocation(int bytes)
+{
+    /* If we have just done a GC, ensure that the finalizers are done and update
+     * the native watermarks.
+     */
+    if (gHs->nativeNeedToRunFinalization) {
+        dvmRunFinalization();
+        dvmHeapSourceUpdateMaxNativeFootprint();
+        gHs->nativeNeedToRunFinalization = false;
+    }
+
+    android_atomic_add(bytes, &gHs->nativeBytesAllocated);
+
+    if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintGCWatermark) {
+        /* The second watermark is higher than the gc watermark. If you hit
+         * this it means you are allocating native objects faster than the GC
+         * can keep up with. If this occurs, we do a GC for alloc.
+         */
+        if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintLimit) {
+            Thread* self = dvmThreadSelf();
+            dvmRunFinalization();
+            if (dvmCheckException(self)) {
+                return;
+            }
+            dvmLockHeap();
+            bool waited = dvmWaitForConcurrentGcToComplete();
+            dvmUnlockHeap();
+            if (waited) {
+                // Just finished a GC, attempt to run finalizers.
+                dvmRunFinalization();
+                if (dvmCheckException(self)) {
+                    return;
+                }
+            }
+
+            // If we still are over the watermark, attempt a GC for alloc and run finalizers.
+            if ((size_t)gHs->nativeBytesAllocated > gHs->nativeFootprintLimit) {
+                dvmLockHeap();
+                dvmWaitForConcurrentGcToComplete();
+                dvmCollectGarbageInternal(GC_FOR_MALLOC);
+                dvmUnlockHeap();
+                dvmRunFinalization();
+                gHs->nativeNeedToRunFinalization = false;
+                if (dvmCheckException(self)) {
+                    return;
+                }
+            }
+            /* We have just run finalizers, update the native watermark since
+             * it is very likely that finalizers released native managed
+             * allocations.
+             */
+            dvmHeapSourceUpdateMaxNativeFootprint();
+        } else {
+            dvmSignalCond(&gHs->gcThreadCond);
+        }
+    }
+}
+
+/*
+ * Called from VMRuntime.registerNativeFree.
+ */
+void dvmHeapSourceRegisterNativeFree(int bytes)
+{
+    int expected_size, new_size;
+    do {
+        expected_size = gHs->nativeBytesAllocated;
+        new_size = expected_size - bytes;
+        if (new_size < 0) {
+            break;
+        }
+    } while (android_atomic_cas(expected_size, new_size,
+                                &gHs->nativeBytesAllocated));
+}
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
new file mode 100644
index 0000000..42fccb2
--- /dev/null
+++ b/vm/alloc/HeapSource.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef DALVIK_HEAP_SOURCE_H_
+#define DALVIK_HEAP_SOURCE_H_
+
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h" // for GcHeap
+
+/* dlmalloc uses one size_t per allocated chunk.
+ */
+#define HEAP_SOURCE_CHUNK_OVERHEAD         (1 * sizeof (size_t))
+
+/* The largest number of separate heaps we can handle.
+ */
+#define HEAP_SOURCE_MAX_HEAP_COUNT 2
+
+enum HeapSourceValueSpec {
+    HS_FOOTPRINT,
+    HS_ALLOWED_FOOTPRINT,
+    HS_BYTES_ALLOCATED,
+    HS_OBJECTS_ALLOCATED
+};
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.
+ */
+GcHeap *dvmHeapSourceStartup(size_t startingSize,
+                             size_t maximumSize,
+                             size_t growthLimit);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create a new heap for post-zygote allocations.
+ * Having a separate heap should maximize the number of pages
+ * that a given app_process shares with the zygote process.
+ */
+bool dvmHeapSourceStartupAfterZygote(void);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create an additional zygote heap before the first fork().
+ * Having a separate heap should reduce the number of shared
+ * pages subsequently touched by the zygote process.
+ */
+bool dvmHeapSourceStartupBeforeFork(void);
+
+/*
+ * Shutdown any threads internal to the heap source.  This should be
+ * called before the heap source itself is shutdown.
+ */
+void dvmHeapSourceThreadShutdown(void);
+
+/*
+ * Tears down the heap source and frees any resources associated with it.
+ */
+void dvmHeapSourceShutdown(GcHeap **gcHeap);
+
+/*
+ * Returns the base and inclusive max addresses of the heap source
+ * heaps.  The base and max values are suitable for passing directly
+ * to the bitmap sweeping routine.
+ */
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps);
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void);
+
+/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits(void);
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void);
+
+/*
+ * Returns a high water mark, between base and limit all objects must have been
+ * allocated.
+ */
+void *dvmHeapSourceGetLimit(void);
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ */
+size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec,
+                             size_t perHeapStats[], size_t arrayLen);
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void *dvmHeapSourceAlloc(size_t n);
+
+/*
+ * Allocates <n> bytes of zeroed data, growing up to absoluteMaxSize
+ * if necessary.
+ */
+void *dvmHeapSourceAllocAndGrow(size_t n);
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage.  The list must contain addresses all
+ * in the same mspace, and must be in increasing order. This implies
+ * that there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs);
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool dvmHeapSourceContains(const void *ptr);
+
+/*
+ * Returns true iff <ptr> is within the address space managed by heap source.
+ */
+bool dvmHeapSourceContainsAddress(const void *ptr);
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t dvmHeapSourceChunkSize(const void *ptr);
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ */
+size_t dvmHeapSourceFootprint(void);
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t dvmHeapSourceGetIdealFootprint(void);
+
+/*
+ * Given the current contents of the heap, increase the allowed
+ * heap footprint to match the target utilization ratio.  This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization(void);
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
+                                       size_t used_bytes, void* arg),
+                       void *arg);
+/*
+ * Gets the number of heaps available in the heap source.
+ */
+size_t dvmHeapSourceGetNumHeaps(void);
+
+/*
+ * Exchanges the mark and object bitmaps.
+ */
+void dvmHeapSourceSwapBitmaps(void);
+
+/*
+ * Zeroes the mark bitmap.
+ */
+void dvmHeapSourceZeroMarkBitmap(void);
+
+/*
+ * Marks all objects inside the immune region of the heap. Addresses
+ * at or above this pointer are threatened, addresses below this
+ * pointer are immune.
+ */
+void dvmMarkImmuneObjects(const char *immuneLimit);
+
+/*
+ * Returns a pointer that demarcates the threatened region of the
+ * heap.  Addresses at or above this pointer are threatened, addresses
+ * below this pointer are immune.
+ */
+void *dvmHeapSourceGetImmuneLimit(bool isPartial);
+
+/*
+ * Returns the maximum size of the heap.  This value will be either
+ * the value of -Xmx or a user supplied growth limit.
+ */
+size_t dvmHeapSourceGetMaximumSize(void);
+
+/*
+ * Called from VMRuntime.registerNativeAllocation.
+ */
+void dvmHeapSourceRegisterNativeAllocation(int bytes);
+
+/*
+ * Called from VMRuntime.registerNativeFree.
+ */
+void dvmHeapSourceRegisterNativeFree(int bytes);
+
+#endif  // DALVIK_HEAP_SOURCE_H_
diff --git a/vm/alloc/MarkSweep.cpp b/vm/alloc/MarkSweep.cpp
new file mode 100644
index 0000000..008b404
--- /dev/null
+++ b/vm/alloc/MarkSweep.cpp
@@ -0,0 +1,942 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "Dalvik.h"
+#include "alloc/CardTable.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapBitmapInlines.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
+#include <limits.h>     // for ULONG_MAX
+#include <sys/mman.h>   // for madvise(), mmap()
+#include <errno.h>
+
+/*
+ * Returns true if the given object is marked.
+ */
+static bool isMarked(const Object *obj, const GcMarkContext *ctx)
+{
+    return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
+}
+
+/*
+ * Initializes the stack top and advises the mark stack pages as needed.
+ */
+static bool createMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    size_t length = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
+        (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+    madvise(stack->base, length, MADV_NORMAL);
+    stack->top = stack->base;
+    return true;
+}
+
+/*
+ * Assigns NULL to the stack top and advises the mark stack pages as
+ * not needed.
+ */
+static void destroyMarkStack(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    madvise(stack->base, stack->length, MADV_DONTNEED);
+    stack->top = NULL;
+}
+
+/*
+ * Pops an object from the mark stack.
+ */
+static void markStackPush(GcMarkStack *stack, const Object *obj)
+{
+    assert(stack != NULL);
+    assert(stack->base <= stack->top);
+    assert(stack->limit > stack->top);
+    assert(obj != NULL);
+    *stack->top = obj;
+    ++stack->top;
+}
+
+/*
+ * Pushes an object on the mark stack.
+ */
+static const Object *markStackPop(GcMarkStack *stack)
+{
+    assert(stack != NULL);
+    assert(stack->base < stack->top);
+    assert(stack->limit > stack->top);
+    --stack->top;
+    return *stack->top;
+}
+
+bool dvmHeapBeginMarkStep(bool isPartial)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    if (!createMarkStack(&ctx->stack)) {
+        return false;
+    }
+    ctx->finger = NULL;
+    ctx->immuneLimit = (char*)dvmHeapSourceGetImmuneLimit(isPartial);
+    return true;
+}
+
+static long setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
+{
+    return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
+}
+
+static void markObjectNonNull(const Object *obj, GcMarkContext *ctx,
+                              bool checkFinger)
+{
+    assert(ctx != NULL);
+    assert(obj != NULL);
+    assert(dvmIsValidObject(obj));
+    if (obj < (Object *)ctx->immuneLimit) {
+        assert(isMarked(obj, ctx));
+        return;
+    }
+    if (!setAndReturnMarkBit(ctx, obj)) {
+        /* This object was not previously marked.
+         */
+        if (checkFinger && (void *)obj < ctx->finger) {
+            /* This object will need to go on the mark stack.
+             */
+            markStackPush(&ctx->stack, obj);
+        }
+    }
+}
+
+/* Used to mark objects when recursing.  Recursion is done by moving
+ * the finger across the bitmaps in address order and marking child
+ * objects.  Any newly-marked objects whose addresses are lower than
+ * the finger won't be visited by the bitmap scan, so those objects
+ * need to be added to the mark stack.
+ */
+static void markObject(const Object *obj, GcMarkContext *ctx)
+{
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, true);
+    }
+}
+
+/*
+ * Callback applied to root references during the initial root
+ * marking.  Marks white objects but does not push them on the mark
+ * stack.
+ */
+static void rootMarkObjectVisitor(void *addr, u4 thread, RootType type,
+                                  void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, false);
+    }
+}
+
+/* Mark the set of root objects.
+ *
+ * Things we need to scan:
+ * - System classes defined by root classloader
+ * - For each thread:
+ *   - Interpreted stack, from top to "curFrame"
+ *     - Dalvik registers (args + local vars)
+ *   - JNI local references
+ *   - Automatic VM local references (TrackedAlloc)
+ *   - Associated Thread/VMThread object
+ *   - ThreadGroups (could track & start with these instead of working
+ *     upward from Threads)
+ *   - Exception currently being thrown, if present
+ * - JNI global references
+ * - Interned string table
+ * - Primitive classes
+ * - Special objects
+ *   - gDvm.outOfMemoryObj
+ * - Objects in debugger object registry
+ *
+ * Don't need:
+ * - Native stack (for in-progress stuff in the VM)
+ *   - The TrackedAlloc stuff watches all native VM references.
+ */
+void dvmHeapMarkRootSet()
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
+    dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
+}
+
+/*
+ * Callback applied to root references during root remarking.  Marks
+ * white objects and pushes them on the mark stack.
+ */
+static void rootReMarkObjectVisitor(void *addr, u4 thread, RootType type,
+                                    void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj != NULL) {
+        markObjectNonNull(obj, ctx, true);
+    }
+}
+
+/*
+ * Grays all references in the roots.
+ */
+void dvmHeapReMarkRootSet()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    assert(ctx->finger == (void *)ULONG_MAX);
+    dvmVisitRoots(rootReMarkObjectVisitor, ctx);
+}
+
+/*
+ * Scans instance fields.
+ */
+static void scanFields(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+        unsigned int refOffsets = obj->clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object *ref = dvmGetFieldObject(obj, offset);
+            markObject(ref, ctx);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (ClassObject *clazz = obj->clazz;
+             clazz != NULL;
+             clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                void *addr = BYTE_OFFSET(obj, field->byteOffset);
+                Object *ref = ((JValue *)addr)->l;
+                markObject(ref, ctx);
+            }
+        }
+    }
+}
+
+/*
+ * Scans the static fields of a class object.
+ */
+static void scanStaticFields(const ClassObject *clazz, GcMarkContext *ctx)
+{
+    assert(clazz != NULL);
+    assert(ctx != NULL);
+    for (int i = 0; i < clazz->sfieldCount; ++i) {
+        char ch = clazz->sfields[i].signature[0];
+        if (ch == '[' || ch == 'L') {
+            Object *obj = clazz->sfields[i].value.l;
+            markObject(obj, ctx);
+        }
+    }
+}
+
+/*
+ * Visit the interfaces of a class object.
+ */
+static void scanInterfaces(const ClassObject *clazz, GcMarkContext *ctx)
+{
+    assert(clazz != NULL);
+    assert(ctx != NULL);
+    for (int i = 0; i < clazz->interfaceCount; ++i) {
+        markObject((const Object *)clazz->interfaces[i], ctx);
+    }
+}
+
+/*
+ * Scans the header, static field references, and interface
+ * pointers of a class object.
+ */
+static void scanClassObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(dvmIsClassObject(obj));
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    const ClassObject *asClass = (const ClassObject *)obj;
+    if (IS_CLASS_FLAG_SET(asClass, CLASS_ISARRAY)) {
+        markObject((const Object *)asClass->elementClass, ctx);
+    }
+    /* Do super and the interfaces contain Objects and not dex idx values? */
+    if (asClass->status > CLASS_IDX) {
+        markObject((const Object *)asClass->super, ctx);
+    }
+    markObject((const Object *)asClass->classLoader, ctx);
+    scanFields(obj, ctx);
+    scanStaticFields(asClass, ctx);
+    if (asClass->status > CLASS_IDX) {
+        scanInterfaces(asClass, ctx);
+    }
+}
+
+/*
+ * Scans the header of all array objects.  If the array object is
+ * specialized to a reference type, scans the array data as well.
+ */
+static void scanArrayObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+        const ArrayObject *array = (const ArrayObject *)obj;
+        const Object **contents = (const Object **)(void *)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            markObject(contents[i], ctx);
+        }
+    }
+}
+
+/*
+ * Returns class flags relating to Reference subclasses.
+ */
+static int referenceClassFlags(const Object *obj)
+{
+    int flags = CLASS_ISREFERENCE |
+                CLASS_ISWEAKREFERENCE |
+                CLASS_ISFINALIZERREFERENCE |
+                CLASS_ISPHANTOMREFERENCE;
+    return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+/*
+ * Returns true if the object derives from SoftReference.
+ */
+static bool isSoftReference(const Object *obj)
+{
+    return referenceClassFlags(obj) == CLASS_ISREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from WeakReference.
+ */
+static bool isWeakReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from FinalizerReference.
+ */
+static bool isFinalizerReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISFINALIZERREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from PhantomReference.
+ */
+static bool isPhantomReference(const Object *obj)
+{
+    return referenceClassFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+
+/*
+ * Adds a reference to the tail of a circular queue of references.
+ */
+static void enqueuePendingReference(Object *ref, Object **list)
+{
+    assert(ref != NULL);
+    assert(list != NULL);
+    size_t offset = gDvm.offJavaLangRefReference_pendingNext;
+    if (*list == NULL) {
+        dvmSetFieldObject(ref, offset, ref);
+        *list = ref;
+    } else {
+        Object *head = dvmGetFieldObject(*list, offset);
+        dvmSetFieldObject(ref, offset, head);
+        dvmSetFieldObject(*list, offset, ref);
+    }
+}
+
+/*
+ * Removes the reference at the head of a circular queue of
+ * references.
+ */
+static Object *dequeuePendingReference(Object **list)
+{
+    assert(list != NULL);
+    assert(*list != NULL);
+    size_t offset = gDvm.offJavaLangRefReference_pendingNext;
+    Object *head = dvmGetFieldObject(*list, offset);
+    Object *ref;
+    if (*list == head) {
+        ref = *list;
+        *list = NULL;
+    } else {
+        Object *next = dvmGetFieldObject(head, offset);
+        dvmSetFieldObject(*list, offset, next);
+        ref = head;
+    }
+    dvmSetFieldObject(ref, offset, NULL);
+    return ref;
+}
+
+/*
+ * Process the "referent" field in a java.lang.ref.Reference.  If the
+ * referent has not yet been marked, put it on the appropriate list in
+ * the gcHeap for later processing.
+ */
+static void delayReferenceReferent(Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
+    assert(ctx != NULL);
+    GcHeap *gcHeap = gDvm.gcHeap;
+    size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    Object *pending = dvmGetFieldObject(obj, pendingNextOffset);
+    Object *referent = dvmGetFieldObject(obj, referentOffset);
+    if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
+        Object **list = NULL;
+        if (isSoftReference(obj)) {
+            list = &gcHeap->softReferences;
+        } else if (isWeakReference(obj)) {
+            list = &gcHeap->weakReferences;
+        } else if (isFinalizerReference(obj)) {
+            list = &gcHeap->finalizerReferences;
+        } else if (isPhantomReference(obj)) {
+            list = &gcHeap->phantomReferences;
+        }
+        assert(list != NULL);
+        enqueuePendingReference(obj, list);
+    }
+}
+
+/*
+ * Scans the header and field references of a data object.
+ */
+static void scanDataObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(ctx != NULL);
+    markObject((const Object *)obj->clazz, ctx);
+    scanFields(obj, ctx);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        delayReferenceReferent((Object *)obj, ctx);
+    }
+}
+
+/*
+ * Scans an object reference.  Determines the type of the reference
+ * and dispatches to a specialized scanning routine.
+ */
+static void scanObject(const Object *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        scanClassObject(obj, ctx);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        scanArrayObject(obj, ctx);
+    } else {
+        scanDataObject(obj, ctx);
+    }
+}
+
+/*
+ * Scan anything that's on the mark stack.  We can't use the bitmaps
+ * anymore, so use a finger that points past the end of them.
+ */
+static void processMarkStack(GcMarkContext *ctx)
+{
+    assert(ctx != NULL);
+    assert(ctx->finger == (void *)ULONG_MAX);
+    assert(ctx->stack.top >= ctx->stack.base);
+    GcMarkStack *stack = &ctx->stack;
+    while (stack->top > stack->base) {
+        const Object *obj = markStackPop(stack);
+        scanObject(obj, ctx);
+    }
+}
+
+static size_t objectSize(const Object *obj)
+{
+    assert(dvmIsValidObject(obj));
+    assert(dvmIsValidObject((Object *)obj->clazz));
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        return dvmArrayObjectSize((ArrayObject *)obj);
+    } else if (obj->clazz == gDvm.classJavaLangClass) {
+        return dvmClassObjectSize((ClassObject *)obj);
+    } else {
+        return obj->clazz->objectSize;
+    }
+}
+
+/*
+ * Scans forward to the header of the next marked object between start
+ * and limit.  Returns NULL if no marked objects are in that region.
+ */
+static Object *nextGrayObject(const u1 *base, const u1 *limit,
+                              const HeapBitmap *markBits)
+{
+    const u1 *ptr;
+
+    assert(base < limit);
+    assert(limit - base <= GC_CARD_SIZE);
+    for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) {
+        if (dvmHeapBitmapIsObjectBitSet(markBits, ptr))
+            return (Object *)ptr;
+    }
+    return NULL;
+}
+
+/*
+ * Scans range of dirty cards between start and end.  A range of dirty
+ * cards is composed consecutively dirty cards or dirty cards spanned
+ * by a gray object.  Returns the address of a clean card if the scan
+ * reached a clean card or NULL if the scan reached the end.
+ */
+const u1 *scanDirtyCards(const u1 *start, const u1 *end,
+                         GcMarkContext *ctx)
+{
+    const HeapBitmap *markBits = ctx->bitmap;
+    const u1 *card = start, *prevAddr = NULL;
+    while (card < end) {
+        if (*card != GC_CARD_DIRTY) {
+            return card;
+        }
+        const u1 *ptr = prevAddr ? prevAddr : (u1*)dvmAddrFromCard(card);
+        const u1 *limit = ptr + GC_CARD_SIZE;
+        while (ptr < limit) {
+            Object *obj = nextGrayObject(ptr, limit, markBits);
+            if (obj == NULL) {
+                break;
+            }
+            scanObject(obj, ctx);
+            ptr = (u1*)obj + ALIGN_UP(objectSize(obj), HB_OBJECT_ALIGNMENT);
+        }
+        if (ptr < limit) {
+            /* Ended within the current card, advance to the next card. */
+            ++card;
+            prevAddr = NULL;
+        } else {
+            /* Ended past the current card, skip ahead. */
+            card = dvmCardFromAddr(ptr);
+            prevAddr = ptr;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Blackens gray objects found on dirty cards.
+ */
+static void scanGrayObjects(GcMarkContext *ctx)
+{
+    GcHeap *h = gDvm.gcHeap;
+    const u1 *base, *limit, *ptr, *dirty;
+
+    base = &h->cardTableBase[0];
+    // The limit is the card one after the last accessible card.
+    limit = dvmCardFromAddr((u1 *)dvmHeapSourceGetLimit() - GC_CARD_SIZE) + 1;
+    assert(limit <= &base[h->cardTableOffset + h->cardTableLength]);
+
+    ptr = base;
+    for (;;) {
+        dirty = (const u1 *)memchr(ptr, GC_CARD_DIRTY, limit - ptr);
+        if (dirty == NULL) {
+            break;
+        }
+        assert((dirty > ptr) && (dirty < limit));
+        ptr = scanDirtyCards(dirty, limit, ctx);
+        if (ptr == NULL) {
+            break;
+        }
+        assert((ptr > dirty) && (ptr < limit));
+    }
+}
+
+/*
+ * Callback for scanning each object in the bitmap.  The finger is set
+ * to the address corresponding to the lowest address in the next word
+ * of bits in the bitmap.
+ */
+static void scanBitmapCallback(Object *obj, void *finger, void *arg)
+{
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    ctx->finger = (void *)finger;
+    scanObject(obj, ctx);
+}
+
+/* Given bitmaps with the root set marked, find and mark all
+ * reachable objects.  When this returns, the entire set of
+ * live objects will be marked and the mark stack will be empty.
+ */
+void dvmHeapScanMarkedObjects(void)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    assert(ctx->finger == NULL);
+
+    /* The bitmaps currently have bits set for the root set.
+     * Walk across the bitmaps and scan each object.
+     */
+    dvmHeapBitmapScanWalk(ctx->bitmap, scanBitmapCallback, ctx);
+
+    ctx->finger = (void *)ULONG_MAX;
+
+    /* We've walked the mark bitmaps.  Scan anything that's
+     * left on the mark stack.
+     */
+    processMarkStack(ctx);
+}
+
+void dvmHeapReScanMarkedObjects()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    /*
+     * The finger must have been set to the maximum value to ensure
+     * that gray objects will be pushed onto the mark stack.
+     */
+    assert(ctx->finger == (void *)ULONG_MAX);
+    scanGrayObjects(ctx);
+    processMarkStack(ctx);
+}
+
+/*
+ * Clear the referent field.
+ */
+static void clearReference(Object *reference)
+{
+    size_t offset = gDvm.offJavaLangRefReference_referent;
+    dvmSetFieldObject(reference, offset, NULL);
+}
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * and has not yet been enqueued.
+ */
+static bool isEnqueuable(const Object *reference)
+{
+    assert(reference != NULL);
+    Object *queue = dvmGetFieldObject(reference,
+            gDvm.offJavaLangRefReference_queue);
+    Object *queueNext = dvmGetFieldObject(reference,
+            gDvm.offJavaLangRefReference_queueNext);
+    return queue != NULL && queueNext == NULL;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+    assert(ref != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);
+}
+
+/*
+ * Walks the reference list marking any references subject to the
+ * reference clearing policy.  References with a black referent are
+ * removed from the list.  References with white referents biased
+ * toward saving are blackened and also removed from the list.
+ */
+static void preserveSomeSoftReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    Object *clear = NULL;
+    size_t counter = 0;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent == NULL) {
+            /* Referent was cleared by the user during marking. */
+            continue;
+        }
+        bool marked = isMarked(referent, ctx);
+        if (!marked && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, mark it. */
+            markObject(referent, ctx);
+            marked = true;
+        }
+        if (!marked) {
+            /* Referent is white, queue it for clearing. */
+            enqueuePendingReference(ref, &clear);
+        }
+    }
+    *list = clear;
+    /*
+     * Restart the mark with the newly black references added to the
+     * root set.
+     */
+    processMarkStack(ctx);
+}
+
+/*
+ * Unlink the reference list clearing references objects with white
+ * referents.  Cleared references registered to a reference queue are
+ * scheduled for appending by the heap worker thread.
+ */
+static void clearWhiteReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent != NULL && !isMarked(referent, ctx)) {
+            /* Referent is white, clear it. */
+            clearReference(ref);
+            if (isEnqueuable(ref)) {
+                enqueueReference(ref);
+            }
+        }
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * Enqueues finalizer references with white referents.  White
+ * referents are blackened, moved to the zombie field, and the
+ * referent field is cleared.
+ */
+static void enqueueFinalizerReferences(Object **list)
+{
+    assert(list != NULL);
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    size_t referentOffset = gDvm.offJavaLangRefReference_referent;
+    size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
+    bool hasEnqueued = false;
+    while (*list != NULL) {
+        Object *ref = dequeuePendingReference(list);
+        Object *referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent != NULL && !isMarked(referent, ctx)) {
+            markObject(referent, ctx);
+            /* If the referent is non-null the reference must queuable. */
+            assert(isEnqueuable(ref));
+            dvmSetFieldObject(ref, zombieOffset, referent);
+            clearReference(ref);
+            enqueueReference(ref);
+            hasEnqueued = true;
+        }
+    }
+    if (hasEnqueued) {
+        processMarkStack(ctx);
+    }
+    assert(*list == NULL);
+}
+
+/*
+ * This object is an instance of a class that overrides finalize().  Mark
+ * it as finalizable.
+ *
+ * This is called when Object.<init> completes normally.  It's also
+ * called for clones of finalizable objects.
+ */
+void dvmSetFinalizable(Object *obj)
+{
+    assert(obj != NULL);
+    Thread *self = dvmThreadSelf();
+    assert(self != NULL);
+    Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
+    assert(meth != NULL);
+    JValue unusedResult;
+    dvmCallMethod(self, meth, NULL, &unusedResult, obj);
+}
+
+/*
+ * Process reference class instances and schedule finalizations.
+ */
+void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
+                              Object **weakReferences,
+                              Object **finalizerReferences,
+                              Object **phantomReferences)
+{
+    assert(softReferences != NULL);
+    assert(weakReferences != NULL);
+    assert(finalizerReferences != NULL);
+    assert(phantomReferences != NULL);
+    /*
+     * Unless we are in the zygote or required to clear soft
+     * references with white references, preserve some white
+     * referents.
+     */
+    if (!gDvm.zygote && !clearSoftRefs) {
+        preserveSomeSoftReferences(softReferences);
+    }
+    /*
+     * Clear all remaining soft and weak references with white
+     * referents.
+     */
+    clearWhiteReferences(softReferences);
+    clearWhiteReferences(weakReferences);
+    /*
+     * Preserve all white objects with finalize methods and schedule
+     * them for finalization.
+     */
+    enqueueFinalizerReferences(finalizerReferences);
+    /*
+     * Clear all f-reachable soft and weak references with white
+     * referents.
+     */
+    clearWhiteReferences(softReferences);
+    clearWhiteReferences(weakReferences);
+    /*
+     * Clear all phantom references with white referents.
+     */
+    clearWhiteReferences(phantomReferences);
+    /*
+     * At this point all reference lists should be empty.
+     */
+    assert(*softReferences == NULL);
+    assert(*weakReferences == NULL);
+    assert(*finalizerReferences == NULL);
+    assert(*phantomReferences == NULL);
+}
+
+/*
+ * Pushes a list of cleared references out to the managed heap.
+ */
+void dvmEnqueueClearedReferences(Object **cleared)
+{
+    assert(cleared != NULL);
+    if (*cleared != NULL) {
+        Thread *self = dvmThreadSelf();
+        assert(self != NULL);
+        Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;
+        assert(meth != NULL);
+        JValue unused;
+        Object *reference = *cleared;
+        dvmCallMethod(self, meth, NULL, &unused, reference);
+        *cleared = NULL;
+    }
+}
+
+void dvmHeapFinishMarkStep()
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    /* The mark bits are now not needed.
+     */
+    dvmHeapSourceZeroMarkBitmap();
+
+    /* Clean up everything else associated with the marking process.
+     */
+    destroyMarkStack(&ctx->stack);
+
+    ctx->finger = NULL;
+}
+
+struct SweepContext {
+    size_t numObjects;
+    size_t numBytes;
+    bool isConcurrent;
+};
+
+static void sweepBitmapCallback(size_t numPtrs, void **ptrs, void *arg)
+{
+    assert(arg != NULL);
+    SweepContext *ctx = (SweepContext *)arg;
+    if (ctx->isConcurrent) {
+        dvmLockHeap();
+    }
+    ctx->numBytes += dvmHeapSourceFreeList(numPtrs, ptrs);
+    ctx->numObjects += numPtrs;
+    if (ctx->isConcurrent) {
+        dvmUnlockHeap();
+    }
+}
+
+/*
+ * Returns true if the given object is unmarked.  This assumes that
+ * the bitmaps have not yet been swapped.
+ */
+static int isUnmarkedObject(void *obj)
+{
+    return !isMarked((Object *)obj, &gDvm.gcHeap->markContext);
+}
+
+static void sweepWeakJniGlobals()
+{
+    IndirectRefTable* table = &gDvm.jniWeakGlobalRefTable;
+    GcMarkContext* ctx = &gDvm.gcHeap->markContext;
+    typedef IndirectRefTable::iterator It; // TODO: C++0x auto
+    for (It it = table->begin(), end = table->end(); it != end; ++it) {
+        Object** entry = *it;
+        if (!isMarked(*entry, ctx)) {
+            *entry = kClearedJniWeakGlobal;
+        }
+    }
+}
+
+/*
+ * Process all the internal system structures that behave like
+ * weakly-held objects.
+ */
+void dvmHeapSweepSystemWeaks()
+{
+    dvmGcDetachDeadInternedStrings(isUnmarkedObject);
+    dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
+    sweepWeakJniGlobals();
+}
+
+/*
+ * Walk through the list of objects that haven't been marked and free
+ * them.  Assumes the bitmaps have been swapped.
+ */
+void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes)
+{
+    uintptr_t base[HEAP_SOURCE_MAX_HEAP_COUNT];
+    uintptr_t max[HEAP_SOURCE_MAX_HEAP_COUNT];
+    SweepContext ctx;
+    HeapBitmap *prevLive, *prevMark;
+    size_t numHeaps, numSweepHeaps;
+
+    numHeaps = dvmHeapSourceGetNumHeaps();
+    dvmHeapSourceGetRegions(base, max, numHeaps);
+    if (isPartial) {
+        assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == base[0]);
+        numSweepHeaps = 1;
+    } else {
+        numSweepHeaps = numHeaps;
+    }
+    ctx.numObjects = ctx.numBytes = 0;
+    ctx.isConcurrent = isConcurrent;
+    prevLive = dvmHeapSourceGetMarkBits();
+    prevMark = dvmHeapSourceGetLiveBits();
+    for (size_t i = 0; i < numSweepHeaps; ++i) {
+        dvmHeapBitmapSweepWalk(prevLive, prevMark, base[i], max[i],
+                               sweepBitmapCallback, &ctx);
+    }
+    *numObjects = ctx.numObjects;
+    *numBytes = ctx.numBytes;
+    if (gDvm.allocProf.enabled) {
+        gDvm.allocProf.freeCount += ctx.numObjects;
+        gDvm.allocProf.freeSize += ctx.numBytes;
+    }
+}
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
new file mode 100644
index 0000000..b14333a
--- /dev/null
+++ b/vm/alloc/MarkSweep.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef DALVIK_ALLOC_MARK_SWEEP_H_
+#define DALVIK_ALLOC_MARK_SWEEP_H_
+
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+
+struct GcMarkStack {
+    /* Highest address (exclusive)
+     */
+    const Object **limit;
+
+    /* Current top of the stack (exclusive)
+     */
+    const Object **top;
+
+    /* Lowest address (inclusive)
+     */
+    const Object **base;
+
+    /* Maximum stack size, in bytes.
+     */
+    size_t length;
+};
+
+/* This is declared publicly so that it can be included in gDvm.gcHeap.
+ */
+struct GcMarkContext {
+    HeapBitmap *bitmap;
+    GcMarkStack stack;
+    const char *immuneLimit;
+    const void *finger;   // only used while scanning/recursing.
+};
+
+bool dvmHeapBeginMarkStep(bool isPartial);
+void dvmHeapMarkRootSet(void);
+void dvmHeapReMarkRootSet(void);
+void dvmHeapScanMarkedObjects(void);
+void dvmHeapReScanMarkedObjects(void);
+void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
+                              Object **weakReferences,
+                              Object **finalizerReferences,
+                              Object **phantomReferences);
+void dvmHeapFinishMarkStep(void);
+void dvmHeapSweepSystemWeaks(void);
+void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes);
+void dvmEnqueueClearedReferences(Object **references);
+
+#endif  // DALVIK_ALLOC_MARK_SWEEP_H_
diff --git a/vm/alloc/TEST/HeapBitmapTest/Makefile b/vm/alloc/TEST/HeapBitmapTest/Makefile
new file mode 100644
index 0000000..969eb63
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/Makefile
@@ -0,0 +1,25 @@
+.PHONY: all
+all: runtest
+
+$(shell mkdir -p out)
+
+CC := gcc
+CFLAGS := -g -Wall -Werror
+#CFLAGS += -O2
+
+out/main.o: main.c ../../HeapBitmap.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../..
+
+out/HeapBitmap.o: ../../HeapBitmap.c ../../HeapBitmap.h include/cutils/ashmem.h include/Dalvik.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../.. -I include
+
+out/hbtest: out/main.o out/HeapBitmap.o out/clz.o
+	$(CC) $^ -o $@
+
+.PHONY: runtest
+runtest: out/hbtest
+	out/hbtest
+
+.PHONY: clean
+clean:
+	rm -rf out
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
new file mode 100644
index 0000000..89492d5
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
@@ -0,0 +1,18 @@
+#ifndef DALVIK_H_
+#define DALVIK_H_
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#define ALOGW(...) printf("W/" __VA_ARGS__)
+#define ALOGE(...) printf("E/" __VA_ARGS__)
+
+inline void dvmAbort(void) {
+    exit(1);
+}
+
+#endif  // DALVIK_H_
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
new file mode 100644
index 0000000..8680c77
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
@@ -0,0 +1,14 @@
+#ifndef ASHMEM_H_
+#define ASHMEM_H_
+
+#include <fcntl.h>
+
+#define ASHMEM_NAME_LEN 128
+
+inline int
+ashmem_create_region(const char *name, size_t len)
+{
+    return open("/dev/zero", O_RDWR);
+}
+
+#endif
diff --git a/vm/alloc/TEST/HeapBitmapTest/main.c b/vm/alloc/TEST/HeapBitmapTest/main.c
new file mode 100644
index 0000000..10fa7f8
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/main.c
@@ -0,0 +1,496 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#define __attribute(x) /* disable inlining */
+#include "HeapBitmap.h"
+#undef __attribute
+
+#define PAGE_SIZE 4096
+#define HEAP_BASE ((void *)0x10000)
+#define HEAP_SIZE (5 * PAGE_SIZE + 888)
+
+#define VERBOSE 1
+#if VERBOSE
+#define TRACE(...) printf(__VA_ARGS__)
+#else
+#define TRACE(...) /**/
+#endif
+
+void
+test_init()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    memset(&hb, 0x55, sizeof(hb));
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+
+    assert(hb.bits != NULL);
+    assert(hb.bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+    assert(hb.base == (uintptr_t)HEAP_BASE);
+    assert(hb.max < hb.base);
+
+    /* Make sure hb.bits is mapped.
+     */
+    *hb.bits = 0x55;
+    assert(*hb.bits = 0x55);
+    *hb.bits = 0;
+
+#define TEST_UNMAP 0
+#if TEST_UNMAP
+    /* Hold onto this to make sure it's unmapped later.
+     */
+    unsigned long int *bits = hb.bits;
+#endif
+
+    dvmHeapBitmapDelete(&hb);
+
+    assert(hb.bits == NULL);
+    assert(hb.bitsLen == 0);
+    assert(hb.base == 0);
+    assert(hb.max == 0);
+
+#if TEST_UNMAP
+    /* This pointer shouldn't be mapped anymore.
+     */
+    *bits = 0x55;
+    assert(!"Should have segfaulted");
+#endif
+}
+
+bool is_zeroed(const HeapBitmap *hb)
+{
+    int i;
+
+    for (i = 0; i < hb->bitsLen / sizeof (*hb->bits); i++) {
+        if (hb->bits[i] != 0L) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void assert_empty(const HeapBitmap *hb)
+{
+    assert(hb->bits != NULL);
+    assert(hb->bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+    assert(hb->base == (uintptr_t)HEAP_BASE);
+    assert(hb->max < hb->base);
+
+    assert(is_zeroed(hb));
+
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+}
+
+void
+test_bits()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+
+    assert_empty(&hb);
+
+    /* Set the lowest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Clear the lowest address.
+     */
+    dvmHeapBitmapClearObjectBit(&hb, HEAP_BASE);
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!is_zeroed(&hb));
+
+    /* Clear the highest address.
+     */
+    dvmHeapBitmapClearObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(is_zeroed(&hb));
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_clear()
+{
+    HeapBitmap hb;
+    bool ok;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+    assert_empty(&hb);
+
+    /* Set the highest address.
+     */
+    dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(!is_zeroed(&hb));
+
+    /* Clear the bitmap.
+     */
+    dvmHeapBitmapZero(&hb);
+    assert_empty(&hb);
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_modify()
+{
+    HeapBitmap hb;
+    bool ok;
+    unsigned long bit;
+
+    ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+    assert(ok);
+    assert_empty(&hb);
+
+    /* Set the lowest address.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+    assert(bit == 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the lowest address again.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+    assert(bit != 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(bit == 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Set the highest address again.
+     */
+    bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+    assert(bit != 0);
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+    assert(!dvmHeapBitmapMayContainObject(&hb,
+            HEAP_BASE + HEAP_SIZE));
+
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE));
+    assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HB_OBJECT_ALIGNMENT));
+    assert(dvmHeapBitmapIsObjectBitSet(&hb,
+            HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb);
+}
+
+/*
+ * xor test support functions
+ */
+
+static void *gCallbackArg = NULL;
+
+#define NUM_XOR_PTRS  128
+static size_t gNumPtrs;
+static void *gXorPtrs[NUM_XOR_PTRS];
+static bool gClearedPtrs[NUM_XOR_PTRS];
+static bool gSeenPtrs[NUM_XOR_PTRS];
+
+bool
+xorCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
+{
+    assert(numPtrs > 0);
+    assert(ptrs != NULL);
+    assert(arg == gCallbackArg);
+
+size_t i;
+    for (i = 0; i < numPtrs; i++) {
+        assert(ptrs[i] < finger);
+        printf("callback: 0x%08x ( < 0x%08x )\n",
+                (uintptr_t)ptrs[i], (uintptr_t)finger);
+    }
+
+    return true;
+}
+
+bool
+seenAndClearedMatch()
+{
+    size_t i;
+    for (i = 0; i < gNumPtrs; i++) {
+        if (gClearedPtrs[i] != gSeenPtrs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+run_xor(ssize_t offset, size_t step)
+{
+    assert(step != 0);
+    assert(step < HEAP_SIZE);
+
+    /* Figure out the range.
+     */
+uintptr_t base;
+uintptr_t top;
+    if (offset >= 0) {
+        base = (uintptr_t)HEAP_BASE + offset;
+    } else {
+        base = (uintptr_t)HEAP_BASE + (uintptr_t)HEAP_SIZE + offset;
+    }
+    if (base < (uintptr_t)HEAP_BASE) {
+        base = (uintptr_t)HEAP_BASE;
+    } else if (base > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+        base = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+    } else {
+        base = (base + HB_OBJECT_ALIGNMENT - 1) & ~(HB_OBJECT_ALIGNMENT - 1);
+    }
+    step *= HB_OBJECT_ALIGNMENT;
+    top = base + step * NUM_XOR_PTRS;
+    if (top > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+        top = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+    }
+
+    /* Create the pointers.
+     */
+    gNumPtrs = 0;
+    memset(gXorPtrs, 0, sizeof(gXorPtrs));
+    memset(gClearedPtrs, 0, sizeof(gClearedPtrs));
+    memset(gSeenPtrs, 0, sizeof(gSeenPtrs));
+
+uintptr_t addr;
+void **p = gXorPtrs;
+    for (addr = base; addr < top; addr += step) {
+        *p++ = (void *)addr;
+        gNumPtrs++;
+    }
+    assert(seenAndClearedMatch());
+
+    /* Set up the bitmaps.
+     */
+HeapBitmap hb1, hb2;
+bool ok;
+
+    ok = dvmHeapBitmapInit(&hb1, HEAP_BASE, HEAP_SIZE, "test1");
+    assert(ok);
+    ok = dvmHeapBitmapInitFromTemplate(&hb2, &hb1, "test2");
+    assert(ok);
+
+    /* Walk two empty bitmaps.
+     */
+TRACE("walk 0\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+    assert(seenAndClearedMatch());
+
+    /* Walk one empty bitmap.
+     */
+TRACE("walk 1\n");
+    dvmHeapBitmapSetObjectBit(&hb1, (void *)base);
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Make the bitmaps match.
+     */
+TRACE("walk 2\n");
+    dvmHeapBitmapSetObjectBit(&hb2, (void *)base);
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Clear the bitmaps.
+     */
+    dvmHeapBitmapZero(&hb1);
+    assert_empty(&hb1);
+    dvmHeapBitmapZero(&hb2);
+    assert_empty(&hb2);
+
+    /* Set the pointers we created in one of the bitmaps,
+     * then visit them.
+     */
+size_t i;
+    for (i = 0; i < gNumPtrs; i++) {
+        dvmHeapBitmapSetObjectBit(&hb1, gXorPtrs[i]);
+    }
+TRACE("walk 3\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Set every third pointer in the other bitmap, and visit again.
+     */
+    for (i = 0; i < gNumPtrs; i += 3) {
+        dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+    }
+TRACE("walk 4\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Set every other pointer in the other bitmap, and visit again.
+     */
+    for (i = 0; i < gNumPtrs; i += 2) {
+        dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+    }
+TRACE("walk 5\n");
+    ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+    /* Walk just one bitmap.
+     */
+TRACE("walk 6\n");
+    ok = dvmHeapBitmapWalk(&hb2, xorCallback, gCallbackArg);
+    assert(ok);
+
+//xxx build an expect list for the callback
+//xxx test where max points to beginning, middle, and end of a word
+
+    /* Clean up.
+     */
+    dvmHeapBitmapDelete(&hb1);
+    dvmHeapBitmapDelete(&hb2);
+}
+
+void
+test_xor()
+{
+    run_xor(0, 1);
+    run_xor(100, 34);
+}
+
+int main(int argc, char *argv[])
+{
+    printf("test_init...\n");
+    test_init();
+
+    printf("test_bits...\n");
+    test_bits();
+
+    printf("test_clear...\n");
+    test_clear();
+
+    printf("test_modify...\n");
+    test_modify();
+
+    printf("test_xor...\n");
+    test_xor();
+
+    printf("done.\n");
+    return 0;
+}
diff --git a/vm/alloc/Verify.cpp b/vm/alloc/Verify.cpp
new file mode 100644
index 0000000..93a090a
--- /dev/null
+++ b/vm/alloc/Verify.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+#include "alloc/Visit.h"
+
+/*
+ * Visitor applied to each reference field when searching for things
+ * that point to an object.  Sets the argument to NULL when a match is
+ * found.
+ */
+static void dumpReferencesVisitor(void *pObj, void *arg)
+{
+    Object *obj = *(Object **)pObj;
+    Object *lookingFor = *(Object **)arg;
+    if (lookingFor != NULL && lookingFor == obj) {
+        *(Object **)arg = NULL;
+    }
+}
+
+/*
+ * Visitor applied to each bitmap element to search for things that
+ * point to an object.  Logs a message when a match is found.
+ */
+static void dumpReferencesCallback(Object *obj, void *arg)
+{
+    if (obj == (Object *)arg) {
+        return;
+    }
+    dvmVisitObject(dumpReferencesVisitor, obj, &arg);
+    if (arg == NULL) {
+        ALOGD("Found %p in the heap @ %p", arg, obj);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Visitor applied to each root to search for things that point to an
+ * object.  Logs a message when a match is found.
+ */
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
+{
+    Object *obj = *(Object **)ptr;
+    Object *lookingFor = *(Object **)arg;
+    if (obj == lookingFor) {
+        ALOGD("Found %p in a root @ %p", arg, ptr);
+    }
+}
+
+/*
+ * Searches the roots and heap for object references.
+ */
+static void dumpReferences(const Object *obj)
+{
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    void *arg = (void *)obj;
+    dvmVisitRoots(dumpReferencesRootVisitor, arg);
+    dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg);
+}
+
+/*
+ * Checks that the given reference points to a valid object.
+ */
+static void verifyReference(void *addr, void *arg)
+{
+    Object *obj;
+    bool isValid;
+
+    assert(addr != NULL);
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        isValid = true;
+    } else {
+        isValid = dvmIsValidObject(obj);
+    }
+    if (!isValid) {
+        Object **parent = (Object **)arg;
+        if (*parent != NULL) {
+            ALOGE("Verify of object %p failed", *parent);
+            dvmDumpObject(*parent);
+            *parent = NULL;
+        }
+        ALOGE("Verify of reference %p @ %p failed", obj, addr);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj)
+{
+    Object *arg = const_cast<Object*>(obj);
+    dvmVisitObject(verifyReference, arg, &arg);
+    if (arg == NULL) {
+        dumpReferences(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Helper function to call dvmVerifyObject from a bitmap walker.
+ */
+static void verifyBitmapCallback(Object *obj, void *arg)
+{
+    dvmVerifyObject(obj);
+}
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap)
+{
+    dvmHeapBitmapWalk(bitmap, verifyBitmapCallback, NULL);
+}
+
+/*
+ * Helper function to call verifyReference from the root verifier.
+ */
+static void verifyRootReference(void *addr, u4 threadId,
+                                RootType type, void *arg)
+{
+    verifyReference(addr, arg);
+}
+
+/*
+ * Verifies references in the roots.
+ */
+void dvmVerifyRoots()
+{
+    dvmVisitRoots(verifyRootReference, NULL);
+}
diff --git a/vm/alloc/Verify.h b/vm/alloc/Verify.h
new file mode 100644
index 0000000..b9370ff
--- /dev/null
+++ b/vm/alloc/Verify.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_ALLOC_VERIFY_H_
+#define DALVIK_ALLOC_VERIFY_H_
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj);
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap);
+
+/*
+ * Verifies the contents of various global roots.
+ */
+void dvmVerifyRoots(void);
+
+#endif  // DALVIK_ALLOC_VERIFY_H_
diff --git a/vm/alloc/Visit.cpp b/vm/alloc/Visit.cpp
new file mode 100644
index 0000000..410b66e
--- /dev/null
+++ b/vm/alloc/Visit.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/Visit.h"
+#include "alloc/VisitInlines.h"
+
+/*
+ * Visits all of the reference locations in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    visitObject(visitor, obj, arg);
+}
+
+/*
+ * Applies a verification function to all present values in the hash table.
+ */
+static void visitHashTable(RootVisitor *visitor, HashTable *table,
+                           RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    dvmHashTableLock(table);
+    for (int i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+            (*visitor)(&entry->data, 0, type, arg);
+        }
+    }
+    dvmHashTableUnlock(table);
+}
+
+/*
+ * Visits all entries in the reference table.
+ */
+static void visitReferenceTable(RootVisitor *visitor, ReferenceTable *table,
+                                u4 threadId, RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    for (Object **entry = table->table; entry < table->nextEntry; ++entry) {
+        assert(entry != NULL);
+        (*visitor)(entry, threadId, type, arg);
+    }
+}
+
+/*
+ * Visits all entries in the indirect reference table.
+ */
+static void visitIndirectRefTable(RootVisitor *visitor, IndirectRefTable *table,
+                                  u4 threadId, RootType type, void *arg)
+{
+    assert(visitor != NULL);
+    assert(table != NULL);
+    typedef IndirectRefTable::iterator It; // TODO: C++0x auto
+    for (It it = table->begin(), end = table->end(); it != end; ++it) {
+        (*visitor)(*it, threadId, type, arg);
+    }
+}
+
+/*
+ * Visits all stack slots except those belonging to native method
+ * arguments.
+ */
+static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg)
+{
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    u4 threadId = thread->threadId;
+    const StackSaveArea *saveArea;
+    for (u4 *fp = (u4 *)thread->interpSave.curFrame;
+         fp != NULL;
+         fp = (u4 *)saveArea->prevFrame) {
+        Method *method;
+        saveArea = SAVEAREA_FROM_FP(fp);
+        method = (Method *)saveArea->method;
+        if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+            if (pMap != NULL) {
+                /* found map, get registers for this address */
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+            }
+            if (regVector == NULL) {
+                /*
+                 * Either there was no register map or there is no
+                 * info for the current PC.  Perform a conservative
+                 * scan.
+                 */
+                for (size_t i = 0; i < method->registersSize; ++i) {
+                    if (dvmIsValidObject((Object *)fp[i])) {
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
+                    }
+                }
+            } else {
+                /*
+                 * Precise scan.  v0 is at the lowest address on the
+                 * interpreted stack, and is the first bit in the
+                 * register vector, so we can walk through the
+                 * register map and memory in the same direction.
+                 *
+                 * A '1' bit indicates a live reference.
+                 */
+                u2 bits = 1 << 1;
+                for (size_t i = 0; i < method->registersSize; ++i) {
+                    bits >>= 1;
+                    if (bits == 1) {
+                        /* set bit 9 so we can tell when we're empty */
+                        bits = *regVector++ | 0x0100;
+                    }
+                    if ((bits & 0x1) != 0) {
+                        /*
+                         * Register is marked as live, it's a valid root.
+                         */
+#if WITH_EXTRA_GC_CHECKS
+                        if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) {
+                            /* this is very bad */
+                            ALOGE("PGC: invalid ref in reg %d: %#x",
+                                 method->registersSize - 1 - i, fp[i]);
+                            ALOGE("PGC: %s.%s addr %#x",
+                                 method->clazz->descriptor, method->name,
+                                 saveArea->xtra.currentPc - method->insns);
+                            continue;
+                        }
+#endif
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
+                    }
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+        /*
+         * Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp ||
+               saveArea->prevFrame == NULL);
+    }
+}
+
+/*
+ * Visits all roots associated with a thread.
+ */
+static void visitThread(RootVisitor *visitor, Thread *thread, void *arg)
+{
+    u4 threadId;
+
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    threadId = thread->threadId;
+    (*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg);
+    (*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg);
+    visitReferenceTable(visitor, &thread->internalLocalRefTable, threadId, ROOT_NATIVE_STACK, arg);
+    visitIndirectRefTable(visitor, &thread->jniLocalRefTable, threadId, ROOT_JNI_LOCAL, arg);
+    if (thread->jniMonitorRefTable.table != NULL) {
+        visitReferenceTable(visitor, &thread->jniMonitorRefTable, threadId, ROOT_JNI_MONITOR, arg);
+    }
+    visitThreadStack(visitor, thread, arg);
+}
+
+/*
+ * Visits all threads on the thread list.
+ */
+static void visitThreads(RootVisitor *visitor, void *arg)
+{
+    Thread *thread;
+
+    assert(visitor != NULL);
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        visitThread(visitor, thread, arg);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+static void visitPrimitiveTypes(RootVisitor *visitor, void *arg)
+{
+    (*visitor)(&gDvm.typeVoid, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeBoolean, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeByte, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeShort, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeChar, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeInt, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeLong, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeFloat, 0, ROOT_STICKY_CLASS, arg);
+    (*visitor)(&gDvm.typeDouble, 0, ROOT_STICKY_CLASS, arg);
+}
+
+/*
+ * Visits roots.  TODO: visit cached global references.
+ */
+void dvmVisitRoots(RootVisitor *visitor, void *arg)
+{
+    assert(visitor != NULL);
+    visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg);
+    visitPrimitiveTypes(visitor, arg);
+    if (gDvm.dbgRegistry != NULL) {
+        visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
+    }
+    if (gDvm.literalStrings != NULL) {
+        visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
+    }
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+    visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+    visitThreads(visitor, arg);
+    (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
+}
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
new file mode 100644
index 0000000..ff69a63
--- /dev/null
+++ b/vm/alloc/Visit.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_ALLOC_VISIT_H_
+#define DALVIK_ALLOC_VISIT_H_
+
+#include "Dalvik.h"
+
+enum RootType {
+  ROOT_UNKNOWN = 0,
+  ROOT_JNI_GLOBAL,
+  ROOT_JNI_LOCAL,
+  ROOT_JAVA_FRAME,
+  ROOT_NATIVE_STACK,
+  ROOT_STICKY_CLASS,
+  ROOT_THREAD_BLOCK,
+  ROOT_MONITOR_USED,
+  ROOT_THREAD_OBJECT,
+  ROOT_INTERNED_STRING,
+  ROOT_DEBUGGER,
+  ROOT_VM_INTERNAL,
+  ROOT_JNI_MONITOR,
+};
+
+/*
+ * Callback invoked with the address of a reference and a user
+ * supplied context argument.
+ */
+typedef void Visitor(void *addr, void *arg);
+
+/*
+ * Like a Visitor, but passes root specific information such as the
+ * containing thread id and the root type.  In cases where a root is
+ * not specific to a thread, 0, an invalid thread id is provided.
+ */
+typedef void RootVisitor(void *addr, u4 threadId, RootType type, void *arg);
+
+/*
+ * Visits references in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg);
+
+/*
+ * Visits references in the root set.
+ */
+void dvmVisitRoots(RootVisitor *visitor, void *arg);
+
+#endif  // DALVIK_ALLOC_VISIT_H_
diff --git a/vm/alloc/VisitInlines.h b/vm/alloc/VisitInlines.h
new file mode 100644
index 0000000..c6204ac
--- /dev/null
+++ b/vm/alloc/VisitInlines.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_ALLOC_VISITINLINES_H_
+#define DALVIK_ALLOC_VISITINLINES_H_
+
+/*
+ * Visits the instance fields of a class or data object.
+ */
+static void visitFields(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+        size_t refOffsets = obj->clazz->refOffsets;
+        while (refOffsets != 0) {
+            size_t rshift = CLZ(refOffsets);
+            size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+            Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+            (*visitor)(ref, arg);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        for (ClassObject *clazz = obj->clazz;
+             clazz != NULL;
+             clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (int i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                size_t offset = field->byteOffset;
+                Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+                (*visitor)(ref, arg);
+            }
+        }
+    }
+}
+
+/*
+ * Visits the static fields of a class object.
+ */
+static void visitStaticFields(Visitor *visitor, ClassObject *clazz,
+                              void *arg)
+{
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (int i = 0; i < clazz->sfieldCount; ++i) {
+        char ch = clazz->sfields[i].signature[0];
+        if (ch == '[' || ch == 'L') {
+            (*visitor)(&clazz->sfields[i].value.l, arg);
+        }
+    }
+}
+
+/*
+ * Visit the interfaces of a class object.
+ */
+static void visitInterfaces(Visitor *visitor, ClassObject *clazz,
+                            void *arg)
+{
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (int i = 0; i < clazz->interfaceCount; ++i) {
+        (*visitor)(&clazz->interfaces[i], arg);
+    }
+}
+
+/*
+ * Visits all the references stored in a class object instance.
+ */
+static void visitClassObject(Visitor *visitor, Object *obj, void *arg)
+{
+    ClassObject *asClass;
+
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(!strcmp(obj->clazz->descriptor, "Ljava/lang/Class;"));
+    (*visitor)(&obj->clazz, arg);
+    asClass = (ClassObject *)obj;
+    if (IS_CLASS_FLAG_SET(asClass, CLASS_ISARRAY)) {
+        (*visitor)(&asClass->elementClass, arg);
+    }
+    if (asClass->status > CLASS_IDX) {
+        (*visitor)(&asClass->super, arg);
+    }
+    (*visitor)(&asClass->classLoader, arg);
+    visitFields(visitor, obj, arg);
+    visitStaticFields(visitor, asClass, arg);
+    if (asClass->status > CLASS_IDX) {
+      visitInterfaces(visitor, asClass, arg);
+    }
+}
+
+/*
+ * Visits the class object and, if the array is typed as an object
+ * array, all of the array elements.
+ */
+static void visitArrayObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    (*visitor)(&obj->clazz, arg);
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+        ArrayObject *array = (ArrayObject *)obj;
+        Object **contents = (Object **)(void *)array->contents;
+        for (size_t i = 0; i < array->length; ++i) {
+            (*visitor)(&contents[i], arg);
+        }
+    }
+}
+
+/*
+ * Visits the class object and reference typed instance fields of a
+ * data object.
+ */
+static void visitDataObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    (*visitor)(&obj->clazz, arg);
+    visitFields(visitor, obj, arg);
+}
+
+/*
+ * Like visitDataObject, but visits the hidden referent field that
+ * belongings to the subclasses of java.lang.Reference.
+ */
+static void visitReferenceObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    visitDataObject(visitor, obj, arg);
+    size_t offset = gDvm.offJavaLangRefReference_referent;
+    Object **ref = (Object **)BYTE_OFFSET(obj, offset);
+    (*visitor)(ref, arg);
+}
+
+/*
+ * Visits all of the reference stored in an object.
+ */
+static void visitObject(Visitor *visitor, Object *obj, void *arg)
+{
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    if (dvmIsClassObject(obj)) {
+        visitClassObject(visitor, obj, arg);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        visitArrayObject(visitor, obj, arg);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+        visitReferenceObject(visitor, obj, arg);
+    } else {
+        visitDataObject(visitor, obj, arg);
+    }
+}
+
+#endif  // DALVIK_ALLOC_VISITINLINES_H_
diff --git a/vm/alloc/WriteBarrier.h b/vm/alloc/WriteBarrier.h
new file mode 100644
index 0000000..31b3482
--- /dev/null
+++ b/vm/alloc/WriteBarrier.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_ALLOC_WRITEBARRIER_H_
+#define DALVIK_ALLOC_WRITEBARRIER_H_
+
+/*
+ * Note writes to the heap. These functions must be called if a field
+ * of an Object in the heap changes, and before any GC safe-point. The
+ * call is not needed if NULL is stored in the field.
+ */
+
+/*
+ * The address within the Object has been written, and perhaps changed.
+ */
+INLINE void dvmWriteBarrierField(const Object *obj, void *addr)
+{
+    dvmMarkCard(obj);
+}
+
+/*
+ * All of the Object may have changed.
+ */
+INLINE void dvmWriteBarrierObject(const Object *obj)
+{
+    dvmMarkCard(obj);
+}
+
+/*
+ * Some or perhaps all of the array indexes in the Array, greater than
+ * or equal to start and strictly less than end, have been written,
+ * and perhaps changed.
+ */
+INLINE void dvmWriteBarrierArray(const ArrayObject *obj,
+                                 size_t start, size_t end)
+{
+    dvmMarkCard((Object *)obj);
+}
+
+#endif  // DALVIK_ALLOC_WRITEBARRIER_H_
diff --git a/vm/analysis/CodeVerify.cpp b/vm/analysis/CodeVerify.cpp
new file mode 100644
index 0000000..56a9fbd
--- /dev/null
+++ b/vm/analysis/CodeVerify.cpp
@@ -0,0 +1,6206 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik bytecode structural verifier.  The only public entry point
+ * (except for a few shared utility functions) is dvmVerifyCodeFlow().
+ *
+ * TODO: might benefit from a signature-->class lookup cache.  Could avoid
+ * some string-peeling and wouldn't need to compute hashes.
+ */
+#include "Dalvik.h"
+#include "analysis/Liveness.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/Optimize.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+#include <stddef.h>
+
+
+/*
+ * We don't need to store the register data for many instructions, because
+ * we either only need it at branch points (for verification) or GC points
+ * and branches (for verification + type-precise register analysis).
+ */
+enum RegisterTrackingMode {
+    kTrackRegsBranches,
+    kTrackRegsGcPoints,
+    kTrackRegsAll
+};
+
+/*
+ * Set this to enable dead code scanning.  This is not required, but it's
+ * very useful when testing changes to the verifier (to make sure we're not
+ * skipping over stuff) and for checking the optimized output from "dx".
+ * The only reason not to do it is that it slightly increases the time
+ * required to perform verification.
+ */
+#ifndef NDEBUG
+# define DEAD_CODE_SCAN  true
+#else
+# define DEAD_CODE_SCAN  false
+#endif
+
+static bool gDebugVerbose = false;
+
+#define SHOW_REG_DETAILS \
+    (0 | DRT_SHOW_LIVENESS /*| DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS*/)
+
+/*
+ * 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(_insnRegCount)  (_insnRegCount)
+
+/*
+ * Big fat collection of register data.
+ */
+typedef struct RegisterTable {
+    /*
+     * Array of RegisterLine structs, one per address in the method.  We only
+     * set the pointers for certain addresses, based on instruction widths
+     * and what we're trying to accomplish.
+     */
+    RegisterLine* registerLines;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize" plus kExtraRegs.
+     */
+    size_t      insnRegCountPlus;
+
+    /*
+     * Storage for a register line we're currently working on.
+     */
+    RegisterLine workLine;
+
+    /*
+     * Storage for a register line we're saving for later.
+     */
+    RegisterLine savedLine;
+
+    /*
+     * A single large alloc, with all of the storage needed for RegisterLine
+     * data (RegType array, MonitorEntries array, monitor stack).
+     */
+    void*       lineAlloc;
+} RegisterTable;
+
+
+/* fwd */
+#ifndef NDEBUG
+static void checkMergeTab();
+#endif
+static bool isInitMethod(const Method* meth);
+static RegType getInvocationThis(const RegisterLine* registerLine,\
+    const DecodedInstruction* pDecInsn, VerifyError* pFailure);
+static void verifyRegisterType(RegisterLine* registerLine, \
+    u4 vsrc, RegType checkType, VerifyError* pFailure);
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable);
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
+    RegisterTable* regTable, int insnIdx, UninitInstanceMap* uninitMap,
+    int* pStartGuess);
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
+static void dumpRegTypes(const VerifierData* vdata, \
+    const RegisterLine* registerLine, int addr, const char* addrName,
+    const UninitInstanceMap* uninitMap, int displayFlags);
+
+/* bit values for dumpRegTypes() "displayFlags" */
+enum {
+    DRT_SIMPLE          = 0,
+    DRT_SHOW_REF_TYPES  = 0x01,
+    DRT_SHOW_LOCALS     = 0x02,
+    DRT_SHOW_LIVENESS   = 0x04,
+};
+
+
+/*
+ * ===========================================================================
+ *      RegType and UninitInstanceMap utility functions
+ * ===========================================================================
+ */
+
+#define __  kRegTypeUnknown
+#define _U  kRegTypeUninit
+#define _X  kRegTypeConflict
+#define _0  kRegTypeZero
+#define _1  kRegTypeOne
+#define _Z  kRegTypeBoolean
+#define _y  kRegTypeConstPosByte
+#define _Y  kRegTypeConstByte
+#define _h  kRegTypeConstPosShort
+#define _H  kRegTypeConstShort
+#define _c  kRegTypeConstChar
+#define _i  kRegTypeConstInteger
+#define _b  kRegTypePosByte
+#define _B  kRegTypeByte
+#define _s  kRegTypePosShort
+#define _S  kRegTypeShort
+#define _C  kRegTypeChar
+#define _I  kRegTypeInteger
+#define _F  kRegTypeFloat
+#define _N  kRegTypeConstLo
+#define _n  kRegTypeConstHi
+#define _J  kRegTypeLongLo
+#define _j  kRegTypeLongHi
+#define _D  kRegTypeDoubleLo
+#define _d  kRegTypeDoubleHi
+
+/*
+ * Merge result table for primitive values.  The table is symmetric along
+ * the diagonal.
+ *
+ * Note that 32-bit int/float do not merge into 64-bit long/double.  This
+ * is a register merge, not a widening conversion.  Only the "implicit"
+ * widening within a category, e.g. byte to short, is allowed.
+ *
+ * Dalvik does not draw a distinction between int and float, but we enforce
+ * that once a value is used as int, it can't be used as float, and vice
+ * versa. We do not allow free exchange between 32-bit int/float and 64-bit
+ * long/double.
+ *
+ * Note that Uninit+Uninit=Uninit.  This holds true because we only
+ * use this when the RegType value is exactly equal to kRegTypeUninit, which
+ * can only happen for the zeroeth entry in the table.
+ *
+ * "Unknown" never merges with anything known.  The only time a register
+ * transitions from "unknown" to "known" is when we're executing code
+ * for the first time, and we handle that with a simple copy.
+ */
+const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX] =
+{
+    /* chk:  _  U  X  0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F  N  n  J  j  D  d */
+    { /*_*/ __,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*U*/ _X,_U,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*X*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+    { /*0*/ _X,_X,_X,_0,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*1*/ _X,_X,_X,_Z,_1,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*Z*/ _X,_X,_X,_Z,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*y*/ _X,_X,_X,_y,_y,_y,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*Y*/ _X,_X,_X,_Y,_Y,_Y,_Y,_Y,_h,_H,_c,_i,_B,_B,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*h*/ _X,_X,_X,_h,_h,_h,_h,_h,_h,_H,_c,_i,_s,_S,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*H*/ _X,_X,_X,_H,_H,_H,_H,_H,_H,_H,_c,_i,_S,_S,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*c*/ _X,_X,_X,_c,_c,_c,_c,_c,_c,_c,_c,_i,_C,_I,_C,_I,_C,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*i*/ _X,_X,_X,_i,_i,_i,_i,_i,_i,_i,_i,_i,_I,_I,_I,_I,_I,_I,_F,_X,_X,_X,_X,_X,_X },
+    { /*b*/ _X,_X,_X,_b,_b,_b,_b,_B,_s,_S,_C,_I,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*B*/ _X,_X,_X,_B,_B,_B,_B,_B,_S,_S,_I,_I,_B,_B,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*s*/ _X,_X,_X,_s,_s,_s,_s,_S,_s,_S,_C,_I,_s,_S,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*S*/ _X,_X,_X,_S,_S,_S,_S,_S,_S,_S,_I,_I,_S,_S,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*C*/ _X,_X,_X,_C,_C,_C,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*I*/ _X,_X,_X,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_X,_X,_X,_X,_X,_X,_X },
+    { /*F*/ _X,_X,_X,_F,_F,_F,_F,_F,_F,_F,_F,_F,_X,_X,_X,_X,_X,_X,_F,_X,_X,_X,_X,_X,_X },
+    { /*N*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_N,_X,_J,_X,_D,_X },
+    { /*n*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_n,_X,_j,_X,_d },
+    { /*J*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_J,_X,_X,_X },
+    { /*j*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_j,_X,_X },
+    { /*D*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_D,_X,_X,_X,_D,_X },
+    { /*d*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_d,_X,_X,_X,_d },
+};
+
+#undef __
+#undef _U
+#undef _X
+#undef _0
+#undef _1
+#undef _Z
+#undef _y
+#undef _Y
+#undef _h
+#undef _H
+#undef _c
+#undef _i
+#undef _b
+#undef _B
+#undef _s
+#undef _S
+#undef _C
+#undef _I
+#undef _F
+#undef _N
+#undef _n
+#undef _J
+#undef _j
+#undef _D
+#undef _d
+
+#ifndef NDEBUG
+/*
+ * Verify symmetry in the conversion table.
+ */
+static void checkMergeTab()
+{
+    int i, j;
+
+    for (i = 0; i < kRegTypeMAX; i++) {
+        for (j = i; j < kRegTypeMAX; j++) {
+            if (gDvmMergeTab[i][j] != gDvmMergeTab[j][i]) {
+                ALOGE("Symmetry violation: %d,%d vs %d,%d", i, j, j, i);
+                dvmAbort();
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Determine whether we can convert "srcType" to "checkType", where
+ * "checkType" is one of the category-1 non-reference types.
+ *
+ * Constant derived types may become floats, but other values may not.
+ */
+static bool canConvertTo1nr(RegType srcType, RegType checkType)
+{
+    static const char convTab
+        [kRegType1nrEND-kRegType1nrSTART+1][kRegType1nrEND-kRegType1nrSTART+1] =
+    {
+        /* chk: 0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F */
+        { /*0*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*1*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*Z*/ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*y*/ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*Y*/ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
+        { /*h*/ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
+        { /*H*/ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1 },
+        { /*c*/ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
+        { /*i*/ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
+        { /*b*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 },
+        { /*B*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
+        { /*s*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+        { /*S*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
+        { /*C*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
+        { /*I*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+        { /*F*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+    };
+
+    assert(checkType >= kRegType1nrSTART && checkType <= kRegType1nrEND);
+#if 0
+    if (checkType < kRegType1nrSTART || checkType > kRegType1nrEND) {
+        LOG_VFY("Unexpected checkType %d (srcType=%d)", checkType, srcType);
+        assert(false);
+        return false;
+    }
+#endif
+
+    //printf("convTab[%d][%d] = %d\n", srcType, checkType,
+    //    convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART]);
+    if (srcType >= kRegType1nrSTART && srcType <= kRegType1nrEND)
+        return (bool) convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART];
+
+    return false;
+}
+
+/*
+ * Determine whether the category-2 types are compatible.
+ */
+static bool canConvertTo2(RegType srcType, RegType checkType)
+{
+    return ((srcType == kRegTypeConstLo || srcType == checkType) &&
+            (checkType == kRegTypeLongLo || checkType == kRegTypeDoubleLo));
+}
+
+/*
+ * Determine whether or not "instrType" and "targetType" are compatible,
+ * for purposes of getting or setting a value in a field or array.  The
+ * idea is that an instruction with a category 1nr type (say, aget-short
+ * or iput-boolean) is accessing a static field, instance field, or array
+ * entry, and we want to make sure sure that the operation is legal.
+ *
+ * At a minimum, source and destination must have the same width.  We
+ * further refine this to assert that "short" and "char" are not
+ * compatible, because the sign-extension is different on the "get"
+ * operations.
+ *
+ * We're not considering the actual contents of the register, so we'll
+ * never get "pseudo-types" like kRegTypeZero or kRegTypePosShort.  We
+ * could get kRegTypeUnknown in "targetType" if a field or array class
+ * lookup failed.  Category 2 types and references are checked elsewhere.
+ */
+static bool checkFieldArrayStore1nr(RegType instrType, RegType targetType)
+{
+    return (instrType == targetType);
+}
+
+/*
+ * Convert a VM PrimitiveType enum value to the equivalent RegType value.
+ */
+static RegType primitiveTypeToRegType(PrimitiveType primType)
+{
+    switch (primType) {
+        case PRIM_BOOLEAN: return kRegTypeBoolean;
+        case PRIM_BYTE:    return kRegTypeByte;
+        case PRIM_SHORT:   return kRegTypeShort;
+        case PRIM_CHAR:    return kRegTypeChar;
+        case PRIM_INT:     return kRegTypeInteger;
+        case PRIM_LONG:    return kRegTypeLongLo;
+        case PRIM_FLOAT:   return kRegTypeFloat;
+        case PRIM_DOUBLE:  return kRegTypeDoubleLo;
+        case PRIM_VOID:
+        default: {
+            assert(false);
+            return kRegTypeUnknown;
+        }
+    }
+}
+
+/*
+ * Convert a const derived RegType to the equivalent non-const RegType value.
+ * Does nothing if the argument type isn't const derived.
+ */
+static RegType constTypeToRegType(RegType constType)
+{
+    switch (constType) {
+        case kRegTypeConstPosByte: return kRegTypePosByte;
+        case kRegTypeConstByte: return kRegTypeByte;
+        case kRegTypeConstPosShort: return kRegTypePosShort;
+        case kRegTypeConstShort: return kRegTypeShort;
+        case kRegTypeConstChar: return kRegTypeChar;
+        case kRegTypeConstInteger: return kRegTypeInteger;
+        default: {
+            return constType;
+        }
+    }
+}
+
+/*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value. The types used here indicate the value came
+ * from a const instruction, and may not correctly represent the real type
+ * of the value. Upon use, a constant derived type is updated with the
+ * type from the use, which will be unambiguous.
+ */
+static char determineCat1Const(s4 value)
+{
+    if (value < -32768)
+        return kRegTypeConstInteger;
+    else if (value < -128)
+        return kRegTypeConstShort;
+    else if (value < 0)
+        return kRegTypeConstByte;
+    else if (value == 0)
+        return kRegTypeZero;
+    else if (value == 1)
+        return kRegTypeOne;
+    else if (value < 128)
+        return kRegTypeConstPosByte;
+    else if (value < 32768)
+        return kRegTypeConstPosShort;
+    else if (value < 65536)
+        return kRegTypeConstChar;
+    else
+        return kRegTypeConstInteger;
+}
+
+/*
+ * Create a new uninitialized instance map.
+ *
+ * The map is allocated and populated with address entries.  The addresses
+ * appear in ascending order to allow binary searching.
+ *
+ * Very few methods have 10 or more new-instance instructions; the
+ * majority have 0 or 1.  Occasionally a static initializer will have 200+.
+ *
+ * TODO: merge this into the static pass or initRegisterTable; want to
+ * avoid walking through the instructions yet again just to set up this table
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+    const InsnFlags* insnFlags, int newInstanceCount)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns;
+    UninitInstanceMap* uninitMap;
+    bool isInit = false;
+    int idx, addr;
+
+    if (isInitMethod(meth)) {
+        newInstanceCount++;
+        isInit = true;
+    }
+
+    /*
+     * Allocate the header and map as a single unit.
+     *
+     * TODO: consider having a static instance so we can avoid allocations.
+     * I don't think the verifier is guaranteed to be single-threaded when
+     * running in the VM (rather than dexopt), so that must be taken into
+     * account.
+     */
+    int size = offsetof(UninitInstanceMap, map) +
+                newInstanceCount * sizeof(uninitMap->map[0]);
+    uninitMap = (UninitInstanceMap*)calloc(1, size);
+    if (uninitMap == NULL)
+        return NULL;
+    uninitMap->numEntries = newInstanceCount;
+
+    idx = 0;
+    if (isInit) {
+        uninitMap->map[idx++].addr = kUninitThisArgAddr;
+    }
+
+    /*
+     * Run through and find the new-instance instructions.
+     */
+    for (addr = 0; addr < insnsSize; /**/) {
+        int width = dvmInsnGetWidth(insnFlags, addr);
+
+        Opcode opcode = dexOpcodeFromCodeUnit(*insns);
+        if (opcode == OP_NEW_INSTANCE)
+            uninitMap->map[idx++].addr = addr;
+
+        addr += width;
+        insns += width;
+    }
+
+    assert(idx == newInstanceCount);
+    return uninitMap;
+}
+
+/*
+ * Free the map.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap)
+{
+    free(uninitMap);
+}
+
+/*
+ * Set the class object associated with the instruction at "addr".
+ *
+ * Returns the map slot index, or -1 if the address isn't listed in the map
+ * (shouldn't happen) or if a class is already associated with the address
+ * (bad bytecode).
+ *
+ * Entries, once set, do not change -- a given address can only allocate
+ * one type of object.
+ */
+static int setUninitInstance(UninitInstanceMap* uninitMap, int addr,
+    ClassObject* clazz)
+{
+    int idx;
+
+    assert(clazz != NULL);
+
+#ifdef VERIFIER_STATS
+    gDvm.verifierStats.uninitSearches++;
+#endif
+
+    /* TODO: binary search when numEntries > 8 */
+    for (idx = uninitMap->numEntries - 1; idx >= 0; idx--) {
+        if (uninitMap->map[idx].addr == addr) {
+            if (uninitMap->map[idx].clazz != NULL &&
+                uninitMap->map[idx].clazz != clazz)
+            {
+                LOG_VFY("VFY: addr %d already set to %p, not setting to %p",
+                    addr, uninitMap->map[idx].clazz, clazz);
+                return -1;          // already set to something else??
+            }
+            uninitMap->map[idx].clazz = clazz;
+            return idx;
+        }
+    }
+
+    LOG_VFY("VFY: addr %d not found in uninit map", addr);
+    assert(false);      // shouldn't happen
+    return -1;
+}
+
+/*
+ * Get the class object at the specified index.
+ */
+static ClassObject* getUninitInstance(const UninitInstanceMap* uninitMap,
+    int idx)
+{
+    assert(idx >= 0 && idx < uninitMap->numEntries);
+    return uninitMap->map[idx].clazz;
+}
+
+/* determine if "type" is actually an object reference (init/uninit/zero) */
+static inline bool regTypeIsReference(RegType type) {
+    return (type > kRegTypeMAX || type == kRegTypeUninit ||
+            type == kRegTypeZero);
+}
+
+/* determine if "type" is an uninitialized object reference */
+static inline bool regTypeIsUninitReference(RegType type) {
+    return ((type & kRegTypeUninitMask) == kRegTypeUninit);
+}
+
+/* convert the initialized reference "type" to a ClassObject pointer */
+/* (does not expect uninit ref types or "zero") */
+static ClassObject* regTypeInitializedReferenceToClass(RegType type)
+{
+    assert(regTypeIsReference(type) && type != kRegTypeZero);
+    if ((type & 0x01) == 0) {
+        return (ClassObject*) type;
+    } else {
+        //LOG_VFY("VFY: attempted to use uninitialized reference");
+        return NULL;
+    }
+}
+
+/* extract the index into the uninitialized instance map table */
+static inline int regTypeToUninitIndex(RegType type) {
+    assert(regTypeIsUninitReference(type));
+    return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
+}
+
+/* convert the reference "type" to a ClassObject pointer */
+static ClassObject* regTypeReferenceToClass(RegType type,
+    const UninitInstanceMap* uninitMap)
+{
+    assert(regTypeIsReference(type) && type != kRegTypeZero);
+    if (regTypeIsUninitReference(type)) {
+        assert(uninitMap != NULL);
+        return getUninitInstance(uninitMap, regTypeToUninitIndex(type));
+    } else {
+        return (ClassObject*) type;
+    }
+}
+
+/* convert the ClassObject pointer to an (initialized) register type */
+static inline RegType regTypeFromClass(ClassObject* clazz) {
+    return (u4) clazz;
+}
+
+/* return the RegType for the uninitialized reference in slot "uidx" */
+static RegType regTypeFromUninitIndex(int uidx) {
+    return (u4) (kRegTypeUninit | (uidx << kRegTypeUninitShift));
+}
+
+
+/*
+ * ===========================================================================
+ *      Signature operations
+ * ===========================================================================
+ */
+
+/*
+ * Is this method a constructor?
+ */
+static bool isInitMethod(const Method* meth)
+{
+    return (*meth->name == '<' && strcmp(meth->name+1, "init>") == 0);
+}
+
+/*
+ * Is this method a class initializer?
+ */
+#if 0
+static bool isClassInitMethod(const Method* meth)
+{
+    return (*meth->name == '<' && strcmp(meth->name+1, "clinit>") == 0);
+}
+#endif
+
+/*
+ * Look up a class reference given as a simple string descriptor.
+ *
+ * If we can't find it, return a generic substitute when possible.
+ */
+static ClassObject* lookupClassByDescriptor(const Method* meth,
+    const char* pDescriptor, VerifyError* pFailure)
+{
+    /*
+     * The javac compiler occasionally puts references to nonexistent
+     * classes in signatures.  For example, if you have a non-static
+     * inner class with no constructor, the compiler provides
+     * a private <init> for you.  Constructing the class
+     * requires <init>(parent), but the outer class can't call
+     * that because the method is private.  So the compiler
+     * generates a package-scope <init>(parent,bogus) method that
+     * just calls the regular <init> (the "bogus" part being necessary
+     * to distinguish the signature of the synthetic method).
+     * Treating the bogus class as an instance of java.lang.Object
+     * allows the verifier to process the class successfully.
+     */
+
+    //ALOGI("Looking up '%s'", typeStr);
+    ClassObject* clazz;
+    clazz = dvmFindClassNoInit(pDescriptor, meth->clazz->classLoader);
+    if (clazz == NULL) {
+        dvmClearOptException(dvmThreadSelf());
+        if (strchr(pDescriptor, '$') != NULL) {
+            ALOGV("VFY: unable to find class referenced in signature (%s)",
+                pDescriptor);
+        } else {
+            LOG_VFY("VFY: unable to find class referenced in signature (%s)",
+                pDescriptor);
+        }
+
+        if (pDescriptor[0] == '[') {
+            /* We are looking at an array descriptor. */
+
+            /*
+             * There should never be a problem loading primitive arrays.
+             */
+            if (pDescriptor[1] != 'L' && pDescriptor[1] != '[') {
+                LOG_VFY("VFY: invalid char in signature in '%s'",
+                    pDescriptor);
+                *pFailure = VERIFY_ERROR_GENERIC;
+            }
+
+            /*
+             * Try to continue with base array type.  This will let
+             * us pass basic stuff (e.g. get array len) that wouldn't
+             * fly with an Object.  This is NOT correct if the
+             * missing type is a primitive array, but we should never
+             * have a problem loading those.  (I'm not convinced this
+             * is correct or even useful.  Just use Object here?)
+             */
+            clazz = dvmFindClassNoInit("[Ljava/lang/Object;",
+                meth->clazz->classLoader);
+        } else if (pDescriptor[0] == 'L') {
+            /*
+             * We are looking at a non-array reference descriptor;
+             * try to continue with base reference type.
+             */
+            clazz = gDvm.classJavaLangObject;
+        } else {
+            /* We are looking at a primitive type. */
+            LOG_VFY("VFY: invalid char in signature in '%s'", pDescriptor);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+
+        if (clazz == NULL) {
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    }
+
+    if (dvmIsPrimitiveClass(clazz)) {
+        LOG_VFY("VFY: invalid use of primitive type '%s'", pDescriptor);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        clazz = NULL;
+    }
+
+    return clazz;
+}
+
+/*
+ * Look up a class reference in a signature.  Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature (that is, to
+ * the ';').
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig,
+    VerifyError* pFailure)
+{
+    const char* sig = *pSig;
+    const char* endp = sig;
+
+    assert(sig != NULL && *sig == 'L');
+
+    while (*++endp != ';' && *endp != '\0')
+        ;
+    if (*endp != ';') {
+        LOG_VFY("VFY: bad signature component '%s' (missing ';')", sig);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return NULL;
+    }
+
+    endp++;    /* Advance past the ';'. */
+    int typeLen = endp - sig;
+    char typeStr[typeLen+1]; /* +1 for the '\0' */
+    memcpy(typeStr, sig, typeLen);
+    typeStr[typeLen] = '\0';
+
+    *pSig = endp - 1; /* - 1 so that *pSig points at, not past, the ';' */
+
+    return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Look up an array class reference in a signature.  Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature.
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureArrayClass(const Method* meth,
+    const char** pSig, VerifyError* pFailure)
+{
+    const char* sig = *pSig;
+    const char* endp = sig;
+
+    assert(sig != NULL && *sig == '[');
+
+    /* find the end */
+    while (*++endp == '[' && *endp != '\0')
+        ;
+
+    if (*endp == 'L') {
+        while (*++endp != ';' && *endp != '\0')
+            ;
+        if (*endp != ';') {
+            LOG_VFY("VFY: bad signature component '%s' (missing ';')", sig);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+    }
+
+    int typeLen = endp - sig +1;
+    char typeStr[typeLen+1];
+    memcpy(typeStr, sig, typeLen);
+    typeStr[typeLen] = '\0';
+
+    *pSig = endp;
+
+    return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Set the register types for the first instruction in the method based on
+ * the method signature.
+ *
+ * This has the side-effect of validating the signature.
+ *
+ * Returns "true" on success.
+ */
+static bool setTypesFromSignature(const Method* meth, RegType* regTypes,
+    UninitInstanceMap* uninitMap)
+{
+    DexParameterIterator iterator;
+    int actualArgs, expectedArgs, argStart;
+    VerifyError failure = VERIFY_ERROR_NONE;
+    const char* descriptor;
+
+    dexParameterIteratorInit(&iterator, &meth->prototype);
+    argStart = meth->registersSize - meth->insSize;
+    expectedArgs = meth->insSize;     /* long/double count as two */
+    actualArgs = 0;
+
+    assert(argStart >= 0);      /* should have been verified earlier */
+
+    /*
+     * Include the "this" pointer.
+     */
+    if (!dvmIsStaticMethod(meth)) {
+        /*
+         * If this is a constructor for a class other than java.lang.Object,
+         * mark the first ("this") argument as uninitialized.  This restricts
+         * field access until the superclass constructor is called.
+         */
+        if (isInitMethod(meth) && meth->clazz != gDvm.classJavaLangObject) {
+            int uidx = setUninitInstance(uninitMap, kUninitThisArgAddr,
+                            meth->clazz);
+            assert(uidx == 0);
+            regTypes[argStart + actualArgs] = regTypeFromUninitIndex(uidx);
+        } else {
+            regTypes[argStart + actualArgs] = regTypeFromClass(meth->clazz);
+        }
+        actualArgs++;
+    }
+
+    for (;;) {
+        descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (descriptor == NULL) {
+            break;
+        }
+
+        if (actualArgs >= expectedArgs) {
+            LOG_VFY("VFY: expected %d args, found more (%s)",
+                expectedArgs, descriptor);
+            goto bad_sig;
+        }
+
+        switch (*descriptor) {
+        case 'L':
+        case '[':
+            /*
+             * We assume that reference arguments are initialized.  The
+             * only way it could be otherwise (assuming the caller was
+             * verified) is if the current method is <init>, but in that
+             * case it's effectively considered initialized the instant
+             * we reach here (in the sense that we can return without
+             * doing anything or call virtual methods).
+             */
+            {
+                ClassObject* clazz =
+                    lookupClassByDescriptor(meth, descriptor, &failure);
+                if (!VERIFY_OK(failure))
+                    goto bad_sig;
+                regTypes[argStart + actualArgs] = regTypeFromClass(clazz);
+            }
+            actualArgs++;
+            break;
+        case 'Z':
+            regTypes[argStart + actualArgs] = kRegTypeBoolean;
+            actualArgs++;
+            break;
+        case 'C':
+            regTypes[argStart + actualArgs] = kRegTypeChar;
+            actualArgs++;
+            break;
+        case 'B':
+            regTypes[argStart + actualArgs] = kRegTypeByte;
+            actualArgs++;
+            break;
+        case 'I':
+            regTypes[argStart + actualArgs] = kRegTypeInteger;
+            actualArgs++;
+            break;
+        case 'S':
+            regTypes[argStart + actualArgs] = kRegTypeShort;
+            actualArgs++;
+            break;
+        case 'F':
+            regTypes[argStart + actualArgs] = kRegTypeFloat;
+            actualArgs++;
+            break;
+        case 'D':
+            regTypes[argStart + actualArgs] = kRegTypeDoubleLo;
+            regTypes[argStart + actualArgs +1] = kRegTypeDoubleHi;
+            actualArgs += 2;
+            break;
+        case 'J':
+            regTypes[argStart + actualArgs] = kRegTypeLongLo;
+            regTypes[argStart + actualArgs +1] = kRegTypeLongHi;
+            actualArgs += 2;
+            break;
+        default:
+            LOG_VFY("VFY: unexpected signature type char '%c'", *descriptor);
+            goto bad_sig;
+        }
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d", expectedArgs, actualArgs);
+        goto bad_sig;
+    }
+
+    descriptor = dexProtoGetReturnType(&meth->prototype);
+
+    /*
+     * Validate return type.  We don't do the type lookup; just want to make
+     * sure that it has the right format.  Only major difference from the
+     * method argument format is that 'V' is supported.
+     */
+    switch (*descriptor) {
+    case 'I':
+    case 'C':
+    case 'S':
+    case 'B':
+    case 'Z':
+    case 'V':
+    case 'F':
+    case 'D':
+    case 'J':
+        if (*(descriptor+1) != '\0')
+            goto bad_sig;
+        break;
+    case '[':
+        /* single/multi, object/primitive */
+        while (*++descriptor == '[')
+            ;
+        if (*descriptor == 'L') {
+            while (*++descriptor != ';' && *descriptor != '\0')
+                ;
+            if (*descriptor != ';')
+                goto bad_sig;
+        } else {
+            if (*(descriptor+1) != '\0')
+                goto bad_sig;
+        }
+        break;
+    case 'L':
+        /* could be more thorough here, but shouldn't be required */
+        while (*++descriptor != ';' && *descriptor != '\0')
+            ;
+        if (*descriptor != ';')
+            goto bad_sig;
+        break;
+    default:
+        goto bad_sig;
+    }
+
+    return true;
+
+//fail:
+//    LOG_VFY_METH(meth, "VFY:  bad sig");
+//    return false;
+
+bad_sig:
+    {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOG_VFY("VFY: bad signature '%s' for %s.%s",
+            desc, meth->clazz->descriptor, meth->name);
+        free(desc);
+    }
+    return false;
+}
+
+/*
+ * Return the register type for the method.  We can't just use the
+ * already-computed DalvikJniReturnType, because if it's a reference type
+ * we need to do the class lookup.
+ *
+ * Returned references are assumed to be initialized.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+static RegType getMethodReturnType(const Method* meth)
+{
+    RegType type;
+    const char* descriptor = dexProtoGetReturnType(&meth->prototype);
+
+    switch (*descriptor) {
+    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 '[':
+        {
+            VerifyError failure = VERIFY_ERROR_NONE;
+            ClassObject* clazz =
+                lookupClassByDescriptor(meth, descriptor, &failure);
+            assert(VERIFY_OK(failure));
+            type = regTypeFromClass(clazz);
+        }
+        break;
+    default:
+        /* we verified signature return type earlier, so this is impossible */
+        assert(false);
+        type = kRegTypeConflict;
+        break;
+    }
+
+    return type;
+}
+
+/*
+ * Convert a single-character signature value (i.e. a primitive type) to
+ * the corresponding RegType.  This is intended for access to object fields
+ * holding primitive types.
+ *
+ * Returns kRegTypeUnknown for objects, arrays, and void.
+ */
+static RegType primSigCharToRegType(char sigChar)
+{
+    RegType type;
+
+    switch (sigChar) {
+    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 'F':
+        type = kRegTypeFloat;
+        break;
+    case 'D':
+        type = kRegTypeDoubleLo;
+        break;
+    case 'J':
+        type = kRegTypeLongLo;
+        break;
+    case 'V':
+    case 'L':
+    case '[':
+        type = kRegTypeUnknown;
+        break;
+    default:
+        assert(false);
+        type = kRegTypeUnknown;
+        break;
+    }
+
+    return type;
+}
+
+/*
+ * See if the method matches the MethodType.
+ */
+static bool isCorrectInvokeKind(MethodType methodType, Method* resMethod)
+{
+    switch (methodType) {
+    case METHOD_DIRECT:
+        return dvmIsDirectMethod(resMethod);
+    case METHOD_STATIC:
+        return dvmIsStaticMethod(resMethod);
+    case METHOD_VIRTUAL:
+    case METHOD_INTERFACE:
+        return !dvmIsDirectMethod(resMethod);
+    default:
+        return false;
+    }
+}
+
+/*
+ * Verify the arguments to a method.  We're executing in "method", making
+ * a call to the method reference in vB.
+ *
+ * If this is a "direct" invoke, we allow calls to <init>.  For calls to
+ * <init>, the first argument may be an uninitialized reference.  Otherwise,
+ * calls to anything starting with '<' will be rejected, as will any
+ * uninitialized reference arguments.
+ *
+ * For non-static method calls, this will verify that the method call is
+ * appropriate for the "this" argument.
+ *
+ * The method reference is in vBBBB.  The "isRange" parameter determines
+ * whether we use 0-4 "args" values or a range of registers defined by
+ * vAA and vCCCC.
+ *
+ * Widening conversions on integers and references are allowed, but
+ * narrowing conversions are not.
+ *
+ * Returns the resolved method on success, NULL on failure (with *pFailure
+ * set appropriately).
+ */
+static Method* verifyInvocationArgs(const Method* meth,
+    RegisterLine* registerLine, const int insnRegCount,
+    const DecodedInstruction* pDecInsn, UninitInstanceMap* uninitMap,
+    MethodType methodType, bool isRange, bool isSuper, VerifyError* pFailure)
+{
+    Method* resMethod;
+    char* sigOriginal = NULL;
+    const char* sig;
+    int expectedArgs;
+    int actualArgs;
+
+    /*
+     * 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,
+            pFailure);
+    }
+    if (resMethod == NULL) {
+        /* failed; print a meaningful failure message */
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+
+        const DexMethodId* pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+        const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+        char* methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+        const char* classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+        if (!gDvm.optimizing) {
+            std::string dotMissingClass =
+                dvmHumanReadableDescriptor(classDescriptor);
+            std::string dotMethClass =
+                dvmHumanReadableDescriptor(meth->clazz->descriptor);
+
+            ALOGI("Could not find method %s.%s, referenced from method %s.%s",
+                    dotMissingClass.c_str(), methodName,
+                    dotMethClass.c_str(), meth->name);
+        }
+
+        LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s",
+            dvmMethodTypeStr(methodType), pDecInsn->vB,
+            classDescriptor, methodName, methodDesc);
+        free(methodDesc);
+        if (VERIFY_OK(*pFailure))       /* not set for interface resolve */
+            *pFailure = VERIFY_ERROR_NO_METHOD;
+        goto fail;
+    }
+
+    /*
+     * Only time you can explicitly call a method starting with '<' is when
+     * making a "direct" invocation on "<init>".  There are additional
+     * restrictions but we don't enforce them here.
+     */
+    if (resMethod->name[0] == '<') {
+        if (methodType != METHOD_DIRECT || !isInitMethod(resMethod)) {
+            LOG_VFY("VFY: invalid call to %s.%s",
+                    resMethod->clazz->descriptor, resMethod->name);
+            goto bad_sig;
+        }
+    }
+
+    /*
+     * See if the method type implied by the invoke instruction matches the
+     * access flags for the target method.
+     */
+    if (!isCorrectInvokeKind(methodType, resMethod)) {
+        LOG_VFY("VFY: invoke type does not match method type of %s.%s",
+            resMethod->clazz->descriptor, resMethod->name);
+        goto fail;
+    }
+
+    /*
+     * If we're using invoke-super(method), make sure that the executing
+     * method's class' superclass has a vtable entry for the target method.
+     */
+    if (isSuper) {
+        assert(methodType == METHOD_VIRTUAL);
+        ClassObject* super = meth->clazz->super;
+        if (super == NULL || resMethod->methodIndex > super->vtableCount) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOG_VFY("VFY: invalid invoke-super from %s.%s to super %s.%s %s",
+                    meth->clazz->descriptor, meth->name,
+                    (super == NULL) ? "-" : super->descriptor,
+                    resMethod->name, desc);
+            free(desc);
+            *pFailure = VERIFY_ERROR_NO_METHOD;
+            goto fail;
+        }
+    }
+
+    /*
+     * We use vAA as our expected arg count, rather than resMethod->insSize,
+     * because we need to match the call to the signature.  Also, we might
+     * might be calling through an abstract method definition (which doesn't
+     * have register count values).
+     */
+    sigOriginal = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+    sig = sigOriginal;
+    expectedArgs = pDecInsn->vA;
+    actualArgs = 0;
+
+    /* caught by static verifier */
+    assert(isRange || expectedArgs <= 5);
+
+    if (expectedArgs > meth->outsSize) {
+        LOG_VFY("VFY: invalid arg count (%d) exceeds outsSize (%d)",
+            expectedArgs, meth->outsSize);
+        goto fail;
+    }
+
+    if (*sig++ != '(')
+        goto bad_sig;
+
+    /*
+     * Check the "this" argument, which must be an instance of the class
+     * that declared the method.  For an interface class, we don't do the
+     * full interface merge, so we can't do a rigorous check here (which
+     * is okay since we have to do it at runtime).
+     */
+    if (!dvmIsStaticMethod(resMethod)) {
+        ClassObject* actualThisRef;
+        RegType actualArgType;
+
+        actualArgType = getInvocationThis(registerLine, pDecInsn, pFailure);
+        if (!VERIFY_OK(*pFailure))
+            goto fail;
+
+        if (regTypeIsUninitReference(actualArgType) && resMethod->name[0] != '<')
+        {
+            LOG_VFY("VFY: 'this' arg must be initialized");
+            goto fail;
+        }
+        if (methodType != METHOD_INTERFACE && actualArgType != kRegTypeZero) {
+            actualThisRef = regTypeReferenceToClass(actualArgType, uninitMap);
+            if (!dvmInstanceof(actualThisRef, resMethod->clazz)) {
+                LOG_VFY("VFY: 'this' arg '%s' not instance of '%s'",
+                        actualThisRef->descriptor,
+                        resMethod->clazz->descriptor);
+                goto fail;
+            }
+        }
+        actualArgs++;
+    }
+
+    /*
+     * Process the target method's signature.  This signature may or may not
+     * have been verified, so we can't assume it's properly formed.
+     */
+    while (*sig != '\0' && *sig != ')') {
+        if (actualArgs >= expectedArgs) {
+            LOG_VFY("VFY: expected %d args, found more (%c)",
+                expectedArgs, *sig);
+            goto bad_sig;
+        }
+
+        u4 getReg;
+        if (isRange)
+            getReg = pDecInsn->vC + actualArgs;
+        else
+            getReg = pDecInsn->arg[actualArgs];
+
+        switch (*sig) {
+        case 'L':
+            {
+                ClassObject* clazz = lookupSignatureClass(meth, &sig, pFailure);
+                if (!VERIFY_OK(*pFailure))
+                    goto bad_sig;
+                verifyRegisterType(registerLine, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case '[':
+            {
+                ClassObject* clazz =
+                    lookupSignatureArrayClass(meth, &sig, pFailure);
+                if (!VERIFY_OK(*pFailure))
+                    goto bad_sig;
+                verifyRegisterType(registerLine, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case 'Z':
+            verifyRegisterType(registerLine, getReg, kRegTypeBoolean, pFailure);
+            actualArgs++;
+            break;
+        case 'C':
+            verifyRegisterType(registerLine, getReg, kRegTypeChar, pFailure);
+            actualArgs++;
+            break;
+        case 'B':
+            verifyRegisterType(registerLine, getReg, kRegTypeByte, pFailure);
+            actualArgs++;
+            break;
+        case 'I':
+            verifyRegisterType(registerLine, getReg, kRegTypeInteger, pFailure);
+            actualArgs++;
+            break;
+        case 'S':
+            verifyRegisterType(registerLine, getReg, kRegTypeShort, pFailure);
+            actualArgs++;
+            break;
+        case 'F':
+            verifyRegisterType(registerLine, getReg, kRegTypeFloat, pFailure);
+            actualArgs++;
+            break;
+        case 'D':
+            verifyRegisterType(registerLine, getReg, kRegTypeDoubleLo, pFailure);
+            actualArgs += 2;
+            break;
+        case 'J':
+            verifyRegisterType(registerLine, getReg, kRegTypeLongLo, pFailure);
+            actualArgs += 2;
+            break;
+        default:
+            LOG_VFY("VFY: invocation target: bad signature type char '%c'",
+                *sig);
+            goto bad_sig;
+        }
+
+        sig++;
+    }
+    if (*sig != ')') {
+        char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+        LOG_VFY("VFY: invocation target: bad signature '%s'", desc);
+        free(desc);
+        goto bad_sig;
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d", expectedArgs, actualArgs);
+        goto bad_sig;
+    }
+
+    free(sigOriginal);
+    return resMethod;
+
+bad_sig:
+    if (resMethod != NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+        LOG_VFY("VFY:  rejecting call to %s.%s %s",
+            resMethod->clazz->descriptor, resMethod->name, desc);
+        free(desc);
+    }
+
+fail:
+    free(sigOriginal);
+    if (*pFailure == VERIFY_ERROR_NONE)
+        *pFailure = VERIFY_ERROR_GENERIC;
+    return NULL;
+}
+
+/*
+ * Get the class object for the type of data stored in a field.  This isn't
+ * stored in the Field struct, so we have to recover it from the signature.
+ *
+ * This only works for reference types.  Don't call this for primitive types.
+ *
+ * If we can't find the class, we return java.lang.Object, so that
+ * verification can continue if a field is only accessed in trivial ways.
+ */
+static ClassObject* getFieldClass(const Method* meth, const Field* field)
+{
+    ClassObject* fieldClass;
+    const char* signature = field->signature;
+
+    if ((*signature == 'L') || (*signature == '[')) {
+        fieldClass = dvmFindClassNoInit(signature,
+                meth->clazz->classLoader);
+    } else {
+        return NULL;
+    }
+
+    if (fieldClass == NULL) {
+        dvmClearOptException(dvmThreadSelf());
+        ALOGV("VFY: unable to find class '%s' for field %s.%s, trying Object",
+            field->signature, meth->clazz->descriptor, field->name);
+        fieldClass = gDvm.classJavaLangObject;
+    } else {
+        assert(!dvmIsPrimitiveClass(fieldClass));
+    }
+    return fieldClass;
+}
+
+
+/*
+ * ===========================================================================
+ *      Register operations
+ * ===========================================================================
+ */
+
+/*
+ * Get the type of register N.
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ */
+static inline RegType getRegisterType(const RegisterLine* registerLine, u4 vsrc)
+{
+    return registerLine->regTypes[vsrc];
+}
+
+/*
+ * Get the value from a register, and cast it to a ClassObject.  Sets
+ * "*pFailure" if something fails.
+ *
+ * This fails if the register holds an uninitialized class.
+ *
+ * If the register holds kRegTypeZero, this returns a NULL pointer.
+ */
+static ClassObject* getClassFromRegister(const RegisterLine* registerLine,
+    u4 vsrc, VerifyError* pFailure)
+{
+    ClassObject* clazz = NULL;
+    RegType type;
+
+    /* get the element type of the array held in vsrc */
+    type = getRegisterType(registerLine, vsrc);
+
+    /* if "always zero", we allow it to fail at runtime */
+    if (type == kRegTypeZero)
+        goto bail;
+
+    if (!regTypeIsReference(type)) {
+        LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)",
+            vsrc, type);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+    if (regTypeIsUninitReference(type)) {
+        LOG_VFY("VFY: register %u holds uninitialized reference", vsrc);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    clazz = regTypeInitializedReferenceToClass(type);
+
+bail:
+    return clazz;
+}
+
+/*
+ * Get the "this" pointer from a non-static method invocation.  This
+ * returns the RegType so the caller can decide whether it needs the
+ * reference to be initialized or not.  (Can also return kRegTypeZero
+ * if the reference can only be zero at this point.)
+ *
+ * The argument count is in vA, and the first argument is in vC, for both
+ * "simple" and "range" versions.  We just need to make sure vA is >= 1
+ * and then return vC.
+ */
+static RegType getInvocationThis(const RegisterLine* registerLine,
+    const DecodedInstruction* pDecInsn, VerifyError* pFailure)
+{
+    RegType thisType = kRegTypeUnknown;
+
+    if (pDecInsn->vA < 1) {
+        LOG_VFY("VFY: invoke lacks 'this'");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    /* get the element type of the array held in vsrc */
+    thisType = getRegisterType(registerLine, pDecInsn->vC);
+    if (!regTypeIsReference(thisType)) {
+        LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)",
+            pDecInsn->vC, thisType);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+bail:
+    return thisType;
+}
+
+/*
+ * Set the type of register N, verifying that the register is valid.  If
+ * "newType" is the "Lo" part of a 64-bit value, register N+1 will be
+ * set to "newType+1".
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ *
+ * TODO: clear mon stack bits
+ */
+static void setRegisterType(RegisterLine* registerLine, u4 vdst,
+    RegType newType)
+{
+    RegType* insnRegs = registerLine->regTypes;
+
+    switch (newType) {
+    case kRegTypeUnknown:
+    case kRegTypeBoolean:
+    case kRegTypeOne:
+    case kRegTypeConstByte:
+    case kRegTypeConstPosByte:
+    case kRegTypeConstShort:
+    case kRegTypeConstPosShort:
+    case kRegTypeConstChar:
+    case kRegTypeConstInteger:
+    case kRegTypeByte:
+    case kRegTypePosByte:
+    case kRegTypeShort:
+    case kRegTypePosShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+    case kRegTypeFloat:
+    case kRegTypeZero:
+    case kRegTypeUninit:
+        insnRegs[vdst] = newType;
+        break;
+    case kRegTypeConstLo:
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        insnRegs[vdst] = newType;
+        insnRegs[vdst+1] = newType+1;
+        break;
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+        /* should never set these explicitly */
+        ALOGE("BUG: explicit set of high register type");
+        dvmAbort();
+        break;
+
+    default:
+        /* can't switch for ref types, so we check explicitly */
+        if (regTypeIsReference(newType)) {
+            insnRegs[vdst] = newType;
+
+            /*
+             * In most circumstances we won't see a reference to a primitive
+             * class here (e.g. "D"), since that would mean the object in the
+             * register is actually a primitive type.  It can happen as the
+             * result of an assumed-successful check-cast instruction in
+             * which the second argument refers to a primitive class.  (In
+             * practice, such an instruction will always throw an exception.)
+             *
+             * This is not an issue for instructions like const-class, where
+             * the object in the register is a java.lang.Class instance.
+             */
+            break;
+        }
+        /* bad type - fall through */
+
+    case kRegTypeConflict:      // should only be set during a merge
+        ALOGE("BUG: set register to unknown type %d", newType);
+        dvmAbort();
+        break;
+    }
+
+    /*
+     * Clear the monitor entry bits for this register.
+     */
+    if (registerLine->monitorEntries != NULL)
+        registerLine->monitorEntries[vdst] = 0;
+}
+
+/*
+ * Verify that the contents of the specified register have the specified
+ * type (or can be converted to it through an implicit widening conversion).
+ *
+ * This will modify the type of the source register if it was originally
+ * derived from a constant to prevent mixing of int/float and long/double.
+ *
+ * If "vsrc" is a reference, both it and the "vsrc" register must be
+ * initialized ("vsrc" may be Zero).  This will verify that the value in
+ * the register is an instance of checkType, or if checkType is an
+ * interface, verify that the register implements checkType.
+ */
+static void verifyRegisterType(RegisterLine* registerLine, u4 vsrc,
+    RegType checkType, VerifyError* pFailure)
+{
+    const RegType* insnRegs = registerLine->regTypes;
+    RegType srcType = insnRegs[vsrc];
+
+    //ALOGD("check-reg v%u = %d", vsrc, checkType);
+    switch (checkType) {
+    case kRegTypeFloat:
+    case kRegTypeBoolean:
+    case kRegTypePosByte:
+    case kRegTypeByte:
+    case kRegTypePosShort:
+    case kRegTypeShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+        if (!canConvertTo1nr(srcType, checkType)) {
+            LOG_VFY("VFY: register1 v%u type %d, wanted %d",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* Update type if result is float */
+        if (checkType == kRegTypeFloat) {
+            setRegisterType(registerLine, vsrc, checkType);
+        } else {
+            /* Update const type to actual type after use */
+            setRegisterType(registerLine, vsrc, constTypeToRegType(srcType));
+        }
+        break;
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        if (insnRegs[vsrc+1] != srcType+1) {
+            LOG_VFY("VFY: register2 v%u-%u values %d,%d",
+                vsrc, vsrc+1, insnRegs[vsrc], insnRegs[vsrc+1]);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        } else if (!canConvertTo2(srcType, checkType)) {
+            LOG_VFY("VFY: register2 v%u type %d, wanted %d",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* Update type if source is from const */
+        if (srcType == kRegTypeConstLo) {
+            setRegisterType(registerLine, vsrc, checkType);
+        }
+        break;
+    case kRegTypeConstLo:
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+    case kRegTypeZero:
+    case kRegTypeOne:
+    case kRegTypeUnknown:
+    case kRegTypeConflict:
+        /* should never be checking for these explicitly */
+        assert(false);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    case kRegTypeUninit:
+    default:
+        /* make sure checkType is initialized reference */
+        if (!regTypeIsReference(checkType)) {
+            LOG_VFY("VFY: unexpected check type %d", checkType);
+            assert(false);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(checkType)) {
+            LOG_VFY("VFY: uninitialized ref not expected as reg check");
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* make sure srcType is initialized reference or always-NULL */
+        if (!regTypeIsReference(srcType)) {
+            LOG_VFY("VFY: register1 v%u type %d, wanted ref", vsrc, srcType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(srcType)) {
+            LOG_VFY("VFY: register1 v%u holds uninitialized ref", vsrc);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        /* if the register isn't Zero, make sure it's an instance of check */
+        if (srcType != kRegTypeZero) {
+            ClassObject* srcClass = regTypeInitializedReferenceToClass(srcType);
+            ClassObject* checkClass = regTypeInitializedReferenceToClass(checkType);
+            assert(srcClass != NULL);
+            assert(checkClass != NULL);
+
+            if (dvmIsInterfaceClass(checkClass)) {
+                /*
+                 * All objects implement all interfaces as far as the
+                 * verifier is concerned.  The runtime has to sort it out.
+                 * See comments above findCommonSuperclass.
+                 */
+                /*
+                if (srcClass != checkClass &&
+                    !dvmImplements(srcClass, checkClass))
+                {
+                    LOG_VFY("VFY: %s does not implement %s",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+                */
+            } else {
+                if (!dvmInstanceof(srcClass, checkClass)) {
+                    LOG_VFY("VFY: %s is not instance of %s",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+            }
+        }
+        break;
+    }
+}
+
+/*
+ * Set the type of the "result" register.
+ */
+static void setResultRegisterType(RegisterLine* registerLine,
+    const int insnRegCount, RegType newType)
+{
+    setRegisterType(registerLine, RESULT_REGISTER(insnRegCount), newType);
+}
+
+
+/*
+ * Update all registers holding "uninitType" to instead hold the
+ * corresponding initialized reference type.  This is called when an
+ * appropriate <init> method is invoked -- all copies of the reference
+ * must be marked as initialized.
+ */
+static void markRefsAsInitialized(RegisterLine* registerLine, int insnRegCount,
+    UninitInstanceMap* uninitMap, RegType uninitType, VerifyError* pFailure)
+{
+    RegType* insnRegs = registerLine->regTypes;
+    ClassObject* clazz;
+    RegType initType;
+    int i, changed;
+
+    clazz = getUninitInstance(uninitMap, regTypeToUninitIndex(uninitType));
+    if (clazz == NULL) {
+        ALOGE("VFY: unable to find type=%#x (idx=%d)",
+            uninitType, regTypeToUninitIndex(uninitType));
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+    initType = regTypeFromClass(clazz);
+
+    changed = 0;
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitType) {
+            insnRegs[i] = initType;
+            changed++;
+        }
+    }
+    //ALOGD("VFY: marked %d registers as initialized", changed);
+    assert(changed > 0);
+
+    return;
+}
+
+/*
+ * We're creating a new instance of class C at address A.  Any registers
+ * holding instances previously created at address A must be initialized
+ * by now.  If not, we mark them as "conflict" to prevent them from being
+ * used (otherwise, markRefsAsInitialized would mark the old ones and the
+ * new ones at the same time).
+ */
+static void markUninitRefsAsInvalid(RegisterLine* registerLine,
+    int insnRegCount, UninitInstanceMap* uninitMap, RegType uninitType)
+{
+    RegType* insnRegs = registerLine->regTypes;
+    int i, changed;
+
+    changed = 0;
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitType) {
+            insnRegs[i] = kRegTypeConflict;
+            if (registerLine->monitorEntries != NULL)
+                registerLine->monitorEntries[i] = 0;
+            changed++;
+        }
+    }
+
+    //if (changed)
+    //    ALOGD("VFY: marked %d uninitialized registers as invalid", changed);
+}
+
+/*
+ * Find the register line for the specified instruction in the current method.
+ */
+static inline RegisterLine* getRegisterLine(const RegisterTable* regTable,
+    int insnIdx)
+{
+    return &regTable->registerLines[insnIdx];
+}
+
+/*
+ * Copy a register line.
+ */
+static inline void copyRegisterLine(RegisterLine* dst, const RegisterLine* src,
+    size_t numRegs)
+{
+    memcpy(dst->regTypes, src->regTypes, numRegs * sizeof(RegType));
+
+    assert((src->monitorEntries == NULL && dst->monitorEntries == NULL) ||
+           (src->monitorEntries != NULL && dst->monitorEntries != NULL));
+    if (dst->monitorEntries != NULL) {
+        assert(dst->monitorStack != NULL);
+        memcpy(dst->monitorEntries, src->monitorEntries,
+            numRegs * sizeof(MonitorEntries));
+        memcpy(dst->monitorStack, src->monitorStack,
+            kMaxMonitorStackDepth * sizeof(u4));
+        dst->monitorStackTop = src->monitorStackTop;
+    }
+}
+
+/*
+ * Copy a register line into the table.
+ */
+static inline void copyLineToTable(RegisterTable* regTable, int insnIdx,
+    const RegisterLine* src)
+{
+    RegisterLine* dst = getRegisterLine(regTable, insnIdx);
+    assert(dst->regTypes != NULL);
+    copyRegisterLine(dst, src, regTable->insnRegCountPlus);
+}
+
+/*
+ * Copy a register line out of the table.
+ */
+static inline void copyLineFromTable(RegisterLine* dst,
+    const RegisterTable* regTable, int insnIdx)
+{
+    RegisterLine* src = getRegisterLine(regTable, insnIdx);
+    assert(src->regTypes != NULL);
+    copyRegisterLine(dst, src, regTable->insnRegCountPlus);
+}
+
+
+#ifndef NDEBUG
+/*
+ * Compare two register lines.  Returns 0 if they match.
+ *
+ * Using this for a sort is unwise, since the value can change based on
+ * machine endianness.
+ */
+static inline int compareLineToTable(const RegisterTable* regTable,
+    int insnIdx, const RegisterLine* line2)
+{
+    const RegisterLine* line1 = getRegisterLine(regTable, insnIdx);
+    if (line1->monitorEntries != NULL) {
+        int result;
+
+        if (line2->monitorEntries == NULL)
+            return 1;
+        result = memcmp(line1->monitorEntries, line2->monitorEntries,
+            regTable->insnRegCountPlus * sizeof(MonitorEntries));
+        if (result != 0) {
+            LOG_VFY("monitorEntries mismatch");
+            return result;
+        }
+        result = line1->monitorStackTop - line2->monitorStackTop;
+        if (result != 0) {
+            LOG_VFY("monitorStackTop mismatch");
+            return result;
+        }
+        result = memcmp(line1->monitorStack, line2->monitorStack,
+            line1->monitorStackTop);
+        if (result != 0) {
+            LOG_VFY("monitorStack mismatch");
+            return result;
+        }
+    }
+    return memcmp(line1->regTypes, line2->regTypes,
+            regTable->insnRegCountPlus * sizeof(RegType));
+}
+#endif
+
+/*
+ * Register type categories, for type checking.
+ *
+ * The spec says category 1 includes boolean, byte, char, short, int, float,
+ * reference, and returnAddress.  Category 2 includes long and double.
+ *
+ * We treat object references separately, so we have "category1nr".  We
+ * don't support jsr/ret, so there is no "returnAddress" type.
+ */
+enum TypeCategory {
+    kTypeCategoryUnknown = 0,
+    kTypeCategory1nr,           // boolean, byte, char, short, int, float
+    kTypeCategory2,             // long, double
+    kTypeCategoryRef,           // object reference
+};
+
+/*
+ * See if "type" matches "cat".  All we're really looking for here is that
+ * we're not mixing and matching 32-bit and 64-bit quantities, and we're
+ * not mixing references with numerics.  (For example, the arguments to
+ * "a < b" could be integers of different sizes, but they must both be
+ * integers.  Dalvik is less specific about int vs. float, so we treat them
+ * as equivalent here.)
+ *
+ * For category 2 values, "type" must be the "low" half of the value.
+ *
+ * Sets "*pFailure" if something looks wrong.
+ */
+static void checkTypeCategory(RegType type, TypeCategory cat,
+    VerifyError* pFailure)
+{
+    switch (cat) {
+    case kTypeCategory1nr:
+        switch (type) {
+        case kRegTypeZero:
+        case kRegTypeOne:
+        case kRegTypeBoolean:
+        case kRegTypeConstPosByte:
+        case kRegTypeConstByte:
+        case kRegTypeConstPosShort:
+        case kRegTypeConstShort:
+        case kRegTypeConstChar:
+        case kRegTypeConstInteger:
+        case kRegTypePosByte:
+        case kRegTypeByte:
+        case kRegTypePosShort:
+        case kRegTypeShort:
+        case kRegTypeChar:
+        case kRegTypeInteger:
+        case kRegTypeFloat:
+            break;
+        default:
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+
+    case kTypeCategory2:
+        switch (type) {
+        case kRegTypeConstLo:
+        case kRegTypeLongLo:
+        case kRegTypeDoubleLo:
+            break;
+        default:
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+
+    case kTypeCategoryRef:
+        if (type != kRegTypeZero && !regTypeIsReference(type))
+            *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+
+    default:
+        assert(false);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+    }
+}
+
+/*
+ * For a category 2 register pair, verify that "typeh" is the appropriate
+ * high part for "typel".
+ *
+ * Does not verify that "typel" is in fact the low part of a 64-bit
+ * register pair.
+ */
+static void checkWidePair(RegType typel, RegType typeh, VerifyError* pFailure)
+{
+    if ((typeh != typel+1))
+        *pFailure = VERIFY_ERROR_GENERIC;
+}
+
+/*
+ * Implement category-1 "move" instructions.  Copy a 32-bit value from
+ * "vsrc" to "vdst".
+ */
+static void copyRegister1(RegisterLine* registerLine, u4 vdst, u4 vsrc,
+    TypeCategory cat, VerifyError* pFailure)
+{
+    assert(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+    RegType type = getRegisterType(registerLine, vsrc);
+    checkTypeCategory(type, cat, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy1 v%u<-v%u type=%d cat=%d", vdst, vsrc, type, cat);
+    } else {
+        setRegisterType(registerLine, vdst, type);
+        if (cat == kTypeCategoryRef && registerLine->monitorEntries != NULL) {
+            registerLine->monitorEntries[vdst] =
+                registerLine->monitorEntries[vsrc];
+        }
+    }
+}
+
+/*
+ * Implement category-2 "move" instructions.  Copy a 64-bit value from
+ * "vsrc" to "vdst".  This copies both halves of the register.
+ */
+static void copyRegister2(RegisterLine* registerLine, u4 vdst, u4 vsrc,
+    VerifyError* pFailure)
+{
+    RegType typel = getRegisterType(registerLine, vsrc);
+    RegType typeh = getRegisterType(registerLine, vsrc+1);
+
+    checkTypeCategory(typel, kTypeCategory2, pFailure);
+    checkWidePair(typel, typeh, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy2 v%u<-v%u type=%d/%d", vdst, vsrc, typel, typeh);
+    } else {
+        setRegisterType(registerLine, vdst, typel);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Implement "move-result".  Copy the category-1 value from the result
+ * register to another register, and reset the result register.
+ */
+static void copyResultRegister1(RegisterLine* registerLine,
+    const int insnRegCount, u4 vdst, TypeCategory cat, VerifyError* pFailure)
+{
+    RegType type;
+    u4 vsrc;
+
+    assert(vdst < (u4) insnRegCount);
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    type = getRegisterType(registerLine, vsrc);
+    checkTypeCategory(type, cat, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes1 v%u<-v%u cat=%d type=%d",
+            vdst, vsrc, cat, type);
+    } else {
+        setRegisterType(registerLine, vdst, type);
+        setRegisterType(registerLine, vsrc, kRegTypeUnknown);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Implement "move-result-wide".  Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ */
+static void copyResultRegister2(RegisterLine* registerLine,
+    const int insnRegCount, u4 vdst, VerifyError* pFailure)
+{
+    RegType typel, typeh;
+    u4 vsrc;
+
+    assert(vdst < (u4) insnRegCount);
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    typel = getRegisterType(registerLine, vsrc);
+    typeh = getRegisterType(registerLine, vsrc+1);
+    checkTypeCategory(typel, kTypeCategory2, pFailure);
+    checkWidePair(typel, typeh, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes2 v%u<-v%u type=%d/%d",
+            vdst, vsrc, typel, typeh);
+    } else {
+        setRegisterType(registerLine, vdst, typel);
+        setRegisterType(registerLine, vsrc, kRegTypeUnknown);
+        setRegisterType(registerLine, vsrc+1, kRegTypeUnknown);
+        /* target monitor stack bits will be cleared */
+    }
+}
+
+/*
+ * Verify types for a simple two-register instruction (e.g. "neg-int").
+ * "dstType" is stored into vA, and "srcType" is verified against vB.
+ */
+static void checkUnop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType, VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType, pFailure);
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * We're performing an operation like "and-int/2addr" that can be
+ * performed on booleans as well as integers.  We get no indication of
+ * boolean-ness, but we can infer it from the types of the arguments.
+ *
+ * Assumes we've already validated reg1/reg2.
+ *
+ * TODO: consider generalizing this.  The key principle is that the
+ * result of a bitwise operation can only be as wide as the widest of
+ * the operands.  You can safely AND/OR/XOR two chars together and know
+ * you still have a char, so it's reasonable for the compiler or "dx"
+ * to skip the int-to-char instruction.  (We need to do this for boolean
+ * because there is no int-to-boolean operation.)
+ *
+ * Returns true if both args are Boolean, Zero, or One.
+ */
+static bool upcastBooleanOp(RegisterLine* registerLine, u4 reg1, u4 reg2)
+{
+    RegType type1, type2;
+
+    type1 = getRegisterType(registerLine, reg1);
+    type2 = getRegisterType(registerLine, reg2);
+
+    if ((type1 == kRegTypeBoolean || type1 == kRegTypeZero ||
+            type1 == kRegTypeOne) &&
+        (type2 == kRegTypeBoolean || type2 == kRegTypeZero ||
+            type2 == kRegTypeOne))
+    {
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Verify types for A two-register instruction with a literal constant
+ * (e.g. "add-int/lit8").  "dstType" is stored into vA, and "srcType" is
+ * verified against vB.
+ *
+ * If "checkBooleanOp" is set, we use the constant value in vC.
+ */
+static void checkLitop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType, bool checkBooleanOp,
+    VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        /* check vB with the call, then check the constant manually */
+        if (upcastBooleanOp(registerLine, pDecInsn->vB, pDecInsn->vB)
+            && (pDecInsn->vC == 0 || pDecInsn->vC == 1))
+        {
+            dstType = kRegTypeBoolean;
+        }
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Verify types for a simple three-register instruction (e.g. "add-int").
+ * "dstType" is stored into vA, and "srcType1"/"srcType2" are verified
+ * against vB/vC.
+ */
+static void checkBinop(RegisterLine* registerLine, DecodedInstruction* pDecInsn,
+    RegType dstType, RegType srcType1, RegType srcType2, bool checkBooleanOp,
+    VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType1, pFailure);
+    verifyRegisterType(registerLine, pDecInsn->vC, srcType2, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(registerLine, pDecInsn->vB, pDecInsn->vC))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Verify types for a binary "2addr" operation.  "srcType1"/"srcType2"
+ * are verified against vA/vB, then "dstType" is stored into vA.
+ */
+static void checkBinop2addr(RegisterLine* registerLine,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+    RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+    verifyRegisterType(registerLine, pDecInsn->vA, srcType1, pFailure);
+    verifyRegisterType(registerLine, pDecInsn->vB, srcType2, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(registerLine, pDecInsn->vA, pDecInsn->vB))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(registerLine, pDecInsn->vA, dstType);
+}
+
+/*
+ * Treat right-shifting as a narrowing conversion when possible.
+ *
+ * For example, right-shifting an int 24 times results in a value that can
+ * be treated as a byte.
+ *
+ * Things get interesting when contemplating sign extension.  Right-
+ * shifting an integer by 16 yields a value that can be represented in a
+ * "short" but not a "char", but an unsigned right shift by 16 yields a
+ * value that belongs in a char rather than a short.  (Consider what would
+ * happen if the result of the shift were cast to a char or short and then
+ * cast back to an int.  If sign extension, or the lack thereof, causes
+ * a change in the 32-bit representation, then the conversion was lossy.)
+ *
+ * A signed right shift by 17 on an integer results in a short.  An unsigned
+ * right shfit by 17 on an integer results in a posshort, which can be
+ * assigned to a short or a char.
+ *
+ * An unsigned right shift on a short can actually expand the result into
+ * a 32-bit integer.  For example, 0xfffff123 >>> 8 becomes 0x00fffff1,
+ * which can't be represented in anything smaller than an int.
+ *
+ * javac does not generate code that takes advantage of this, but some
+ * of the code optimizers do.  It's generally a peephole optimization
+ * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is
+ * replaced by (bipush 24, ishr).  Knowing that shifting a short 8 times
+ * to the right yields a byte is really more than we need to handle the
+ * code that's out there, but support is not much more complex than just
+ * handling integer.
+ *
+ * Right-shifting never yields a boolean value.
+ *
+ * Returns the new register type.
+ */
+static RegType adjustForRightShift(RegisterLine* registerLine, int reg,
+    unsigned int shiftCount, bool isUnsignedShift, VerifyError* pFailure)
+{
+    RegType srcType = getRegisterType(registerLine, reg);
+    RegType newType;
+
+    /* convert const derived types to their actual types */
+    srcType = constTypeToRegType(srcType);
+
+    /* no-op */
+    if (shiftCount == 0)
+        return srcType;
+
+    /* safe defaults */
+    if (isUnsignedShift)
+        newType = kRegTypeInteger;
+    else
+        newType = srcType;
+
+    if (shiftCount >= 32) {
+        LOG_VFY("Got unexpectedly large shift count %u", shiftCount);
+        /* fail? */
+        return newType;
+    }
+
+    switch (srcType) {
+    case kRegTypeInteger:               /* 32-bit signed value */
+        if (isUnsignedShift) {
+            if (shiftCount > 24)
+                newType = kRegTypePosByte;
+            else if (shiftCount >= 16)
+                newType = kRegTypeChar;
+        } else {
+            if (shiftCount >= 24)
+                newType = kRegTypeByte;
+            else if (shiftCount >= 16)
+                newType = kRegTypeShort;
+        }
+        break;
+    case kRegTypeShort:                 /* 16-bit signed value */
+        if (isUnsignedShift) {
+            /* default (kRegTypeInteger) is correct */
+        } else {
+            if (shiftCount >= 8)
+                newType = kRegTypeByte;
+        }
+        break;
+    case kRegTypePosShort:              /* 15-bit unsigned value */
+        if (shiftCount >= 8)
+            newType = kRegTypePosByte;
+        break;
+    case kRegTypeChar:                  /* 16-bit unsigned value */
+        if (shiftCount > 8)
+            newType = kRegTypePosByte;
+        break;
+    case kRegTypeByte:                  /* 8-bit signed value */
+        /* defaults (u=kRegTypeInteger / s=srcType) are correct */
+        break;
+    case kRegTypePosByte:               /* 7-bit unsigned value */
+        /* always use newType=srcType */
+        newType = srcType;
+        break;
+    case kRegTypeZero:                  /* 1-bit unsigned value */
+    case kRegTypeOne:
+    case kRegTypeBoolean:
+        /* unnecessary? */
+        newType = kRegTypeZero;
+        break;
+    default:
+        /* long, double, references; shouldn't be here! */
+        assert(false);
+        break;
+    }
+
+    if (newType != srcType) {
+        LOGVV("narrowing: %d(%d) --> %d to %d",
+            shiftCount, isUnsignedShift, srcType, newType);
+    } else {
+        LOGVV("not narrowed: %d(%d) --> %d",
+            shiftCount, isUnsignedShift, srcType);
+    }
+    return newType;
+}
+
+
+/*
+ * ===========================================================================
+ *      Register merge
+ * ===========================================================================
+ */
+
+/*
+ * Compute the "class depth" of a class.  This is the distance from the
+ * class to the top of the tree, chasing superclass links.  java.lang.Object
+ * has a class depth of 0.
+ */
+static int getClassDepth(ClassObject* clazz)
+{
+    int depth = 0;
+
+    while (clazz->super != NULL) {
+        clazz = clazz->super;
+        depth++;
+    }
+    return depth;
+}
+
+/*
+ * Given two classes, walk up the superclass tree to find a common
+ * ancestor.  (Called from findCommonSuperclass().)
+ *
+ * TODO: consider caching the class depth in the class object so we don't
+ * have to search for it here.
+ */
+static ClassObject* digForSuperclass(ClassObject* c1, ClassObject* c2)
+{
+    int depth1, depth2;
+
+    depth1 = getClassDepth(c1);
+    depth2 = getClassDepth(c2);
+
+    if (gDebugVerbose) {
+        LOGVV("COMMON: %s(%d) + %s(%d)",
+            c1->descriptor, depth1, c2->descriptor, depth2);
+    }
+
+    /* pull the deepest one up */
+    if (depth1 > depth2) {
+        while (depth1 > depth2) {
+            c1 = c1->super;
+            depth1--;
+        }
+    } else {
+        while (depth2 > depth1) {
+            c2 = c2->super;
+            depth2--;
+        }
+    }
+
+    /* walk up in lock-step */
+    while (c1 != c2) {
+        c1 = c1->super;
+        c2 = c2->super;
+
+        assert(c1 != NULL && c2 != NULL);
+    }
+
+    if (gDebugVerbose) {
+        LOGVV("      : --> %s", c1->descriptor);
+    }
+    return c1;
+}
+
+/*
+ * Merge two array classes.  We can't use the general "walk up to the
+ * superclass" merge because the superclass of an array is always Object.
+ * We want String[] + Integer[] = Object[].  This works for higher dimensions
+ * as well, e.g. String[][] + Integer[][] = Object[][].
+ *
+ * If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
+ *
+ * If Class implements Type, Class[] + Type[] = Type[].
+ *
+ * If the dimensions don't match, we want to convert to an array of Object
+ * with the least dimension, e.g. String[][] + String[][][][] = Object[][].
+ *
+ * Arrays of primitive types effectively have one less dimension when
+ * merging.  int[] + float[] = Object, int[] + String[] = Object,
+ * int[][] + float[][] = Object[], int[][] + String[] = Object[].  (The
+ * only time this function doesn't return an array class is when one of
+ * the arguments is a 1-dimensional primitive array.)
+ *
+ * This gets a little awkward because we may have to ask the VM to create
+ * a new array type with the appropriate element and dimensions.  However, we
+ * shouldn't be doing this often.
+ */
+static ClassObject* findCommonArraySuperclass(ClassObject* c1, ClassObject* c2)
+{
+    ClassObject* arrayClass = NULL;
+    ClassObject* commonElem;
+    int arrayDim1, arrayDim2;
+    int i, numDims;
+    bool hasPrimitive = false;
+
+    arrayDim1 = c1->arrayDim;
+    arrayDim2 = c2->arrayDim;
+    assert(c1->arrayDim > 0);
+    assert(c2->arrayDim > 0);
+
+    if (dvmIsPrimitiveClass(c1->elementClass)) {
+        arrayDim1--;
+        hasPrimitive = true;
+    }
+    if (dvmIsPrimitiveClass(c2->elementClass)) {
+        arrayDim2--;
+        hasPrimitive = true;
+    }
+
+    if (!hasPrimitive && arrayDim1 == arrayDim2) {
+        /*
+         * Two arrays of reference types with equal dimensions.  Try to
+         * find a good match.
+         */
+        commonElem = findCommonSuperclass(c1->elementClass, c2->elementClass);
+        numDims = arrayDim1;
+    } else {
+        /*
+         * Mismatched array depths and/or array(s) of primitives.  We want
+         * Object, or an Object array with appropriate dimensions.
+         *
+         * We initialize arrayClass to Object here, because it's possible
+         * for us to set numDims=0.
+         */
+        if (arrayDim1 < arrayDim2)
+            numDims = arrayDim1;
+        else
+            numDims = arrayDim2;
+        arrayClass = commonElem = c1->super;     // == java.lang.Object
+    }
+
+    /*
+     * Find an appropriately-dimensioned array class.  This is easiest
+     * to do iteratively, using the array class found by the current round
+     * as the element type for the next round.
+     */
+    for (i = 0; i < numDims; i++) {
+        arrayClass = dvmFindArrayClassForElement(commonElem);
+        commonElem = arrayClass;
+    }
+    assert(arrayClass != NULL);
+
+    LOGVV("ArrayMerge '%s' + '%s' --> '%s'",
+        c1->descriptor, c2->descriptor, arrayClass->descriptor);
+    return arrayClass;
+}
+
+/*
+ * Find the first common superclass of the two classes.  We're not
+ * interested in common interfaces.
+ *
+ * The easiest way to do this for concrete classes is to compute the "class
+ * depth" of each, move up toward the root of the deepest one until they're
+ * at the same depth, then walk both up to the root until they match.
+ *
+ * If both classes are arrays, we need to merge based on array depth and
+ * element type.
+ *
+ * If one class is an interface, we check to see if the other class/interface
+ * (or one of its predecessors) implements the interface.  If so, we return
+ * the interface; otherwise, we return Object.
+ *
+ * NOTE: we continue the tradition of "lazy interface handling".  To wit,
+ * suppose we have three classes:
+ *   One implements Fancy, Free
+ *   Two implements Fancy, Free
+ *   Three implements Free
+ * where Fancy and Free are unrelated interfaces.  The code requires us
+ * to merge One into Two.  Ideally we'd use a common interface, which
+ * gives us a choice between Fancy and Free, and no guidance on which to
+ * use.  If we use Free, we'll be okay when Three gets merged in, but if
+ * we choose Fancy, we're hosed.  The "ideal" solution is to create a
+ * set of common interfaces and carry that around, merging further references
+ * into it.  This is a pain.  The easy solution is to simply boil them
+ * down to Objects and let the runtime invokeinterface call fail, which
+ * is what we do.
+ */
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2)
+{
+    assert(!dvmIsPrimitiveClass(c1) && !dvmIsPrimitiveClass(c2));
+
+    if (c1 == c2)
+        return c1;
+
+    if (dvmIsInterfaceClass(c1) && dvmImplements(c2, c1)) {
+        if (gDebugVerbose)
+            LOGVV("COMMON/I1: %s + %s --> %s",
+                c1->descriptor, c2->descriptor, c1->descriptor);
+        return c1;
+    }
+    if (dvmIsInterfaceClass(c2) && dvmImplements(c1, c2)) {
+        if (gDebugVerbose)
+            LOGVV("COMMON/I2: %s + %s --> %s",
+                c1->descriptor, c2->descriptor, c2->descriptor);
+        return c2;
+    }
+
+    if (dvmIsArrayClass(c1) && dvmIsArrayClass(c2)) {
+        return findCommonArraySuperclass(c1, c2);
+    }
+
+    return digForSuperclass(c1, c2);
+}
+
+/*
+ * Merge two RegType values.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "type1".
+ */
+static RegType mergeTypes(RegType type1, RegType type2, bool* pChanged)
+{
+    RegType 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.
+     *
+     * 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) {
+            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 */
+            if (regTypeIsUninitReference(type1) ||
+                regTypeIsUninitReference(type2))
+            {
+                /* can't merge uninit with anything but self */
+                result = kRegTypeConflict;
+            } else {
+                ClassObject* clazz1 = regTypeInitializedReferenceToClass(type1);
+                ClassObject* clazz2 = regTypeInitializedReferenceToClass(type2);
+                ClassObject* mergedClass;
+
+                mergedClass = findCommonSuperclass(clazz1, clazz2);
+                assert(mergedClass != NULL);
+                result = regTypeFromClass(mergedClass);
+            }
+        }
+    }
+
+    if (result != type1)
+        *pChanged = true;
+    return result;
+}
+
+/*
+ * Merge the bits that indicate which monitor entry addresses on the stack
+ * are associated with this register.
+ *
+ * The merge is a simple bitwise AND.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "ents1".
+ */
+static MonitorEntries mergeMonitorEntries(MonitorEntries ents1,
+    MonitorEntries ents2, bool* pChanged)
+{
+    MonitorEntries result = ents1 & ents2;
+    if (result != ents1)
+        *pChanged = true;
+    return result;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workLine" into "regTable" at "nextInsn", and
+ * set the "changed" flag on the target address if any of the registers
+ * has changed.
+ *
+ * Returns "false" if we detect mis-matched monitor stacks.
+ */
+static bool updateRegisters(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, int nextInsn, const RegisterLine* workLine)
+{
+    const size_t insnRegCountPlus = regTable->insnRegCountPlus;
+    assert(workLine != NULL);
+    const RegType* workRegs = workLine->regTypes;
+
+    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", nextInsn);
+        copyLineToTable(regTable, nextInsn, workLine);
+        dvmInsnSetChanged(insnFlags, nextInsn, true);
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.copyRegCount++;
+#endif
+    } else {
+        if (gDebugVerbose) {
+            LOGVV("MERGE into 0x%04x", nextInsn);
+            //dumpRegTypes(vdata, targetRegs, 0, "targ", NULL, 0);
+            //dumpRegTypes(vdata, workRegs, 0, "work", NULL, 0);
+        }
+        /* merge registers, set Changed only if different */
+        RegisterLine* targetLine = getRegisterLine(regTable, nextInsn);
+        RegType* targetRegs = targetLine->regTypes;
+        MonitorEntries* workMonEnts = workLine->monitorEntries;
+        MonitorEntries* targetMonEnts = targetLine->monitorEntries;
+        bool changed = false;
+        unsigned int idx;
+
+        assert(targetRegs != NULL);
+
+        if (targetMonEnts != NULL) {
+            /*
+             * Monitor stacks must be identical.
+             */
+            if (targetLine->monitorStackTop != workLine->monitorStackTop) {
+                LOG_VFY_METH(meth,
+                    "VFY: mismatched stack depth %d vs. %d at 0x%04x",
+                    targetLine->monitorStackTop, workLine->monitorStackTop,
+                    nextInsn);
+                return false;
+            }
+            if (memcmp(targetLine->monitorStack, workLine->monitorStack,
+                    targetLine->monitorStackTop * sizeof(u4)) != 0)
+            {
+                LOG_VFY_METH(meth, "VFY: mismatched monitor stacks at 0x%04x",
+                    nextInsn);
+                return false;
+            }
+        }
+
+        for (idx = 0; idx < insnRegCountPlus; idx++) {
+            targetRegs[idx] =
+                    mergeTypes(targetRegs[idx], workRegs[idx], &changed);
+
+            if (targetMonEnts != NULL) {
+                targetMonEnts[idx] = mergeMonitorEntries(targetMonEnts[idx],
+                    workMonEnts[idx], &changed);
+            }
+        }
+
+        if (gDebugVerbose) {
+            //ALOGI(" RESULT (changed=%d)", changed);
+            //dumpRegTypes(vdata, targetRegs, 0, "rslt", NULL, 0);
+        }
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.mergeRegCount++;
+        if (changed)
+            gDvm.verifierStats.mergeRegChanged++;
+#endif
+
+        if (changed)
+            dvmInsnSetChanged(insnFlags, nextInsn, true);
+    }
+
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Look up an instance field, specified by "fieldIdx", that is going to be
+ * accessed in object "objType".  This resolves the field and then verifies
+ * that the class containing the field is an instance of the reference in
+ * "objType".
+ *
+ * It is possible for "objType" to be kRegTypeZero, meaning that we might
+ * have a null reference.  This is a runtime problem, so we allow it,
+ * skipping some of the type checks.
+ *
+ * In general, "objType" must be an initialized reference.  However, we
+ * allow it to be uninitialized if this is an "<init>" method and the field
+ * is declared within the "objType" class.
+ *
+ * Returns an InstField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static InstField* getInstField(const Method* meth,
+    const UninitInstanceMap* uninitMap, RegType objType, int fieldIdx,
+    VerifyError* pFailure)
+{
+    InstField* instField = NULL;
+    ClassObject* objClass;
+    bool mustBeLocal = false;
+
+    if (!regTypeIsReference(objType)) {
+        LOG_VFY("VFY: attempt to access field in non-reference type %d",
+            objType);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    instField = dvmOptResolveInstField(meth->clazz, fieldIdx, pFailure);
+    if (instField == NULL) {
+        LOG_VFY("VFY: unable to resolve instance field %u", fieldIdx);
+        assert(!VERIFY_OK(*pFailure));
+        goto bail;
+    }
+
+    if (objType == kRegTypeZero)
+        goto bail;
+
+    /*
+     * Access to fields in uninitialized objects is allowed if this is
+     * the <init> method for the object and the field in question is
+     * declared by this class.
+     */
+    objClass = regTypeReferenceToClass(objType, uninitMap);
+    assert(objClass != NULL);
+    if (regTypeIsUninitReference(objType)) {
+        if (!isInitMethod(meth) || meth->clazz != objClass) {
+            LOG_VFY("VFY: attempt to access field via uninitialized ref");
+            *pFailure = VERIFY_ERROR_GENERIC;
+            goto bail;
+        }
+        mustBeLocal = true;
+    }
+
+    if (!dvmInstanceof(objClass, instField->clazz)) {
+        LOG_VFY("VFY: invalid field access (field %s.%s, through %s ref)",
+                instField->clazz->descriptor, instField->name,
+                objClass->descriptor);
+        *pFailure = VERIFY_ERROR_NO_FIELD;
+        goto bail;
+    }
+
+    if (mustBeLocal) {
+        /* for uninit ref, make sure it's defined by this class, not super */
+        if (instField < objClass->ifields ||
+            instField >= objClass->ifields + objClass->ifieldCount)
+        {
+            LOG_VFY("VFY: invalid constructor field access (field %s in %s)",
+                    instField->name, objClass->descriptor);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            goto bail;
+        }
+    }
+
+bail:
+    return instField;
+}
+
+/*
+ * Look up a static field.
+ *
+ * Returns a StaticField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static StaticField* getStaticField(const Method* meth, int fieldIdx,
+    VerifyError* pFailure)
+{
+    StaticField* staticField;
+
+    staticField = dvmOptResolveStaticField(meth->clazz, fieldIdx, pFailure);
+    if (staticField == NULL) {
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+        const DexFieldId* pFieldId;
+
+        pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+
+        LOG_VFY("VFY: unable to resolve static field %u (%s) in %s", fieldIdx,
+            dexStringById(pDexFile, pFieldId->nameIdx),
+            dexStringByTypeIdx(pDexFile, pFieldId->classIdx));
+        assert(!VERIFY_OK(*pFailure));
+        goto bail;
+    }
+
+bail:
+    return staticField;
+}
+
+/*
+ * If "field" is marked "final", make sure this is the either <clinit>
+ * or <init> as appropriate.
+ *
+ * Sets "*pFailure" on failure.
+ */
+static void checkFinalFieldAccess(const Method* meth, const Field* field,
+    VerifyError* pFailure)
+{
+    if (!dvmIsFinalField(field))
+        return;
+
+    /* make sure we're in the same class */
+    if (meth->clazz != field->clazz) {
+        LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s",
+            field->clazz->descriptor, field->name);
+        *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return;
+    }
+
+    /*
+     * The VM spec descriptions of putfield and putstatic say that
+     * IllegalAccessError is only thrown when the instructions appear
+     * outside the declaring class.  Our earlier attempts to restrict
+     * final field modification to constructors are, therefore, wrong.
+     */
+#if 0
+    /* make sure we're in the right kind of constructor */
+    if (dvmIsStaticField(field)) {
+        if (!isClassInitMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: can't modify final static field outside <clinit>");
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    } else {
+        if (!isInitMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: can't modify final field outside <init>");
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    }
+#endif
+}
+
+/*
+ * Make sure that the register type is suitable for use as an array index.
+ *
+ * Sets "*pFailure" if not.
+ */
+static void checkArrayIndexType(const Method* meth, RegType regType,
+    VerifyError* pFailure)
+{
+    if (VERIFY_OK(*pFailure)) {
+        /*
+         * The 1nr types are interchangeable at this level. However,
+         * check that a float is not used as the index.
+         */
+        checkTypeCategory(regType, kTypeCategory1nr, pFailure);
+        if (regType == kRegTypeFloat) {
+          *pFailure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY_METH(meth, "Invalid reg type for array index (%d)",
+                regType);
+        }
+    }
+}
+
+/*
+ * Check constraints on constructor return.  Specifically, make sure that
+ * the "this" argument got initialized.
+ *
+ * The "this" argument to <init> uses code offset kUninitThisArgAddr, which
+ * puts it at the start of the list in slot 0.  If we see a register with
+ * an uninitialized slot 0 reference, we know it somehow didn't get
+ * initialized.
+ *
+ * Returns "true" if all is well.
+ */
+static bool checkConstructorReturn(const Method* meth,
+    const RegisterLine* registerLine, const int insnRegCount)
+{
+    const RegType* insnRegs = registerLine->regTypes;
+    int i;
+
+    if (!isInitMethod(meth))
+        return true;
+
+    RegType uninitThis = regTypeFromUninitIndex(kUninitThisArgSlot);
+
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitThis) {
+            LOG_VFY("VFY: <init> returning without calling superclass init");
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Verify that the target instruction is not "move-exception".  It's important
+ * that the only way to execute a move-exception is as the first instruction
+ * of an exception handler.
+ *
+ * Returns "true" if all is well, "false" if the target instruction is
+ * move-exception.
+ */
+static bool checkMoveException(const Method* meth, int insnIdx,
+    const char* logNote)
+{
+    assert(insnIdx >= 0 && insnIdx < (int)dvmGetMethodInsnsSize(meth));
+
+    if ((meth->insns[insnIdx] & 0xff) == OP_MOVE_EXCEPTION) {
+        LOG_VFY("VFY: invalid use of move-exception");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * For the "move-exception" instruction at "insnIdx", which must be at an
+ * exception handler address, determine the first common superclass of
+ * all exceptions that can land here.  (For javac output, we're probably
+ * looking at multiple spans of bytecode covered by one "try" that lands
+ * at an exception-specific "catch", but in general the handler could be
+ * shared for multiple exceptions.)
+ *
+ * Returns NULL if no matching exception handler can be found, or if the
+ * exception is not a subclass of Throwable.
+ */
+static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx,
+    VerifyError* pFailure)
+{
+    VerifyError localFailure;
+    const DexCode* pCode;
+    DexFile* pDexFile;
+    ClassObject* commonSuper = NULL;
+    u4 handlersSize;
+    u4 offset;
+    u4 i;
+
+    pDexFile = meth->clazz->pDvmDex->pDexFile;
+    pCode = dvmGetMethodCode(meth);
+
+    if (pCode->triesSize != 0) {
+        handlersSize = dexGetHandlersSize(pCode);
+        offset = dexGetFirstHandlerOffset(pCode);
+    } else {
+        handlersSize = 0;
+        offset = 0;
+    }
+
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            const DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            if (handler->address == (u4) insnIdx) {
+                ClassObject* clazz;
+
+                if (handler->typeIdx == kDexNoIndex)
+                    clazz = gDvm.exThrowable;
+                else
+                    clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx,
+                                &localFailure);
+
+                if (clazz == NULL) {
+                    LOG_VFY("VFY: unable to resolve exception class %u (%s)",
+                        handler->typeIdx,
+                        dexStringByTypeIdx(pDexFile, handler->typeIdx));
+                    /* TODO: do we want to keep going?  If we don't fail
+                     * this we run the risk of having a non-Throwable
+                     * introduced at runtime.  However, that won't pass
+                     * an instanceof test, so is essentially harmless. */
+                } else {
+                    if (commonSuper == NULL)
+                        commonSuper = clazz;
+                    else
+                        commonSuper = findCommonSuperclass(clazz, commonSuper);
+                }
+            }
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    if (commonSuper == NULL) {
+        /* no catch blocks, or no catches with classes we can find */
+        LOG_VFY_METH(meth,
+            "VFY: unable to find exception handler at addr %#x", insnIdx);
+        *pFailure = VERIFY_ERROR_GENERIC;
+    } else {
+        // TODO: verify the class is an instance of Throwable?
+    }
+
+    return commonSuper;
+}
+
+/*
+ * Helper for initRegisterTable.
+ *
+ * Returns an updated copy of "storage".
+ */
+static u1* assignLineStorage(u1* storage, RegisterLine* line,
+    bool trackMonitors, size_t regTypeSize, size_t monEntSize, size_t stackSize)
+{
+    line->regTypes = (RegType*) storage;
+    storage += regTypeSize;
+
+    if (trackMonitors) {
+        line->monitorEntries = (MonitorEntries*) storage;
+        storage += monEntSize;
+        line->monitorStack = (u4*) storage;
+        storage += stackSize;
+
+        assert(line->monitorStackTop == 0);
+    }
+
+    return storage;
+}
+
+/*
+ * Initialize the RegisterTable.
+ *
+ * Every instruction address can have a different set of information about
+ * what's in which register, but for verification purposes we only need to
+ * store it at branch target addresses (because we merge into that).
+ *
+ * By zeroing out the regType storage we are effectively initializing the
+ * register information to kRegTypeUnknown.
+ *
+ * We jump through some hoops here to minimize the total number of
+ * allocations we have to perform per method verified.
+ */
+static bool initRegisterTable(const VerifierData* vdata,
+    RegisterTable* regTable, RegisterTrackingMode trackRegsFor)
+{
+    const Method* meth = vdata->method;
+    const int insnsSize = vdata->insnsSize;
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const int kExtraLines = 2;  /* workLine, savedLine */
+    int i;
+
+    /*
+     * Every address gets a RegisterLine struct.  This is wasteful, but
+     * not so much that it's worth chasing through an extra level of
+     * indirection.
+     */
+    regTable->insnRegCountPlus = meth->registersSize + kExtraRegs;
+    regTable->registerLines =
+        (RegisterLine*) calloc(insnsSize, sizeof(RegisterLine));
+    if (regTable->registerLines == NULL)
+        return false;
+
+    assert(insnsSize > 0);
+
+    /*
+     * Count up the number of "interesting" instructions.
+     *
+     * "All" means "every address that holds the start of an instruction".
+     * "Branches" and "GcPoints" mean just those addresses.
+     *
+     * "GcPoints" fills about half the addresses, "Branches" about 15%.
+     */
+    int interestingCount = kExtraLines;
+
+    for (i = 0; i < insnsSize; i++) {
+        bool interesting;
+
+        switch (trackRegsFor) {
+        case kTrackRegsAll:
+            interesting = dvmInsnIsOpcode(insnFlags, i);
+            break;
+        case kTrackRegsGcPoints:
+            interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+                          dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        case kTrackRegsBranches:
+            interesting = dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        default:
+            dvmAbort();
+            return false;
+        }
+
+        if (interesting)
+            interestingCount++;
+
+        /* count instructions, for display only */
+        //if (dvmInsnIsOpcode(insnFlags, i))
+        //    insnCount++;
+    }
+
+    /*
+     * Allocate storage for the register type arrays.
+     * TODO: set trackMonitors based on global config option
+     */
+    size_t regTypeSize = regTable->insnRegCountPlus * sizeof(RegType);
+    size_t monEntSize = regTable->insnRegCountPlus * sizeof(MonitorEntries);
+    size_t stackSize = kMaxMonitorStackDepth * sizeof(u4);
+    bool trackMonitors;
+
+    if (gDvm.monitorVerification) {
+        trackMonitors = (vdata->monitorEnterCount != 0);
+    } else {
+        trackMonitors = false;
+    }
+
+    size_t spacePerEntry = regTypeSize +
+        (trackMonitors ? monEntSize + stackSize : 0);
+    regTable->lineAlloc = calloc(interestingCount, spacePerEntry);
+    if (regTable->lineAlloc == NULL)
+        return false;
+
+#ifdef VERIFIER_STATS
+    size_t totalSpace = interestingCount * spacePerEntry +
+        insnsSize * sizeof(RegisterLine);
+    if (gDvm.verifierStats.biggestAlloc < totalSpace)
+        gDvm.verifierStats.biggestAlloc = totalSpace;
+#endif
+
+    /*
+     * Populate the sparse register line table.
+     *
+     * There is a RegisterLine associated with every address, but not
+     * every RegisterLine has non-NULL pointers to storage for its fields.
+     */
+    u1* storage = (u1*)regTable->lineAlloc;
+    for (i = 0; i < insnsSize; i++) {
+        bool interesting;
+
+        switch (trackRegsFor) {
+        case kTrackRegsAll:
+            interesting = dvmInsnIsOpcode(insnFlags, i);
+            break;
+        case kTrackRegsGcPoints:
+            interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+                          dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        case kTrackRegsBranches:
+            interesting = dvmInsnIsBranchTarget(insnFlags, i);
+            break;
+        default:
+            dvmAbort();
+            return false;
+        }
+
+        if (interesting) {
+            storage = assignLineStorage(storage, &regTable->registerLines[i],
+                trackMonitors, regTypeSize, monEntSize, stackSize);
+        }
+    }
+
+    /*
+     * Grab storage for our "temporary" register lines.
+     */
+    storage = assignLineStorage(storage, &regTable->workLine,
+        trackMonitors, regTypeSize, monEntSize, stackSize);
+    storage = assignLineStorage(storage, &regTable->savedLine,
+        trackMonitors, regTypeSize, monEntSize, stackSize);
+
+    //ALOGD("Tracking registers for [%d], total %d in %d units",
+    //    trackRegsFor, interestingCount-kExtraLines, insnsSize);
+
+    assert(storage - (u1*)regTable->lineAlloc ==
+        (int) (interestingCount * spacePerEntry));
+    assert(regTable->registerLines[0].regTypes != NULL);
+    return true;
+}
+
+/*
+ * Free up any "hairy" structures associated with register lines.
+ */
+static void freeRegisterLineInnards(VerifierData* vdata)
+{
+    unsigned int idx;
+
+    if (vdata->registerLines == NULL)
+        return;
+
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+        if (liveRegs != NULL)
+            dvmFreeBitVector(liveRegs);
+    }
+}
+
+
+/*
+ * Verify that the arguments in a filled-new-array instruction are valid.
+ *
+ * "resClass" is the class refered to by pDecInsn->vB.
+ */
+static void verifyFilledNewArrayRegs(const Method* meth,
+    RegisterLine* registerLine, const DecodedInstruction* pDecInsn,
+    ClassObject* resClass, bool isRange, VerifyError* pFailure)
+{
+    u4 argCount = pDecInsn->vA;
+    RegType expectedType;
+    PrimitiveType elemType;
+    unsigned int ui;
+
+    assert(dvmIsArrayClass(resClass));
+    elemType = resClass->elementClass->primitiveType;
+    if (elemType == PRIM_NOT) {
+        expectedType = regTypeFromClass(resClass->elementClass);
+    } else {
+        expectedType = primitiveTypeToRegType(elemType);
+    }
+    //ALOGI("filled-new-array: %s -> %d", resClass->descriptor, expectedType);
+
+    /*
+     * Verify each register.  If "argCount" is bad, verifyRegisterType()
+     * will run off the end of the list and fail.  It's legal, if silly,
+     * for argCount to be zero.
+     */
+    for (ui = 0; ui < argCount; ui++) {
+        u4 getReg;
+
+        if (isRange)
+            getReg = pDecInsn->vC + ui;
+        else
+            getReg = pDecInsn->arg[ui];
+
+        verifyRegisterType(registerLine, getReg, expectedType, pFailure);
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY("VFY: filled-new-array arg %u(%u) not valid", ui, getReg);
+            return;
+        }
+    }
+}
+
+
+/*
+ * Replace an instruction with "throw-verification-error".  This allows us to
+ * defer error reporting until the code path is first used.
+ *
+ * This is expected to be called during "just in time" verification, not
+ * from within dexopt.  (Verification failures in dexopt will result in
+ * postponement of verification to first use of the class.)
+ *
+ * The throw-verification-error instruction requires two code units.  Some
+ * of the replaced instructions require three; the third code unit will
+ * receive a "nop".  The instruction's length will be left unchanged
+ * in "insnFlags".
+ *
+ * The VM postpones setting of debugger breakpoints in unverified classes,
+ * so there should be no clashes with the debugger.
+ *
+ * Returns "true" on success.
+ */
+static bool replaceFailingInstruction(const Method* meth, InsnFlags* insnFlags,
+    int insnIdx, VerifyError failure)
+{
+    VerifyErrorRefType refType;
+    u2* oldInsns = (u2*) meth->insns + insnIdx;
+    int width;
+
+    if (gDvm.optimizing)
+        ALOGD("Weird: RFI during dexopt?");
+
+    /*
+     * Generate the new instruction out of the old.
+     *
+     * First, make sure this is an instruction we're expecting to stomp on.
+     */
+    Opcode opcode = dexOpcodeFromCodeUnit(*oldInsns);
+    switch (opcode) {
+    case OP_CONST_CLASS:                // insn[1] == class ref, 2 bytes
+    case OP_CHECK_CAST:
+    case OP_INSTANCE_OF:
+    case OP_NEW_INSTANCE:
+    case OP_NEW_ARRAY:
+    case OP_FILLED_NEW_ARRAY:           // insn[1] == class ref, 3 bytes
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        refType = VERIFY_ERROR_REF_CLASS;
+        break;
+
+    case OP_IGET:                       // insn[1] == field ref, 2 bytes
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    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:
+    case OP_SGET:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    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:
+        refType = VERIFY_ERROR_REF_FIELD;
+        break;
+
+    case OP_INVOKE_VIRTUAL:             // insn[1] == method ref, 3 bytes
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        refType = VERIFY_ERROR_REF_METHOD;
+        break;
+
+    default:
+        /* could handle this in a generic way, but this is probably safer */
+        LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x", opcode);
+        return false;
+    }
+
+    assert((dexGetFlagsFromOpcode(opcode) & kInstrCanThrow) != 0);
+
+    /* write a NOP over the third code unit, if necessary */
+    width = dvmInsnGetWidth(insnFlags, insnIdx);
+    switch (width) {
+    case 2:
+    case 4:
+        /* nothing to do */
+        break;
+    case 3:
+        dvmUpdateCodeUnit(meth, oldInsns+2, OP_NOP);
+        break;
+    default:
+        /* whoops */
+        ALOGE("ERROR: stomped a %d-unit instruction with a verifier error",
+            width);
+        dvmAbort();
+    }
+
+    /* encode the opcode, with the failure code in the high byte */
+    assert(width == 2 || width == 3);
+    u2 newVal = OP_THROW_VERIFICATION_ERROR |
+        (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
+    dvmUpdateCodeUnit(meth, oldInsns, newVal);
+
+    return true;
+}
+
+/*
+ * Handle a monitor-enter instruction.
+ */
+void handleMonitorEnter(RegisterLine* workLine, u4 regIdx, u4 insnIdx,
+    VerifyError* pFailure)
+{
+    if (!regTypeIsReference(getRegisterType(workLine, regIdx))) {
+        LOG_VFY("VFY: monitor-enter on non-object");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    if (workLine->monitorEntries == NULL) {
+        /* should only be true if monitor verification is disabled */
+        assert(!gDvm.monitorVerification);
+        return;
+    }
+
+    if (workLine->monitorStackTop == kMaxMonitorStackDepth) {
+        LOG_VFY("VFY: monitor-enter stack overflow (%d)",
+            kMaxMonitorStackDepth);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    /*
+     * Push an entry on the stack, and set a bit in the register flags to
+     * indicate that it's associated with this register.
+     */
+    workLine->monitorEntries[regIdx] |= 1 << workLine->monitorStackTop;
+    workLine->monitorStack[workLine->monitorStackTop++] = insnIdx;
+}
+
+/*
+ * Handle a monitor-exit instruction.
+ */
+void handleMonitorExit(RegisterLine* workLine, u4 regIdx, u4 insnIdx,
+    VerifyError* pFailure)
+{
+    if (!regTypeIsReference(getRegisterType(workLine, regIdx))) {
+        LOG_VFY("VFY: monitor-exit on non-object");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    if (workLine->monitorEntries == NULL) {
+        /* should only be true if monitor verification is disabled */
+        assert(!gDvm.monitorVerification);
+        return;
+    }
+
+    if (workLine->monitorStackTop == 0) {
+        LOG_VFY("VFY: monitor-exit stack underflow");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    /*
+     * Confirm that the entry at the top of the stack is associated with
+     * the register.  Pop the top entry off.
+     */
+    workLine->monitorStackTop--;
+#ifdef BUG_3215458_FIXED
+    /*
+     * TODO: This code can safely be enabled if know we are working on
+     * a dex file of format version 036 or later. (That is, we'll need to
+     * add a check for the version number.)
+     */
+    if ((workLine->monitorEntries[regIdx] & (1 << workLine->monitorStackTop))
+            == 0)
+    {
+        LOG_VFY("VFY: monitor-exit bit %d not set: addr=0x%04x (bits[%d]=%#x)",
+            workLine->monitorStackTop, insnIdx, regIdx,
+            workLine->monitorEntries[regIdx]);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+#endif
+    workLine->monitorStack[workLine->monitorStackTop] = 0;
+
+    /*
+     * Clear the bit from the register flags.
+     */
+    workLine->monitorEntries[regIdx] &= ~(1 << workLine->monitorStackTop);
+}
+
+
+/*
+ * ===========================================================================
+ *      Entry point and driver loop
+ * ===========================================================================
+ */
+
+/*
+ * One-time preparation.
+ */
+static void verifyPrep()
+{
+#ifndef NDEBUG
+    /* only need to do this if the table was updated */
+    checkMergeTab();
+#endif
+}
+
+/*
+ * Entry point for the detailed code-flow analysis of a single method.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata)
+{
+    bool result = false;
+    const Method* meth = vdata->method;
+    const int insnsSize = vdata->insnsSize;
+    const bool generateRegisterMap = gDvm.generateRegisterMaps;
+    RegisterTable regTable;
+
+    memset(&regTable, 0, sizeof(regTable));
+
+#ifdef VERIFIER_STATS
+    gDvm.verifierStats.methodsExamined++;
+    if (vdata->monitorEnterCount)
+        gDvm.verifierStats.monEnterMethods++;
+#endif
+
+    /* TODO: move this elsewhere -- we don't need to do this for every method */
+    verifyPrep();
+
+    if (meth->registersSize * insnsSize > 4*1024*1024) {
+        LOG_VFY_METH(meth,
+            "VFY: warning: method is huge (regs=%d insnsSize=%d)",
+            meth->registersSize, insnsSize);
+        /* might be bogus data, might be some huge generated method */
+    }
+
+    /*
+     * Create register lists, and initialize them to "Unknown".  If we're
+     * also going to create the register map, we need to retain the
+     * register lists for a larger set of addresses.
+     */
+    if (!initRegisterTable(vdata, &regTable,
+            generateRegisterMap ? kTrackRegsGcPoints : kTrackRegsBranches))
+        goto bail;
+
+    vdata->registerLines = regTable.registerLines;
+
+    /*
+     * Perform liveness analysis.
+     *
+     * We can do this before or after the main verifier pass.  The choice
+     * affects whether or not we see the effects of verifier instruction
+     * changes, i.e. substitution of throw-verification-error.
+     *
+     * In practice the ordering doesn't really matter, because T-V-E
+     * just prunes "can continue", creating regions of dead code (with
+     * corresponding register map data that will never be used).
+     */
+    if (generateRegisterMap &&
+        gDvm.registerMapMode == kRegisterMapModeLivePrecise)
+    {
+        /*
+         * Compute basic blocks and predecessor lists.
+         */
+        if (!dvmComputeVfyBasicBlocks(vdata))
+            goto bail;
+
+        /*
+         * Compute liveness.
+         */
+        if (!dvmComputeLiveness(vdata))
+            goto bail;
+    }
+
+    /*
+     * Initialize the types of the registers that correspond to the
+     * method arguments.  We can determine this from the method signature.
+     */
+    if (!setTypesFromSignature(meth, regTable.registerLines[0].regTypes,
+            vdata->uninitMap))
+        goto bail;
+
+    /*
+     * Run the verifier.
+     */
+    if (!doCodeVerification(vdata, &regTable))
+        goto bail;
+
+    /*
+     * Generate a register map.
+     */
+    if (generateRegisterMap) {
+        RegisterMap* pMap = dvmGenerateRegisterMapV(vdata);
+        if (pMap != NULL) {
+            /*
+             * Tuck it into the Method struct.  It will either get used
+             * directly or, if we're in dexopt, will be packed up and
+             * appended to the DEX file.
+             */
+            dvmSetRegisterMap((Method*)meth, pMap);
+        }
+    }
+
+    /*
+     * Success.
+     */
+    result = true;
+
+bail:
+    freeRegisterLineInnards(vdata);
+    free(regTable.registerLines);
+    free(regTable.lineAlloc);
+    return result;
+}
+
+/*
+ * Grind through the instructions.
+ *
+ * The basic strategy is as outlined in v3 4.11.1.2: set the "changed" bit
+ * on the first instruction, process it (setting additional "changed" bits),
+ * and repeat until there are no more.
+ *
+ * v3 4.11.1.1
+ * - (N/A) operand stack is always the same size
+ * - operand stack [registers] contain the correct types of values
+ * - local variables [registers] contain the correct types of values
+ * - methods are invoked with the appropriate arguments
+ * - fields are assigned using values of appropriate types
+ * - opcodes have the correct type values in operand registers
+ * - there is never an uninitialized class instance in a local variable in
+ *   code protected by an exception handler (operand stack is okay, because
+ *   the operand stack is discarded when an exception is thrown) [can't
+ *   know what's a local var w/o the debug info -- should fall out of
+ *   register typing]
+ *
+ * v3 4.11.1.2
+ * - execution cannot fall off the end of the code
+ *
+ * (We also do many of the items described in the "static checks" sections,
+ * because it's easier to do them here.)
+ *
+ * We need an array of RegType values, one per register, for every
+ * instruction.  If the method uses monitor-enter, we need extra data
+ * for every register, and a stack for every "interesting" instruction.
+ * In theory this could become quite large -- up to several megabytes for
+ * a monster function.
+ *
+ * NOTE:
+ * The spec forbids backward branches when there's an uninitialized reference
+ * in a register.  The idea is to prevent something like this:
+ *   loop:
+ *     move r1, r0
+ *     new-instance r0, MyClass
+ *     ...
+ *     if-eq rN, loop  // once
+ *   initialize r0
+ *
+ * This leaves us with two different instances, both allocated by the
+ * same instruction, but only one is initialized.  The scheme outlined in
+ * v3 4.11.1.4 wouldn't catch this, so they work around it by preventing
+ * backward branches.  We achieve identical results without restricting
+ * code reordering by specifying that you can't execute the new-instance
+ * instruction if a register contains an uninitialized instance created
+ * by that same instrutcion.
+ */
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable)
+{
+    const Method* meth = vdata->method;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    UninitInstanceMap* uninitMap = vdata->uninitMap;
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    bool result = false;
+    bool debugVerbose = false;
+    int insnIdx, startGuess;
+
+    /*
+     * Begin by marking the first instruction as "changed".
+     */
+    dvmInsnSetChanged(insnFlags, 0, true);
+
+    if (dvmWantVerboseVerification(meth)) {
+        IF_ALOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            ALOGI("Now verifying: %s.%s %s (ins=%d regs=%d)",
+                meth->clazz->descriptor, meth->name, desc,
+                meth->insSize, meth->registersSize);
+            ALOGI(" ------ [0    4    8    12   16   20   24   28   32   36");
+            free(desc);
+        }
+        debugVerbose = true;
+        gDebugVerbose = true;
+    } else {
+        gDebugVerbose = false;
+    }
+
+    startGuess = 0;
+
+    /*
+     * 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)) {
+            RegisterLine* workLine = &regTable->workLine;
+
+            copyLineFromTable(workLine, regTable, insnIdx);
+        } else {
+#ifndef NDEBUG
+            /*
+             * Sanity check: retrieve the stored register line (assuming
+             * a full table) and make sure it actually matches.
+             */
+            RegisterLine* registerLine = getRegisterLine(regTable, insnIdx);
+            if (registerLine->regTypes != NULL &&
+                compareLineToTable(regTable, insnIdx, &regTable->workLine) != 0)
+            {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOG_VFY("HUH? workLine diverged in %s.%s %s",
+                        meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+                dumpRegTypes(vdata, registerLine, 0, "work",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+                dumpRegTypes(vdata, registerLine, 0, "insn",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+            }
+#endif
+        }
+        if (debugVerbose) {
+            dumpRegTypes(vdata, &regTable->workLine, insnIdx,
+                NULL, uninitMap, SHOW_REG_DETAILS);
+        }
+
+        //ALOGI("process %s.%s %s %d",
+        //    meth->clazz->descriptor, meth->name, meth->descriptor, insnIdx);
+        if (!verifyInstruction(meth, insnFlags, regTable, insnIdx,
+                uninitMap, &startGuess))
+        {
+            //ALOGD("+++ %s bailing at %d", meth->name, insnIdx);
+            goto bail;
+        }
+
+        /*
+         * Clear "changed" and mark as visited.
+         */
+        dvmInsnSetVisited(insnFlags, insnIdx, true);
+        dvmInsnSetChanged(insnFlags, insnIdx, false);
+    }
+
+    if (DEAD_CODE_SCAN && !IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        /*
+         * Scan for dead code.  There's nothing "evil" about dead code
+         * (besides the wasted space), but it indicates a flaw somewhere
+         * down the line, possibly in the verifier.
+         *
+         * If we've substituted "always throw" instructions into the stream,
+         * we are almost certainly going to have some dead code.
+         */
+        int deadStart = -1;
+        for (insnIdx = 0; insnIdx < insnsSize;
+            insnIdx += dvmInsnGetWidth(insnFlags, insnIdx))
+        {
+            /*
+             * Switch-statement data doesn't get "visited" by scanner.  It
+             * may or may not be preceded by a padding NOP (for alignment).
+             */
+            int instr = meth->insns[insnIdx];
+            if (instr == kPackedSwitchSignature ||
+                instr == kSparseSwitchSignature ||
+                instr == kArrayDataSignature ||
+                (instr == OP_NOP && (insnIdx + 1 < insnsSize) &&
+                 (meth->insns[insnIdx+1] == kPackedSwitchSignature ||
+                  meth->insns[insnIdx+1] == kSparseSwitchSignature ||
+                  meth->insns[insnIdx+1] == kArrayDataSignature)))
+            {
+                dvmInsnSetVisited(insnFlags, insnIdx, true);
+            }
+
+            if (!dvmInsnIsVisited(insnFlags, insnIdx)) {
+                if (deadStart < 0)
+                    deadStart = insnIdx;
+            } else if (deadStart >= 0) {
+                IF_ALOGD() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&meth->prototype);
+                    ALOGD("VFY: dead code 0x%04x-%04x in %s.%s %s",
+                        deadStart, insnIdx-1,
+                        meth->clazz->descriptor, meth->name, desc);
+                    free(desc);
+                }
+
+                deadStart = -1;
+            }
+        }
+        if (deadStart >= 0) {
+            IF_ALOGD() {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                ALOGD("VFY: dead code 0x%04x-%04x in %s.%s %s",
+                    deadStart, insnIdx-1,
+                    meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+            }
+        }
+    }
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * Perform verification for a single instruction.
+ *
+ * This requires fully decoding the instruction to determine the effect
+ * it has on registers.
+ *
+ * Finds zero or more following instructions and sets the "changed" flag
+ * if execution at that point needs to be (re-)evaluated.  Register changes
+ * are merged into "regTypes" at the target addresses.  Does not set or
+ * clear any other flags in "insnFlags".
+ *
+ * This may alter meth->insns if we need to replace an instruction with
+ * throw-verification-error.
+ */
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, int insnIdx, UninitInstanceMap* uninitMap,
+    int* pStartGuess)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    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
+     * 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 can also return, in which case there is no successor instruction
+     * from this point.
+     *
+     * The behavior can be determined from the OpcodeFlags.
+     */
+
+    RegisterLine* workLine = &regTable->workLine;
+    const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+    ClassObject* resClass;
+    s4 branchTarget = 0;
+    const int insnRegCount = meth->registersSize;
+    RegType tmpType;
+    DecodedInstruction decInsn;
+    bool justSetResult = false;
+    VerifyError failure = VERIFY_ERROR_NONE;
+
+#ifndef NDEBUG
+    memset(&decInsn, 0x81, sizeof(decInsn));
+#endif
+    dexDecodeInstruction(insns, &decInsn);
+
+    int nextFlags = dexGetFlagsFromOpcode(decInsn.opcode);
+
+    /*
+     * Make a copy of the previous register state.  If the instruction
+     * can throw an exception, we will copy/merge this into the "catch"
+     * address rather than workLine, 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))
+    {
+        copyRegisterLine(&regTable->savedLine, workLine,
+            regTable->insnRegCountPlus);
+    } else {
+#ifndef NDEBUG
+        memset(regTable->savedLine.regTypes, 0xdd,
+            regTable->insnRegCountPlus * sizeof(RegType));
+#endif
+    }
+
+    switch (decInsn.opcode) {
+    case OP_NOP:
+        /*
+         * A "pure" NOP has no effect on anything.  Data tables start with
+         * a signature that looks like a NOP; if we see one of these in
+         * the course of executing code then we have a problem.
+         */
+        if (decInsn.vA != 0) {
+            LOG_VFY("VFY: encountered data table in instruction stream");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+
+    case OP_MOVE:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_16:
+        copyRegister1(workLine, decInsn.vA, decInsn.vB, kTypeCategory1nr,
+            &failure);
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        copyRegister2(workLine, decInsn.vA, decInsn.vB, &failure);
+        break;
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_OBJECT_16:
+        copyRegister1(workLine, decInsn.vA, decInsn.vB, kTypeCategoryRef,
+            &failure);
+        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:
+        copyResultRegister1(workLine, insnRegCount, decInsn.vA,
+            kTypeCategory1nr, &failure);
+        break;
+    case OP_MOVE_RESULT_WIDE:
+        copyResultRegister2(workLine, insnRegCount, decInsn.vA, &failure);
+        break;
+    case OP_MOVE_RESULT_OBJECT:
+        copyResultRegister1(workLine, insnRegCount, decInsn.vA,
+            kTypeCategoryRef, &failure);
+        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.
+         *
+         * "resClass" will hold the closest common superclass of all
+         * exceptions that can be handled here.
+         */
+        resClass = getCaughtExceptionType(meth, insnIdx, &failure);
+        if (resClass == NULL) {
+            assert(!VERIFY_OK(failure));
+        } else {
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+
+    case OP_RETURN_VOID:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else if (getMethodReturnType(meth) != kRegTypeUnknown) {
+            LOG_VFY("VFY: return-void not expected");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+    case OP_RETURN:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            /* check the method signature */
+            RegType returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategory1nr, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-1nr not expected");
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields. Also, it may use integer values for
+             * boolean, byte, short, and character return types.
+             */
+            RegType srcType = getRegisterType(workLine, decInsn.vA);
+            if ((returnType == kRegTypeBoolean && srcType == kRegTypeByte) ||
+                ((returnType == kRegTypeBoolean || returnType == kRegTypeByte ||
+                  returnType == kRegTypeShort || returnType == kRegTypeChar) &&
+                 srcType == kRegTypeInteger))
+                returnType = srcType;
+
+            /* check the register contents */
+            verifyRegisterType(workLine, decInsn.vA, returnType, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-1nr on invalid register v%d",
+                    decInsn.vA);
+            }
+        }
+        break;
+    case OP_RETURN_WIDE:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            RegType returnType;
+
+            /* check the method signature */
+            returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategory2, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-wide not expected");
+
+            /* check the register contents */
+            verifyRegisterType(workLine, decInsn.vA, returnType, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-wide on invalid register pair v%d",
+                    decInsn.vA);
+            }
+        }
+        break;
+    case OP_RETURN_OBJECT:
+        if (!checkConstructorReturn(meth, workLine, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            RegType returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategoryRef, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-object not expected");
+                break;
+            }
+
+            /* returnType is the *expected* return type, not register value */
+            assert(returnType != kRegTypeZero);
+            assert(!regTypeIsUninitReference(returnType));
+
+            /*
+             * Verify that the reference in vAA is an instance of the type
+             * in "returnType".  The Zero type is allowed here.  If the
+             * method is declared to return an interface, then any
+             * initialized reference is acceptable.
+             *
+             * Note getClassFromRegister fails if the register holds an
+             * uninitialized reference, so we do not allow them to be
+             * returned.
+             */
+            ClassObject* declClass;
+
+            declClass = regTypeInitializedReferenceToClass(returnType);
+            resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                if (!dvmIsInterfaceClass(declClass) &&
+                    !dvmInstanceof(resClass, declClass))
+                {
+                    LOG_VFY("VFY: returning %s (cl=%p), declared %s (cl=%p)",
+                            resClass->descriptor, resClass->classLoader,
+                            declClass->descriptor, declClass->classLoader);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workLine, decInsn.vA,
+            determineCat1Const((s4)decInsn.vB));
+        break;
+    case OP_CONST_HIGH16:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workLine, decInsn.vA,
+            determineCat1Const((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; resolved upon use */
+        setRegisterType(workLine, decInsn.vA, kRegTypeConstLo);
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+        assert(gDvm.classJavaLangString != NULL);
+        setRegisterType(workLine, decInsn.vA,
+            regTypeFromClass(gDvm.classJavaLangString));
+        break;
+    case OP_CONST_CLASS:
+        assert(gDvm.classJavaLangClass != NULL);
+        /* make sure we can resolve the class; access check is important */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            setRegisterType(workLine, decInsn.vA,
+                regTypeFromClass(gDvm.classJavaLangClass));
+        }
+        break;
+
+    case OP_MONITOR_ENTER:
+        handleMonitorEnter(workLine, decInsn.vA, insnIdx, &failure);
+        break;
+    case OP_MONITOR_EXIT:
+        /*
+         * monitor-exit instructions are odd.  They can throw exceptions,
+         * but when they do they act as if they succeeded and the PC is
+         * pointing to the following instruction.  (This behavior goes back
+         * to the need to handle asynchronous exceptions, a now-deprecated
+         * feature that Dalvik doesn't support.)
+         *
+         * In practice we don't need to worry about this.  The only
+         * exceptions that can be thrown from monitor-exit are for a
+         * null reference and -exit without a matching -enter.  If the
+         * structured locking checks are working, the former would have
+         * failed on the -enter instruction, and the latter is impossible.
+         *
+         * This is fortunate, because issue 3221411 prevents us from
+         * chasing the "can throw" path when monitor verification is
+         * enabled.  If we can fully verify the locking we can ignore
+         * some catch blocks (which will show up as "dead" code when
+         * we skip them here); if we can't, then the code path could be
+         * "live" so we still need to check it.
+         */
+        if (workLine->monitorEntries != NULL)
+            nextFlags &= ~kInstrCanThrow;
+        handleMonitorExit(workLine, decInsn.vA, insnIdx, &failure);
+        break;
+
+    case OP_CHECK_CAST:
+        /*
+         * If this instruction succeeds, we will promote register vA to
+         * the type in vB.  (This could be a demotion -- not expected, so
+         * we don't try to address it.)
+         *
+         * If it fails, an exception is thrown, which we deal with later
+         * by ignoring the update to decInsn.vA when branching to a handler.
+         */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve check-cast %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            RegType origType;
+
+            origType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(origType)) {
+                LOG_VFY("VFY: check-cast on non-reference in v%u",decInsn.vA);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+    case OP_INSTANCE_OF:
+        /* make sure we're checking a reference type */
+        tmpType = getRegisterType(workLine, decInsn.vB);
+        if (!regTypeIsReference(tmpType)) {
+            LOG_VFY("VFY: vB not a reference (%d)", tmpType);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+
+        /* make sure we can resolve the class; access check is important */
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            /* result is boolean */
+            setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        }
+        break;
+
+    case OP_ARRAY_LENGTH:
+        resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL && !dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: array-length on non-array");
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        setRegisterType(workLine, decInsn.vA, kRegTypeInteger);
+        break;
+
+    case OP_NEW_INSTANCE:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve new-instance %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            RegType uninitType;
+
+            /* can't create an instance of an interface or abstract class */
+            if (dvmIsAbstractClass(resClass) || dvmIsInterfaceClass(resClass)) {
+                LOG_VFY("VFY: new-instance on interface or abstract class %s",
+                    resClass->descriptor);
+                failure = VERIFY_ERROR_INSTANTIATION;
+                break;
+            }
+
+            /* add resolved class to uninit map if not already there */
+            int uidx = setUninitInstance(uninitMap, insnIdx, resClass);
+            assert(uidx >= 0);
+            uninitType = regTypeFromUninitIndex(uidx);
+
+            /*
+             * Any registers holding previous allocations from this address
+             * that have not yet been initialized must be marked invalid.
+             */
+            markUninitRefsAsInvalid(workLine, insnRegCount, uninitMap,
+                uninitType);
+
+            /* add the new uninitialized reference to the register ste */
+            setRegisterType(workLine, decInsn.vA, uninitType);
+        }
+        break;
+    case OP_NEW_ARRAY:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else if (!dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: new-array on non-array class");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            /* make sure "size" register is valid type */
+            verifyRegisterType(workLine, decInsn.vB, kRegTypeInteger, &failure);
+            /* set register type to array class */
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(resClass));
+        }
+        break;
+    case OP_FILLED_NEW_ARRAY:
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+        if (resClass == NULL) {
+            const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+            dvmLogUnableToResolveClass(badClassDesc, meth);
+            LOG_VFY("VFY: unable to resolve filled-array %d (%s) in %s",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else if (!dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: filled-new-array on non-array class");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            bool isRange = (decInsn.opcode == OP_FILLED_NEW_ARRAY_RANGE);
+
+            /* check the arguments to the instruction */
+            verifyFilledNewArrayRegs(meth, workLine, &decInsn,
+                resClass, isRange, &failure);
+            /* filled-array result goes into "result" register */
+            setResultRegisterType(workLine, insnRegCount,
+                regTypeFromClass(resClass));
+            justSetResult = true;
+        }
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeFloat, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeFloat, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeDoubleLo, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeDoubleLo, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMP_LONG:
+        verifyRegisterType(workLine, decInsn.vB, kRegTypeLongLo, &failure);
+        verifyRegisterType(workLine, decInsn.vC, kRegTypeLongLo, &failure);
+        setRegisterType(workLine, decInsn.vA, kRegTypeBoolean);
+        break;
+
+    case OP_THROW:
+        resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+        if (VERIFY_OK(failure) && resClass != NULL) {
+            if (!dvmInstanceof(resClass, gDvm.exThrowable)) {
+                LOG_VFY("VFY: thrown class %s not instanceof Throwable",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        break;
+
+    case OP_GOTO:
+    case OP_GOTO_16:
+    case OP_GOTO_32:
+        /* no effect on or use of registers */
+        break;
+
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+        /* verify that vAA is an integer, or can be converted to one */
+        verifyRegisterType(workLine, decInsn.vA, kRegTypeInteger, &failure);
+        break;
+
+    case OP_FILL_ARRAY_DATA:
+        {
+            RegType valueType;
+            const u2 *arrayData;
+            u2 elemWidth;
+
+            /* Similar to the verification done for APUT */
+            resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* resClass can be null if the reg type is Zero */
+            if (resClass == NULL)
+                break;
+
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT ||
+                resClass->elementClass->primitiveType == PRIM_VOID)
+            {
+                LOG_VFY("VFY: invalid fill-array-data on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = primitiveTypeToRegType(
+                                    resClass->elementClass->primitiveType);
+            assert(valueType != kRegTypeUnknown);
+
+            /*
+             * Now verify if the element width in the table matches the element
+             * width declared in the array
+             */
+            arrayData = insns + (insns[1] | (((s4)insns[2]) << 16));
+            if (arrayData[0] != kArrayDataSignature) {
+                LOG_VFY("VFY: invalid magic for array-data");
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            switch (resClass->elementClass->primitiveType) {
+                case PRIM_BOOLEAN:
+                case PRIM_BYTE:
+                     elemWidth = 1;
+                     break;
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                     elemWidth = 2;
+                     break;
+                case PRIM_FLOAT:
+                case PRIM_INT:
+                     elemWidth = 4;
+                     break;
+                case PRIM_DOUBLE:
+                case PRIM_LONG:
+                     elemWidth = 8;
+                     break;
+                default:
+                     elemWidth = 0;
+                     break;
+            }
+
+            /*
+             * Since we don't compress the data in Dex, expect to see equal
+             * width of data stored in the table and expected from the array
+             * class.
+             */
+            if (arrayData[1] != elemWidth) {
+                LOG_VFY("VFY: array-data size mismatch (%d vs %d)",
+                        arrayData[1], elemWidth);
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        break;
+
+    case OP_IF_EQ:
+    case OP_IF_NE:
+        {
+            RegType type1, type2;
+
+            type1 = getRegisterType(workLine, decInsn.vA);
+            type2 = getRegisterType(workLine, decInsn.vB);
+
+            /* both references? */
+            if (regTypeIsReference(type1) && regTypeIsReference(type2))
+                break;
+
+            /* both category-1nr? */
+            checkTypeCategory(type1, kTypeCategory1nr, &failure);
+            checkTypeCategory(type2, kTypeCategory1nr, &failure);
+            if (type1 == kRegTypeFloat || type2 == kRegTypeFloat) {
+              failure = VERIFY_ERROR_GENERIC;
+            }
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: args to if-eq/if-ne must both be refs or cat1");
+                break;
+            }
+        }
+        break;
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr and not float");
+            break;
+        }
+        tmpType = getRegisterType(workLine, decInsn.vB);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr and not float");
+            break;
+        }
+        break;
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        if (regTypeIsReference(tmpType))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected non-float cat-1 arg to if");
+        break;
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (tmpType == kRegTypeFloat) {
+          failure = VERIFY_ERROR_GENERIC;
+        }
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected non-float cat-1 arg to if");
+        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:
+        {
+            RegType srcType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                /* verify the class */
+                if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                    resClass->elementClass->primitiveType == PRIM_NOT)
+                {
+                    LOG_VFY("VFY: invalid aget-1nr target %s",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* make sure array type matches instruction */
+                srcType = primitiveTypeToRegType(
+                                        resClass->elementClass->primitiveType);
+
+                /* correct if float */
+                if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                    tmpType = kRegTypeFloat;
+
+                if (!checkFieldArrayStore1nr(tmpType, srcType)) {
+                    LOG_VFY("VFY: invalid aget-1nr, array type=%d with"
+                            " inst type=%d (on %s)",
+                        srcType, tmpType, resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            } else {
+                /*
+                 * Null array ref; this code path will fail at runtime. Label
+                 * result as zero to allow it to remain mergeable.
+                 */
+                tmpType = kRegTypeZero;
+            }
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+
+    case OP_AGET_WIDE:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                /* verify the class */
+                if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                    resClass->elementClass->primitiveType == PRIM_NOT)
+                {
+                    LOG_VFY("VFY: invalid aget-wide target %s",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* try to refine "dstType" */
+                switch (resClass->elementClass->primitiveType) {
+                case PRIM_LONG:
+                    dstType = kRegTypeLongLo;
+                    break;
+                case PRIM_DOUBLE:
+                    dstType = kRegTypeDoubleLo;
+                    break;
+                default:
+                    LOG_VFY("VFY: invalid aget-wide on %s",
+                        resClass->descriptor);
+                    dstType = kRegTypeUnknown;
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            } else {
+                /*
+                 * Null array ref; this code path will fail at runtime.  We
+                 * know this is either long or double, so label it const.
+                 */
+                dstType = kRegTypeConstLo;
+            }
+            setRegisterType(workLine, decInsn.vA, dstType);
+        }
+        break;
+
+    case OP_AGET_OBJECT:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get the class of the array we're pulling an object from */
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (resClass != NULL) {
+                ClassObject* elementClass;
+
+                assert(resClass != NULL);
+                if (!dvmIsArrayClass(resClass)) {
+                    LOG_VFY("VFY: aget-object on non-array class");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                assert(resClass->elementClass != NULL);
+
+                /*
+                 * Find the element class.  resClass->elementClass indicates
+                 * the basic type, which won't be what we want for a
+                 * multi-dimensional array.
+                 */
+                if (resClass->descriptor[1] == '[') {
+                    assert(resClass->arrayDim > 1);
+                    elementClass = dvmFindArrayClass(&resClass->descriptor[1],
+                                        resClass->classLoader);
+                } else if (resClass->descriptor[1] == 'L') {
+                    assert(resClass->arrayDim == 1);
+                    elementClass = resClass->elementClass;
+                } else {
+                    LOG_VFY("VFY: aget-object on non-ref array class (%s)",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                dstType = regTypeFromClass(elementClass);
+            } else {
+                /*
+                 * The array reference is NULL, so the current code path will
+                 * throw an exception.  For proper merging with later code
+                 * paths, and correct handling of "if-eqz" tests on the
+                 * result of the array get, we want to treat this as a null
+                 * reference.
+                 */
+                dstType = kRegTypeZero;
+            }
+            setRegisterType(workLine, decInsn.vA, dstType);
+        }
+        break;
+    case OP_APUT:
+        tmpType = kRegTypeInteger;
+        goto aput_1nr_common;
+    case OP_APUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto aput_1nr_common;
+    case OP_APUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto aput_1nr_common;
+    case OP_APUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto aput_1nr_common;
+    case OP_APUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto aput_1nr_common;
+aput_1nr_common:
+        {
+            RegType srcType, dstType, indexType;
+
+            indexType = getRegisterType(workLine, decInsn.vC);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on aput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* resClass can be null if the reg type is Zero */
+            if (resClass == NULL)
+                break;
+
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT)
+            {
+                LOG_VFY("VFY: invalid aput-1nr on %s", resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            /* verify that instruction matches array */
+            dstType = primitiveTypeToRegType(
+                                    resClass->elementClass->primitiveType);
+
+            /* correct if float */
+            if (dstType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            verifyRegisterType(workLine, decInsn.vA, dstType, &failure);
+
+            if (dstType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, dstType)) {
+                LOG_VFY("VFY: invalid aput-1nr on %s (inst=%d dst=%d)",
+                        resClass->descriptor, tmpType, dstType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vC);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        resClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL) {
+            /* verify the class and try to refine "dstType" */
+            if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+                resClass->elementClass->primitiveType == PRIM_NOT)
+            {
+                LOG_VFY("VFY: invalid aput-wide on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            switch (resClass->elementClass->primitiveType) {
+            case PRIM_LONG:
+                verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+                break;
+            case PRIM_DOUBLE:
+                verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+                break;
+            default:
+                LOG_VFY("VFY: invalid aput-wide on %s",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_OBJECT:
+        tmpType = getRegisterType(workLine, decInsn.vC);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* get the ref we're storing; Zero is okay, Uninit is not */
+        resClass = getClassFromRegister(workLine, decInsn.vA, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL) {
+            ClassObject* arrayClass;
+            ClassObject* elementClass;
+
+            /*
+             * Get the array class.  If the array ref is null, we won't
+             * have type information (and we'll crash at runtime with a
+             * null pointer exception).
+             */
+            arrayClass = getClassFromRegister(workLine, decInsn.vB, &failure);
+
+            if (arrayClass != NULL) {
+                /* see if the array holds a compatible type */
+                if (!dvmIsArrayClass(arrayClass)) {
+                    LOG_VFY("VFY: invalid aput-object on %s",
+                            arrayClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Find the element class.  resClass->elementClass indicates
+                 * the basic type, which won't be what we want for a
+                 * multi-dimensional array.
+                 *
+                 * All we want to check here is that the element type is a
+                 * reference class.  We *don't* check instanceof here, because
+                 * you can still put a String into a String[] after the latter
+                 * has been cast to an Object[].
+                 */
+                if (arrayClass->descriptor[1] == '[') {
+                    assert(arrayClass->arrayDim > 1);
+                    elementClass = dvmFindArrayClass(&arrayClass->descriptor[1],
+                                        arrayClass->classLoader);
+                } else {
+                    assert(arrayClass->arrayDim == 1);
+                    elementClass = arrayClass->elementClass;
+                }
+                if (elementClass->primitiveType != PRIM_NOT) {
+                    LOG_VFY("VFY: invalid aput-object of %s into %s",
+                            resClass->descriptor, arrayClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        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:
+        {
+            InstField* instField;
+            RegType objType, fieldType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* make sure the field's type is compatible with expectation */
+            fieldType = primSigCharToRegType(instField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iget-1nr of %s.%s (inst=%d field=%d)",
+                        instField->clazz->descriptor,
+                        instField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+    case OP_IGET_WIDE:
+        {
+            RegType dstType;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (instField->signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid iget-wide of %s.%s",
+                        instField->clazz->descriptor,
+                        instField->name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workLine, decInsn.vA, dstType);
+            }
+        }
+        break;
+    case OP_IGET_OBJECT:
+        {
+            ClassObject* fieldClass;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, instField);
+            if (fieldClass == NULL) {
+                /* class not found or primitive type */
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    instField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                assert(!dvmIsPrimitiveClass(fieldClass));
+                setRegisterType(workLine, decInsn.vA,
+                    regTypeFromClass(fieldClass));
+            }
+        }
+        break;
+    case OP_IPUT:
+        tmpType = kRegTypeInteger;
+        goto iput_1nr_common;
+    case OP_IPUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto iput_1nr_common;
+    case OP_IPUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto iput_1nr_common;
+    case OP_IPUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto iput_1nr_common;
+    case OP_IPUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto iput_1nr_common;
+iput_1nr_common:
+        {
+            RegType srcType, fieldType, objType;
+            InstField* instField;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                tmpType = kRegTypeByte;
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+              tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on iput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, instField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get type of field we're storing into */
+            fieldType = primSigCharToRegType(instField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeBoolean && srcType == kRegTypeByte)
+                fieldType = kRegTypeByte;
+
+            verifyRegisterType(workLine, decInsn.vA, fieldType, &failure);
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iput-1nr of %s.%s (inst=%d field=%d)",
+                        instField->clazz->descriptor,
+                        instField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_IPUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        {
+            RegType typeHi = getRegisterType(workLine, decInsn.vA + 1);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (!VERIFY_OK(failure))
+            break;
+
+        InstField* instField;
+        RegType objType;
+
+        objType = getRegisterType(workLine, decInsn.vB);
+        instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                        &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkFinalFieldAccess(meth, instField, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* check the type, which should be prim */
+        switch (instField->signature[0]) {
+        case 'D':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+            break;
+        case 'J':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+            break;
+        default:
+            LOG_VFY("VFY: invalid iput-wide of %s.%s",
+                    instField->clazz->descriptor,
+                    instField->name);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+    case OP_IPUT_OBJECT:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            InstField* instField;
+            RegType objType, valueType;
+
+            objType = getRegisterType(workLine, decInsn.vB);
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, instField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, instField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    instField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)",
+                        decInsn.vA, instField->name,
+                        fieldClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (valueType != kRegTypeZero) {
+                valueClass = regTypeInitializedReferenceToClass(valueType);
+                if (valueClass == NULL) {
+                    LOG_VFY("VFY: storing uninit ref v%d into ref field",
+                        decInsn.vA);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                /* allow if field is any interface or field is base class */
+                if (!dvmIsInterfaceClass(fieldClass) &&
+                    !dvmInstanceof(valueClass, fieldClass))
+                {
+                    LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            instField->clazz->descriptor,
+                            instField->name);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        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:
+        {
+            StaticField* staticField;
+            RegType fieldType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Make sure the field's type is compatible with expectation.
+             * We can get ourselves into trouble if we mix & match loads
+             * and stores with different widths, so rather than just checking
+             * "canConvertTo1nr" we require that the field types have equal
+             * widths.
+             */
+            fieldType = primSigCharToRegType(staticField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sget-1nr of %s.%s (inst=%d actual=%d)",
+                    staticField->clazz->descriptor,
+                    staticField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workLine, decInsn.vA, tmpType);
+        }
+        break;
+    case OP_SGET_WIDE:
+        {
+            StaticField* staticField;
+            RegType dstType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (staticField->signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid sget-wide of %s.%s",
+                        staticField->clazz->descriptor,
+                        staticField->name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workLine, decInsn.vA, dstType);
+            }
+        }
+        break;
+    case OP_SGET_OBJECT:
+        {
+            StaticField* staticField;
+            ClassObject* fieldClass;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, staticField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    staticField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (dvmIsPrimitiveClass(fieldClass)) {
+                LOG_VFY("VFY: attempt to get prim field with sget-object");
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workLine, decInsn.vA, regTypeFromClass(fieldClass));
+        }
+        break;
+    case OP_SPUT:
+        tmpType = kRegTypeInteger;
+        goto sput_1nr_common;
+    case OP_SPUT_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto sput_1nr_common;
+    case OP_SPUT_BYTE:
+        tmpType = kRegTypeByte;
+        goto sput_1nr_common;
+    case OP_SPUT_CHAR:
+        tmpType = kRegTypeChar;
+        goto sput_1nr_common;
+    case OP_SPUT_SHORT:
+        tmpType = kRegTypeShort;
+        goto sput_1nr_common;
+sput_1nr_common:
+        {
+            RegType srcType, fieldType;
+            StaticField* staticField;
+
+            srcType = getRegisterType(workLine, decInsn.vA);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                tmpType = kRegTypeByte;
+
+            /* correct if float */
+            if (srcType == kRegTypeFloat && tmpType == kRegTypeInteger)
+              tmpType = kRegTypeFloat;
+
+            /* make sure the source register has the correct type */
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on sput instr (need %d)",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, staticField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Get type of field we're storing into.  We know that the
+             * contents of the register match the instruction, but we also
+             * need to ensure that the instruction matches the field type.
+             * Using e.g. sput-short to write into a 32-bit integer field
+             * can lead to trouble if we do 16-bit writes.
+             */
+            fieldType = primSigCharToRegType(staticField->signature[0]);
+
+            /* correct if float */
+            if (fieldType == kRegTypeFloat && tmpType == kRegTypeInteger)
+                tmpType = kRegTypeFloat;
+
+            if (fieldType == kRegTypeBoolean && srcType == kRegTypeByte)
+                fieldType = kRegTypeByte;
+
+            verifyRegisterType(workLine, decInsn.vA, fieldType, &failure);
+
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sput-1nr of %s.%s (inst=%d actual=%d)",
+                    staticField->clazz->descriptor,
+                    staticField->name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_SPUT_WIDE:
+        tmpType = getRegisterType(workLine, decInsn.vA);
+        {
+            RegType typeHi = getRegisterType(workLine, decInsn.vA + 1);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (!VERIFY_OK(failure))
+            break;
+
+        StaticField* staticField;
+
+        staticField = getStaticField(meth, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkFinalFieldAccess(meth, staticField, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* check the type, which should be prim */
+        switch (staticField->signature[0]) {
+        case 'D':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeDoubleLo, &failure);
+            break;
+        case 'J':
+            verifyRegisterType(workLine, decInsn.vA, kRegTypeLongLo, &failure);
+            break;
+        default:
+            LOG_VFY("VFY: invalid sput-wide of %s.%s",
+                    staticField->clazz->descriptor,
+                    staticField->name);
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+    case OP_SPUT_OBJECT:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            StaticField* staticField;
+            RegType valueType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, staticField, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, staticField);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'",
+                    staticField->signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workLine, decInsn.vA);
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)",
+                        decInsn.vA, staticField->name,
+                        fieldClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (valueType != kRegTypeZero) {
+                valueClass = regTypeInitializedReferenceToClass(valueType);
+                if (valueClass == NULL) {
+                    LOG_VFY("VFY: storing uninit ref v%d into ref field",
+                        decInsn.vA);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+                /* allow if field is any interface or field is base class */
+                if (!dvmIsInterfaceClass(fieldClass) &&
+                    !dvmInstanceof(valueClass, fieldClass))
+                {
+                    LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            staticField->clazz->descriptor,
+                            staticField->name);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+        {
+            Method* calledMethod;
+            RegType returnType;
+            bool isRange;
+            bool isSuper;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_VIRTUAL_RANGE ||
+                        decInsn.opcode == OP_INVOKE_SUPER_RANGE);
+            isSuper =  (decInsn.opcode == OP_INVOKE_SUPER ||
+                        decInsn.opcode == OP_INVOKE_SUPER_RANGE);
+
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_VIRTUAL, isRange,
+                            isSuper, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+        {
+            RegType returnType;
+            Method* calledMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_DIRECT_RANGE);
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_DIRECT, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /*
+             * Some additional checks when calling <init>.  We know from
+             * the invocation arg check that the "this" argument is an
+             * instance of calledMethod->clazz.  Now we further restrict
+             * that to require that calledMethod->clazz is the same as
+             * this->clazz or this->super, allowing the latter only if
+             * the "this" argument is the same as the "this" argument to
+             * this method (which implies that we're in <init> ourselves).
+             */
+            if (isInitMethod(calledMethod)) {
+                RegType thisType;
+                thisType = getInvocationThis(workLine, &decInsn, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+
+                /* no null refs allowed (?) */
+                if (thisType == kRegTypeZero) {
+                    LOG_VFY("VFY: unable to initialize null ref");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                ClassObject* thisClass;
+
+                thisClass = regTypeReferenceToClass(thisType, uninitMap);
+                assert(thisClass != NULL);
+
+                /* must be in same class or in superclass */
+                if (calledMethod->clazz == thisClass->super) {
+                    if (thisClass != meth->clazz) {
+                        LOG_VFY("VFY: invoke-direct <init> on super only "
+                            "allowed for 'this' in <init>");
+                        failure = VERIFY_ERROR_GENERIC;
+                        break;
+                    }
+                }  else if (calledMethod->clazz != thisClass) {
+                    LOG_VFY("VFY: invoke-direct <init> must be on current "
+                            "class or super");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* arg must be an uninitialized reference */
+                if (!regTypeIsUninitReference(thisType)) {
+                    LOG_VFY("VFY: can only initialize the uninitialized");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Replace the uninitialized reference with an initialized
+                 * one, and clear the entry in the uninit map.  We need to
+                 * do this for all registers that have the same object
+                 * instance in them, not just the "this" register.
+                 */
+                markRefsAsInitialized(workLine, insnRegCount, uninitMap,
+                    thisType, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+            }
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+        {
+            RegType returnType;
+            Method* calledMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_STATIC_RANGE);
+            calledMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_STATIC, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        {
+            RegType /*thisType,*/ returnType;
+            Method* absMethod;
+            bool isRange;
+
+            isRange =  (decInsn.opcode == OP_INVOKE_INTERFACE_RANGE);
+            absMethod = verifyInvocationArgs(meth, workLine, insnRegCount,
+                            &decInsn, uninitMap, METHOD_INTERFACE, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+#if 0       /* can't do this here, fails on dalvik test 052-verifier-fun */
+            /*
+             * Get the type of the "this" arg, which should always be an
+             * interface class.  Because we don't do a full merge on
+             * interface classes, this might have reduced to Object.
+             */
+            thisType = getInvocationThis(workLine, &decInsn, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            if (thisType == kRegTypeZero) {
+                /* null pointer always passes (and always fails at runtime) */
+            } else {
+                ClassObject* thisClass;
+
+                thisClass = regTypeInitializedReferenceToClass(thisType);
+                if (thisClass == NULL) {
+                    LOG_VFY("VFY: interface call on uninitialized");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /*
+                 * Either "thisClass" needs to be the interface class that
+                 * defined absMethod, or absMethod's class needs to be one
+                 * of the interfaces implemented by "thisClass".  (Or, if
+                 * we couldn't complete the merge, this will be Object.)
+                 */
+                if (thisClass != absMethod->clazz &&
+                    thisClass != gDvm.classJavaLangObject &&
+                    !dvmImplements(thisClass, absMethod->clazz))
+                {
+                    LOG_VFY("VFY: unable to match absMethod '%s' with %s interfaces",
+                            absMethod->name, thisClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+#endif
+
+            /*
+             * We don't have an object instance, so we can't find the
+             * concrete method.  However, all of the type information is
+             * in the abstract method, so we're good.
+             */
+            returnType = getMethodReturnType(absMethod);
+            setResultRegisterType(workLine, insnRegCount, returnType);
+            justSetResult = true;
+        }
+        break;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, &failure);
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_NEG_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, &failure);
+        break;
+    case OP_NEG_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeInteger, &failure);
+        break;
+    case OP_LONG_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_FLOAT_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_DOUBLE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeFloat, &failure);
+        break;
+    case OP_DOUBLE_TO_INT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_LONG:
+        checkUnop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_FLOAT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_BYTE:
+        checkUnop(workLine, &decInsn,
+            kRegTypeByte, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_CHAR:
+        checkUnop(workLine, &decInsn,
+            kRegTypeChar, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_SHORT:
+        checkUnop(workLine, &decInsn,
+            kRegTypeShort, kRegTypeInteger, &failure);
+        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:
+        checkBinop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        checkBinop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+        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:
+        checkBinop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+        break;
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        /* shift distance is Int, making these different from other binops */
+        checkBinop(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+        break;
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        checkBinop(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+        break;
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        checkBinop(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+            &failure);
+        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:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    case OP_DIV_INT_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        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:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+        break;
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+        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:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+        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:
+        checkBinop2addr(workLine, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+            &failure);
+        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:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    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:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_SHR_INT_LIT8:
+        tmpType = adjustForRightShift(workLine,
+            decInsn.vB, decInsn.vC, false, &failure);
+        checkLitop(workLine, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_USHR_INT_LIT8:
+        tmpType = adjustForRightShift(workLine,
+            decInsn.vB, decInsn.vC, true, &failure);
+        checkLitop(workLine, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+        checkLitop(workLine, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+
+    /*
+     * This falls into the general category of "optimized" instructions,
+     * which don't generally appear during verification.  Because it's
+     * inserted in the course of verification, we can expect to see it here.
+     */
+    case OP_THROW_VERIFICATION_ERROR:
+        break;
+
+    /*
+     * Verifying "quickened" instructions is tricky, because we have
+     * discarded the original field/method information.  The byte offsets
+     * and vtable indices only have meaning in the context of an object
+     * instance.
+     *
+     * If a piece of code declares a local reference variable, assigns
+     * null to it, and then issues a virtual method call on it, we
+     * cannot evaluate the method call during verification.  This situation
+     * isn't hard to handle, since we know the call will always result in an
+     * NPE, and the arguments and return value don't matter.  Any code that
+     * depends on the result of the method call is inaccessible, so the
+     * fact that we can't fully verify anything that comes after the bad
+     * call is not a problem.
+     *
+     * We must also consider the case of multiple code paths, only some of
+     * which involve a null reference.  We can completely verify the method
+     * if we sidestep the results of executing with a null reference.
+     * For example, if on the first pass through the code we try to do a
+     * virtual method invocation through a null ref, we have to skip the
+     * method checks and have the method return a "wildcard" type (which
+     * merges with anything to become that other thing).  The move-result
+     * will tell us if it's a reference, single-word numeric, or double-word
+     * value.  We continue to perform the verification, and at the end of
+     * the function any invocations that were never fully exercised are
+     * marked as null-only.
+     *
+     * We would do something similar for the field accesses.  The field's
+     * type, once known, can be used to recover the width of short integers.
+     * If the object reference was null, the field-get returns the "wildcard"
+     * type, which is acceptable for any operation.
+     */
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        /* fall through to failure */
+
+    /*
+     * These instructions are equivalent (from the verifier's point of view)
+     * to the original form.  The change was made for correctness rather
+     * than improved performance (except for invoke-object-init, which
+     * provides both).  The substitution takes place after verification
+     * completes, though, so we don't expect to see them here.
+     */
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+    case OP_RETURN_VOID_BARRIER:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+        /* fall through to failure */
+
+    /* these should never appear during verification */
+    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_BREAKPOINT:
+    case OP_UNUSED_FF:
+        failure = VERIFY_ERROR_GENERIC;
+        break;
+
+    /*
+     * DO NOT add a "default" clause here.  Without it the compiler will
+     * complain if an instruction is missing (which is desirable).
+     */
+    }
+
+    if (!VERIFY_OK(failure)) {
+        if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing) {
+            /* immediate failure, reject class */
+            LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                decInsn.opcode, insnIdx);
+            goto bail;
+        } else {
+            /* replace opcode and continue on */
+            ALOGD("VFY: replacing opcode 0x%02x at 0x%04x",
+                decInsn.opcode, insnIdx);
+            if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) {
+                LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                    decInsn.opcode, insnIdx);
+                goto bail;
+            }
+            /* IMPORTANT: meth->insns may have been changed */
+            insns = meth->insns + insnIdx;
+
+            /* continue on as if we just handled a throw-verification-error */
+            failure = VERIFY_ERROR_NONE;
+            nextFlags = kInstrCanThrow;
+        }
+    }
+
+    /*
+     * If we didn't just set the result register, clear it out.  This
+     * ensures that you can only use "move-result" immediately after the
+     * result is set.  (We could check this statically, but it's not
+     * expensive and it makes our debugging output cleaner.)
+     */
+    if (!justSetResult) {
+        int reg = RESULT_REGISTER(insnRegCount);
+        setRegisterType(workLine, reg, kRegTypeUnknown);
+        setRegisterType(workLine, reg+1, kRegTypeUnknown);
+    }
+
+    /*
+     * Handle "continue".  Tag the next consecutive instruction.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
+        if (insnIdx+insnWidth >= insnsSize) {
+            LOG_VFY_METH(meth,
+                "VFY: execution can walk off end of code area (from %#x)",
+                insnIdx);
+            goto bail;
+        }
+
+        /*
+         * The only way to get to a move-exception instruction is to get
+         * thrown there.  Make sure the next instruction isn't one.
+         */
+        if (!checkMoveException(meth, insnIdx+insnWidth, "next"))
+            goto bail;
+
+        if (getRegisterLine(regTable, insnIdx+insnWidth)->regTypes != NULL) {
+            /*
+             * Merge registers into what we have for the next instruction,
+             * and set the "changed" flag if needed.
+             */
+            if (!updateRegisters(meth, insnFlags, regTable, insnIdx+insnWidth,
+                    workLine))
+                goto bail;
+        } else {
+            /*
+             * We're not recording register data for the next instruction,
+             * so we don't know what the prior state was.  We have to
+             * assume that something has changed and re-evaluate it.
+             */
+            dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
+        }
+    }
+
+    /*
+     * Handle "branch".  Tag the branch target.
+     *
+     * NOTE: instructions like OP_EQZ provide information about the state
+     * of the register when the branch is taken or not taken.  For example,
+     * somebody could get a reference field, check it for zero, and if the
+     * branch is taken immediately store that register in a boolean field
+     * since the value is known to be zero.  We do not currently account for
+     * that, and will reject the code.
+     *
+     * TODO: avoid re-fetching the branch target
+     */
+    if ((nextFlags & kInstrCanBranch) != 0) {
+        bool isConditional;
+
+        if (!dvmGetBranchOffset(meth, insnFlags, insnIdx, &branchTarget,
+                &isConditional))
+        {
+            /* should never happen after static verification */
+            LOG_VFY_METH(meth, "VFY: bad branch at %d", insnIdx);
+            goto bail;
+        }
+        assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
+        assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
+
+        if (!checkMoveException(meth, insnIdx+branchTarget, "branch"))
+            goto bail;
+
+        /* update branch target, set "changed" if appropriate */
+        if (!updateRegisters(meth, insnFlags, regTable, insnIdx+branchTarget,
+                workLine))
+            goto bail;
+    }
+
+    /*
+     * Handle "switch".  Tag all possible branch targets.
+     *
+     * We've already verified that the table is structurally sound, so we
+     * just need to walk through and tag the 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 < insnsSize);
+
+            if (!checkMoveException(meth, absOffset, "switch"))
+                goto bail;
+
+            if (!updateRegisters(meth, insnFlags, regTable, absOffset,
+                    workLine))
+                goto bail;
+        }
+    }
+
+    /*
+     * 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))
+    {
+        const DexCode* pCode = dvmGetMethodCode(meth);
+        DexCatchIterator iterator;
+        bool hasCatchAll = false;
+
+        if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+            for (;;) {
+                DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+                if (handler == NULL) {
+                    break;
+                }
+
+                if (handler->typeIdx == kDexNoIndex)
+                    hasCatchAll = true;
+
+                /*
+                 * Merge registers into the "catch" block.  We want to
+                 * use the "savedRegs" rather than "workRegs", because
+                 * at runtime the exception will be thrown before the
+                 * instruction modifies any registers.
+                 */
+                if (!updateRegisters(meth, insnFlags, regTable,
+                        handler->address, &regTable->savedLine))
+                    goto bail;
+            }
+        }
+
+        /*
+         * If the monitor stack depth is nonzero, there must be a "catch all"
+         * handler for this instruction.  This does apply to monitor-exit
+         * because of async exception handling.
+         */
+        if (workLine->monitorStackTop != 0 && !hasCatchAll) {
+            /*
+             * The state in workLine reflects the post-execution state.
+             * If the current instruction is a monitor-enter and the monitor
+             * stack was empty, we don't need a catch-all (if it throws,
+             * it will do so before grabbing the lock).
+             */
+            if (!(decInsn.opcode == OP_MONITOR_ENTER &&
+                  workLine->monitorStackTop == 1))
+            {
+                LOG_VFY_METH(meth,
+                    "VFY: no catch-all for instruction at 0x%04x", insnIdx);
+                goto bail;
+            }
+        }
+    }
+
+    /*
+     * If we're returning from the method, make sure our monitor stack
+     * is empty.
+     */
+    if ((nextFlags & kInstrCanReturn) != 0 && workLine->monitorStackTop != 0) {
+        LOG_VFY_METH(meth, "VFY: return with stack depth=%d at 0x%04x",
+            workLine->monitorStackTop, insnIdx);
+        goto bail;
+    }
+
+    /*
+     * 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 < insnsSize &&
+        dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * callback function used in dumpRegTypes to print local vars
+ * valid at a given address.
+ */
+static void logLocalsCb(void *cnxt, u2 reg, u4 startAddress, u4 endAddress,
+        const char *name, const char *descriptor,
+        const char *signature)
+{
+    int addr = *((int *)cnxt);
+
+    if (addr >= (int) startAddress && addr < (int) endAddress)
+    {
+        ALOGI("        %2d: '%s' %s", reg, name, descriptor);
+    }
+}
+
+/*
+ * Dump the register types for the specifed address to the log file.
+ */
+static void dumpRegTypes(const VerifierData* vdata,
+    const RegisterLine* registerLine, int addr, const char* addrName,
+    const UninitInstanceMap* uninitMap, int displayFlags)
+{
+    const Method* meth = vdata->method;
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const RegType* addrRegs = registerLine->regTypes;
+    int regCount = meth->registersSize;
+    int fullRegCount = regCount + kExtraRegs;
+    bool branchTarget = dvmInsnIsBranchTarget(insnFlags, addr);
+    int i;
+
+    assert(addr >= 0 && addr < (int) dvmGetMethodInsnsSize(meth));
+
+    int regCharSize = fullRegCount + (fullRegCount-1)/4 + 2 +1;
+    char regChars[regCharSize +1];
+    memset(regChars, ' ', regCharSize);
+    regChars[0] = '[';
+    if (regCount == 0)
+        regChars[1] = ']';
+    else
+        regChars[1 + (regCount-1) + (regCount-1)/4 +1] = ']';
+    regChars[regCharSize] = '\0';
+
+    for (i = 0; i < regCount + kExtraRegs; i++) {
+        char tch;
+
+        switch (addrRegs[i]) {
+        case kRegTypeUnknown:       tch = '.';  break;
+        case kRegTypeConflict:      tch = 'X';  break;
+        case kRegTypeZero:          tch = '0';  break;
+        case kRegTypeOne:           tch = '1';  break;
+        case kRegTypeBoolean:       tch = 'Z';  break;
+        case kRegTypeConstPosByte:  tch = 'y';  break;
+        case kRegTypeConstByte:     tch = 'Y';  break;
+        case kRegTypeConstPosShort: tch = 'h';  break;
+        case kRegTypeConstShort:    tch = 'H';  break;
+        case kRegTypeConstChar:     tch = 'c';  break;
+        case kRegTypeConstInteger:  tch = 'i';  break;
+        case kRegTypePosByte:       tch = 'b';  break;
+        case kRegTypeByte:          tch = 'B';  break;
+        case kRegTypePosShort:      tch = 's';  break;
+        case kRegTypeShort:         tch = 'S';  break;
+        case kRegTypeChar:          tch = 'C';  break;
+        case kRegTypeInteger:       tch = 'I';  break;
+        case kRegTypeFloat:         tch = 'F';  break;
+        case kRegTypeConstLo:       tch = 'N';  break;
+        case kRegTypeConstHi:       tch = 'n';  break;
+        case kRegTypeLongLo:        tch = 'J';  break;
+        case kRegTypeLongHi:        tch = 'j';  break;
+        case kRegTypeDoubleLo:      tch = 'D';  break;
+        case kRegTypeDoubleHi:      tch = 'd';  break;
+        default:
+            if (regTypeIsReference(addrRegs[i])) {
+                if (regTypeIsUninitReference(addrRegs[i]))
+                    tch = 'U';
+                else
+                    tch = 'L';
+            } else {
+                tch = '*';
+                assert(false);
+            }
+            break;
+        }
+
+        if (i < regCount)
+            regChars[1 + i + (i/4)] = tch;
+        else
+            regChars[1 + i + (i/4) + 2] = tch;
+    }
+
+    if (addr == 0 && addrName != NULL) {
+        ALOGI("%c%s %s mst=%d", branchTarget ? '>' : ' ',
+            addrName, regChars, registerLine->monitorStackTop);
+    } else {
+        ALOGI("%c0x%04x %s mst=%d", branchTarget ? '>' : ' ',
+            addr, regChars, registerLine->monitorStackTop);
+    }
+    if (displayFlags & DRT_SHOW_LIVENESS) {
+        /*
+         * We can't use registerLine->liveRegs because it might be the
+         * "work line" rather than the copy from RegisterTable.
+         */
+        BitVector* liveRegs = vdata->registerLines[addr].liveRegs;
+        if (liveRegs != NULL)  {
+            char liveChars[regCharSize + 1];
+            memset(liveChars, ' ', regCharSize);
+            liveChars[regCharSize] = '\0';
+
+            for (i = 0; i < regCount; i++) {
+                bool isLive = dvmIsBitSet(liveRegs, i);
+                liveChars[i + 1 + (i / 4)] = isLive ? '+' : '-';
+            }
+            ALOGI("        %s", liveChars);
+        } else {
+            ALOGI("        %c", '#');
+        }
+    }
+
+    if (displayFlags & DRT_SHOW_REF_TYPES) {
+        for (i = 0; i < regCount + kExtraRegs; i++) {
+            if (regTypeIsReference(addrRegs[i]) && addrRegs[i] != kRegTypeZero)
+            {
+                ClassObject* clazz = regTypeReferenceToClass(addrRegs[i], uninitMap);
+                assert(dvmIsHeapAddress((Object*)clazz));
+                if (i < regCount) {
+                    ALOGI("        %2d: 0x%08x %s%s",
+                        i, addrRegs[i],
+                        regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+                        clazz->descriptor);
+                } else {
+                    ALOGI("        RS: 0x%08x %s%s",
+                        addrRegs[i],
+                        regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+                        clazz->descriptor);
+                }
+            }
+        }
+    }
+    if (displayFlags & DRT_SHOW_LOCALS) {
+        dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile,
+                dvmGetMethodCode(meth),
+                meth->clazz->descriptor,
+                meth->prototype.protoIdx,
+                meth->accessFlags,
+                NULL, logLocalsCb, &addr);
+    }
+}
diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h
new file mode 100644
index 0000000..5857895
--- /dev/null
+++ b/vm/analysis/CodeVerify.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik bytecode verifier.
+ */
+#ifndef DALVIK_CODEVERIFY_H_
+#define DALVIK_CODEVERIFY_H_
+
+#include "analysis/VerifySubs.h"
+#include "analysis/VfyBasicBlock.h"
+
+/*
+ * Enumeration for register type values.  The "hi" piece of a 64-bit value
+ * MUST immediately follow the "lo" piece in the enumeration, so we can check
+ * that hi==lo+1.
+ *
+ * Assignment of constants:
+ *   [-MAXINT,-32768)   : integer
+ *   [-32768,-128)      : short
+ *   [-128,0)           : byte
+ *   0                  : zero
+ *   1                  : one
+ *   [2,128)            : posbyte
+ *   [128,32768)        : posshort
+ *   [32768,65536)      : char
+ *   [65536,MAXINT]     : integer
+ *
+ * Allowed "implicit" widening conversions:
+ *   zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
+ *   one -> boolean, posbyte, byte, posshort, short, char, integer
+ *   boolean -> posbyte, byte, posshort, short, char, integer
+ *   posbyte -> posshort, short, integer, char
+ *   byte -> short, integer
+ *   posshort -> integer, char
+ *   short -> integer
+ *   char -> integer
+ *
+ * In addition, all of the above can convert to "float".
+ *
+ * We're more careful with integer values than the spec requires.  The
+ * motivation is to restrict byte/char/short to the correct range of values.
+ * For example, if a method takes a byte argument, we don't want to allow
+ * the code to load the constant "1024" and pass it in.
+ */
+enum {
+    kRegTypeUnknown = 0,    /* initial state; use value=0 so calloc works */
+    kRegTypeUninit = 1,     /* MUST be odd to distinguish from pointer */
+    kRegTypeConflict,       /* merge clash makes this reg's type unknowable */
+
+    /*
+     * Category-1nr types.  The order of these is chiseled into a couple
+     * of tables, so don't add, remove, or reorder if you can avoid it.
+     */
+#define kRegType1nrSTART    kRegTypeZero
+    kRegTypeZero,           /* 32-bit 0, could be Boolean, Int, Float, or Ref */
+    kRegTypeOne,            /* 32-bit 1, could be Boolean, Int, Float */
+    kRegTypeBoolean,        /* must be 0 or 1 */
+    kRegTypeConstPosByte,   /* const derived byte, known positive */
+    kRegTypeConstByte,      /* const derived byte */
+    kRegTypeConstPosShort,  /* const derived short, known positive */
+    kRegTypeConstShort,     /* const derived short */
+    kRegTypeConstChar,      /* const derived char */
+    kRegTypeConstInteger,   /* const derived integer */
+    kRegTypePosByte,        /* byte, known positive (can become char) */
+    kRegTypeByte,
+    kRegTypePosShort,       /* short, known positive (can become char) */
+    kRegTypeShort,
+    kRegTypeChar,
+    kRegTypeInteger,
+    kRegTypeFloat,
+#define kRegType1nrEND      kRegTypeFloat
+
+    kRegTypeConstLo,        /* const derived wide, lower half */
+    kRegTypeConstHi,        /* const derived wide, upper half */
+    kRegTypeLongLo,         /* lower-numbered register; endian-independent */
+    kRegTypeLongHi,
+    kRegTypeDoubleLo,
+    kRegTypeDoubleHi,
+
+    /*
+     * Enumeration max; this is used with "full" (32-bit) RegType values.
+     *
+     * Anything larger than this is a ClassObject or uninit ref.  Mask off
+     * all but the low 8 bits; if you're left with kRegTypeUninit, pull
+     * the uninit index out of the high 24.  Because kRegTypeUninit has an
+     * odd value, there is no risk of a particular ClassObject pointer bit
+     * pattern being confused for it (assuming our class object allocator
+     * uses word alignment).
+     */
+    kRegTypeMAX
+};
+#define kRegTypeUninitMask  0xff
+#define kRegTypeUninitShift 8
+
+/*
+ * RegType holds information about the type of data held in a register.
+ * For most types it's a simple enum.  For reference types it holds a
+ * pointer to the ClassObject, and for uninitialized references it holds
+ * an index into the UninitInstanceMap.
+ */
+typedef u4 RegType;
+
+/*
+ * A bit vector indicating which entries in the monitor stack are
+ * associated with this register.  The low bit corresponds to the stack's
+ * bottom-most entry.
+ */
+typedef u4 MonitorEntries;
+#define kMaxMonitorStackDepth   (sizeof(MonitorEntries) * 8)
+
+/*
+ * During verification, we associate one of these with every "interesting"
+ * instruction.  We track the status of all registers, and (if the method
+ * has any monitor-enter instructions) maintain a stack of entered monitors
+ * (identified by code unit offset).
+ *
+ * If live-precise register maps are enabled, the "liveRegs" vector will
+ * be populated.  Unlike the other lists of registers here, we do not
+ * track the liveness of the method result register (which is not visible
+ * to the GC).
+ */
+struct RegisterLine {
+    RegType*        regTypes;
+    MonitorEntries* monitorEntries;
+    u4*             monitorStack;
+    unsigned int    monitorStackTop;
+    BitVector*      liveRegs;
+};
+
+/*
+ * Table that maps uninitialized instances to classes, based on the
+ * address of the new-instance instruction.  One per method.
+ */
+struct UninitInstanceMap {
+    int numEntries;
+    struct {
+        int             addr;   /* code offset, or -1 for method arg ("this") */
+        ClassObject*    clazz;  /* class created at this address */
+    } map[1];
+};
+#define kUninitThisArgAddr  (-1)
+#define kUninitThisArgSlot  0
+
+/*
+ * Various bits of data used by the verifier and register map generator.
+ */
+struct VerifierData {
+    /*
+     * The method we're working on.
+     */
+    const Method*   method;
+
+    /*
+     * Number of code units of instructions in the method.  A cache of the
+     * value calculated by dvmGetMethodInsnsSize().
+     */
+    u4              insnsSize;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize".  (Does not include the
+     * pending return value.)
+     */
+    u4              insnRegCount;
+
+    /*
+     * Instruction widths and flags, one entry per code unit.
+     */
+    InsnFlags*      insnFlags;
+
+    /*
+     * Uninitialized instance map, used for tracking the movement of
+     * objects that have been allocated but not initialized.
+     */
+    UninitInstanceMap* uninitMap;
+
+    /*
+     * Array of RegisterLine structs, one entry per code unit.  We only need
+     * entries for code units that hold the start of an "interesting"
+     * instruction.  For register map generation, we're only interested
+     * in GC points.
+     */
+    RegisterLine*   registerLines;
+
+    /*
+     * The number of occurrences of specific opcodes.
+     */
+    size_t          newInstanceCount;
+    size_t          monitorEnterCount;
+
+    /*
+     * Array of pointers to basic blocks, one entry per code unit.  Used
+     * for liveness analysis.
+     */
+    VfyBasicBlock** basicBlocks;
+};
+
+
+/* table with static merge logic for primitive types */
+extern const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX];
+
+
+/*
+ * Returns "true" if the flags indicate that this address holds the start
+ * of an instruction.
+ */
+INLINE bool dvmInsnIsOpcode(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagWidthMask) != 0;
+}
+
+/*
+ * Extract the unsigned 16-bit instruction width from "flags".
+ */
+INLINE int dvmInsnGetWidth(const InsnFlags* insnFlags, int addr) {
+    return insnFlags[addr] & kInsnFlagWidthMask;
+}
+
+/*
+ * Changed?
+ */
+INLINE bool dvmInsnIsChanged(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagChanged) != 0;
+}
+INLINE void dvmInsnSetChanged(InsnFlags* insnFlags, int addr, bool changed)
+{
+    if (changed)
+        insnFlags[addr] |= kInsnFlagChanged;
+    else
+        insnFlags[addr] &= ~kInsnFlagChanged;
+}
+
+/*
+ * Visited?
+ */
+INLINE bool dvmInsnIsVisited(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagVisited) != 0;
+}
+INLINE void dvmInsnSetVisited(InsnFlags* insnFlags, int addr, bool changed)
+{
+    if (changed)
+        insnFlags[addr] |= kInsnFlagVisited;
+    else
+        insnFlags[addr] &= ~kInsnFlagVisited;
+}
+
+/*
+ * Visited or changed?
+ */
+INLINE bool dvmInsnIsVisitedOrChanged(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & (kInsnFlagVisited|kInsnFlagChanged)) != 0;
+}
+
+/*
+ * In a "try" block?
+ */
+INLINE bool dvmInsnIsInTry(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagInTry) != 0;
+}
+INLINE void dvmInsnSetInTry(InsnFlags* insnFlags, int addr, bool inTry)
+{
+    assert(inTry);
+    //if (inTry)
+        insnFlags[addr] |= kInsnFlagInTry;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagInTry;
+}
+
+/*
+ * Instruction is a branch target or exception handler?
+ */
+INLINE bool dvmInsnIsBranchTarget(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagBranchTarget) != 0;
+}
+INLINE void dvmInsnSetBranchTarget(InsnFlags* insnFlags, int addr,
+    bool isBranch)
+{
+    assert(isBranch);
+    //if (isBranch)
+        insnFlags[addr] |= kInsnFlagBranchTarget;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagBranchTarget;
+}
+
+/*
+ * Instruction is a GC point?
+ */
+INLINE bool dvmInsnIsGcPoint(const InsnFlags* insnFlags, int addr) {
+    return (insnFlags[addr] & kInsnFlagGcPoint) != 0;
+}
+INLINE void dvmInsnSetGcPoint(InsnFlags* insnFlags, int addr,
+    bool isGcPoint)
+{
+    assert(isGcPoint);
+    //if (isGcPoint)
+        insnFlags[addr] |= kInsnFlagGcPoint;
+    //else
+    //    insnFlags[addr] &= ~kInsnFlagGcPoint;
+}
+
+
+/*
+ * Create a new UninitInstanceMap.
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+    const InsnFlags* insnFlags, int newInstanceCount);
+
+/*
+ * Release the storage associated with an UninitInstanceMap.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap);
+
+/*
+ * Verify bytecode in "meth".  "insnFlags" should be populated with
+ * instruction widths and "in try" flags.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata);
+
+#endif  // DALVIK_CODEVERIFY_H_
diff --git a/vm/analysis/DexPrepare.cpp b/vm/analysis/DexPrepare.cpp
new file mode 100644
index 0000000..e8112d5
--- /dev/null
+++ b/vm/analysis/DexPrepare.cpp
@@ -0,0 +1,1563 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Prepare a DEX file for use by the VM.  Depending upon the VM options
+ * we will attempt to verify and/or optimize the code, possibly appending
+ * register maps.
+ *
+ * TODO: the format of the optimized header is currently "whatever we
+ * happen to write", since the VM that writes it is by definition the same
+ * as the VM that reads it.  Still, it should be better documented and
+ * more rigorously structured.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "analysis/RegisterMap.h"
+#include "analysis/Optimize.h"
+
+#include <string>
+
+#include <libgen.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <zlib.h>
+
+/* fwd */
+static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex);
+static bool loadAllClasses(DvmDex* pDvmDex);
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+    bool doOpt);
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+    const DexClassDef* pClassDef, bool doVerify, bool doOpt);
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
+static int writeDependencies(int fd, u4 modWhen, u4 crc);
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,\
+    const RegisterMapBuilder* pRegMapBuilder);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
+
+/*
+ * Get just the directory portion of the given path. Equivalent to dirname(3).
+ */
+static std::string saneDirName(const std::string& path) {
+    size_t n = path.rfind('/');
+    if (n == std::string::npos) {
+        return ".";
+    }
+    return path.substr(0, n);
+}
+
+/*
+ * Helper for dvmOpenCacheDexFile() in a known-error case: Check to
+ * see if the directory part of the given path (all but the last
+ * component) exists and is writable. Complain to the log if not.
+ */
+static bool directoryIsValid(const std::string& fileName)
+{
+    std::string dirName(saneDirName(fileName));
+
+    struct stat sb;
+    if (stat(dirName.c_str(), &sb) < 0) {
+        ALOGE("Could not stat dex cache directory '%s': %s", dirName.c_str(), strerror(errno));
+        return false;
+    }
+
+    if (!S_ISDIR(sb.st_mode)) {
+        ALOGE("Dex cache directory isn't a directory: %s", dirName.c_str());
+        return false;
+    }
+
+    if (access(dirName.c_str(), W_OK) < 0) {
+        ALOGE("Dex cache directory isn't writable: %s", dirName.c_str());
+        return false;
+    }
+
+    if (access(dirName.c_str(), R_OK) < 0) {
+        ALOGE("Dex cache directory isn't readable: %s", dirName.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Return the fd of an open file in the DEX file cache area.  If the cache
+ * file doesn't exist or is out of date, this will remove the old entry,
+ * create a new one (writing only the file header), and return with the
+ * "new file" flag set.
+ *
+ * It's possible to execute from an unoptimized DEX file directly,
+ * assuming the byte ordering and structure alignment is correct, but
+ * disadvantageous because some significant optimizations are not possible.
+ * It's not generally possible to do the same from an uncompressed Jar
+ * file entry, because we have to guarantee 32-bit alignment in the
+ * memory-mapped file.
+ *
+ * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
+ * and "crc32" come from the Zip directory entry.  For a stand-alone DEX
+ * file, it's the modification date of the file and the Adler32 from the
+ * DEX header (which immediately follows the magic).  If these don't
+ * match what's stored in the opt header, we reject the file immediately.
+ *
+ * On success, the file descriptor will be positioned just past the "opt"
+ * file header, and will be locked with flock.  "*pCachedName" will point
+ * to newly-allocated storage.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
+    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
+{
+    int fd, cc;
+    struct stat fdStat, fileStat;
+    bool readOnly = false;
+
+    *pNewFile = false;
+
+retry:
+    /*
+     * Try to open the cache file.  If we've been asked to,
+     * create it if it doesn't exist.
+     */
+    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+    if (fd < 0) {
+        fd = open(cacheFileName, O_RDONLY, 0);
+        if (fd < 0) {
+            if (createIfMissing) {
+                // TODO: write an equivalent of strerror_r that returns a std::string.
+                const std::string errnoString(strerror(errno));
+                if (directoryIsValid(cacheFileName)) {
+                    ALOGE("Can't open dex cache file '%s': %s", cacheFileName, errnoString.c_str());
+                }
+            }
+            return fd;
+        }
+        readOnly = true;
+    } else {
+        fchmod(fd, 0644);
+    }
+
+    /*
+     * Grab an exclusive lock on the cache file.  If somebody else is
+     * working on it, we'll block here until they complete.  Because
+     * we're waiting on an external resource, we go into VMWAIT mode.
+     */
+    ALOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)",
+        cacheFileName, fd, isBootstrap);
+    ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    cc = flock(fd, LOCK_EX | LOCK_NB);
+    if (cc != 0) {
+        ALOGD("DexOpt: sleeping on flock(%s)", cacheFileName);
+        cc = flock(fd, LOCK_EX);
+    }
+    dvmChangeStatus(NULL, oldStatus);
+    if (cc != 0) {
+        ALOGE("Can't lock dex cache '%s': %d", cacheFileName, cc);
+        close(fd);
+        return -1;
+    }
+    ALOGV("DexOpt:  locked cache file");
+
+    /*
+     * Check to see if the fd we opened and locked matches the file in
+     * the filesystem.  If they don't, then somebody else unlinked ours
+     * and created a new file, and we need to use that one instead.  (If
+     * we caught them between the unlink and the create, we'll get an
+     * ENOENT from the file stat.)
+     */
+    cc = fstat(fd, &fdStat);
+    if (cc != 0) {
+        ALOGE("Can't stat open file '%s'", cacheFileName);
+        LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+        goto close_fail;
+    }
+    cc = stat(cacheFileName, &fileStat);
+    if (cc != 0 ||
+        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+    {
+        ALOGD("DexOpt: our open cache file is stale; sleeping and retrying");
+        LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+        flock(fd, LOCK_UN);
+        close(fd);
+        usleep(250 * 1000);     /* if something is hosed, don't peg machine */
+        goto retry;
+    }
+
+    /*
+     * We have the correct file open and locked.  If the file size is zero,
+     * then it was just created by us, and we want to fill in some fields
+     * in the "opt" header and set "*pNewFile".  Otherwise, we want to
+     * verify that the fields in the header match our expectations, and
+     * reset the file if they don't.
+     */
+    if (fdStat.st_size == 0) {
+        if (readOnly) {
+            ALOGW("DexOpt: file has zero length and isn't writable");
+            goto close_fail;
+        }
+        cc = dexOptCreateEmptyHeader(fd);
+        if (cc != 0)
+            goto close_fail;
+        *pNewFile = true;
+        ALOGV("DexOpt: successfully initialized new cache file");
+    } else {
+        bool expectVerify, expectOpt;
+
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
+            expectVerify = false;
+        } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
+            expectVerify = !isBootstrap;
+        } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
+            expectVerify = true;
+        }
+
+        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
+            expectOpt = false;
+        } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                   gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
+            expectOpt = expectVerify;
+        } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
+            expectOpt = true;
+        }
+
+        ALOGV("checking deps, expecting vfy=%d opt=%d",
+            expectVerify, expectOpt);
+
+        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
+                expectVerify, expectOpt))
+        {
+            if (readOnly) {
+                /*
+                 * We could unlink and rewrite the file if we own it or
+                 * the "sticky" bit isn't set on the directory.  However,
+                 * we're not able to truncate it, which spoils things.  So,
+                 * give up now.
+                 */
+                if (createIfMissing) {
+                    ALOGW("Cached DEX '%s' (%s) is stale and not writable",
+                        fileName, cacheFileName);
+                }
+                goto close_fail;
+            }
+
+            /*
+             * If we truncate the existing file before unlinking it, any
+             * process that has it mapped will fail when it tries to touch
+             * the pages.
+             *
+             * This is very important.  The zygote process will have the
+             * boot DEX files (core, framework, etc.) mapped early.  If
+             * (say) core.dex gets updated, and somebody launches an app
+             * that uses App.dex, then App.dex gets reoptimized because it's
+             * dependent upon the boot classes.  However, dexopt will be
+             * using the *new* core.dex to do the optimizations, while the
+             * app will actually be running against the *old* core.dex
+             * because it starts from zygote.
+             *
+             * Even without zygote, it's still possible for a class loader
+             * to pull in an APK that was optimized against an older set
+             * of DEX files.  We must ensure that everything fails when a
+             * boot DEX gets updated, and for general "why aren't my
+             * changes doing anything" purposes its best if we just make
+             * everything crash when a DEX they're using gets updated.
+             */
+            ALOGD("ODEX file is stale or bad; removing and retrying (%s)",
+                cacheFileName);
+            if (ftruncate(fd, 0) != 0) {
+                ALOGW("Warning: unable to truncate cache file '%s': %s",
+                    cacheFileName, strerror(errno));
+                /* keep going */
+            }
+            if (unlink(cacheFileName) != 0) {
+                ALOGW("Warning: unable to remove cache file '%s': %d %s",
+                    cacheFileName, errno, strerror(errno));
+                /* keep going; permission failure should probably be fatal */
+            }
+            LOGVV("DexOpt: unlocking cache file %s", cacheFileName);
+            flock(fd, LOCK_UN);
+            close(fd);
+            goto retry;
+        } else {
+            ALOGV("DexOpt: good deps in cache file");
+        }
+    }
+
+    assert(fd >= 0);
+    return fd;
+
+close_fail:
+    flock(fd, LOCK_UN);
+    close(fd);
+    return -1;
+}
+
+/*
+ * Unlock the file descriptor.
+ *
+ * Returns "true" on success.
+ */
+bool dvmUnlockCachedDexFile(int fd)
+{
+    LOGVV("DexOpt: unlocking cache file fd=%d", fd);
+    return (flock(fd, LOCK_UN) == 0);
+}
+
+
+/*
+ * Given a descriptor for a file with DEX data in it, produce an
+ * optimized version.
+ *
+ * The file pointed to by "fd" is expected to be a locked shared resource
+ * (or private); we make no efforts to enforce multi-process correctness
+ * here.
+ *
+ * "fileName" is only used for debug output.  "modWhen" and "crc" are stored
+ * in the dependency set.
+ *
+ * The "isBootstrap" flag determines how the optimizer and verifier handle
+ * package-scope access checks.  When optimizing, we only load the bootstrap
+ * class DEX files and the target DEX, so the flag determines whether the
+ * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
+ * This only really matters if the target DEX contains classes that claim to
+ * be in the same package as bootstrap classes.
+ *
+ * The optimizer will need to load every class in the target DEX file.
+ * This is generally undesirable, so we start a subprocess to do the
+ * work and wait for it to complete.
+ *
+ * Returns "true" on success.  All data will have been written to "fd".
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    const char* lastPart = strrchr(fileName, '/');
+    if (lastPart != NULL)
+        lastPart++;
+    else
+        lastPart = fileName;
+
+    ALOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---", lastPart, isBootstrap);
+
+    pid_t pid;
+
+    /*
+     * This could happen if something in our bootclasspath, which we thought
+     * was all optimized, got rejected.
+     */
+    if (gDvm.optimizing) {
+        ALOGW("Rejecting recursive optimization attempt on '%s'", fileName);
+        return false;
+    }
+
+    pid = fork();
+    if (pid == 0) {
+        static const int kUseValgrind = 0;
+        static const char* kDexOptBin = "/bin/dexopt";
+        static const char* kValgrinder = "/usr/bin/valgrind";
+        static const int kFixedArgCount = 10;
+        static const int kValgrindArgCount = 5;
+        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
+        int bcpSize = dvmGetBootPathSize();
+        int argc = kFixedArgCount + bcpSize
+            + (kValgrindArgCount * kUseValgrind);
+        const char* argv[argc+1];             // last entry is NULL
+        char values[argc][kMaxIntLen];
+        char* execFile;
+        const char* androidRoot;
+        int flags;
+
+        /* change process groups, so we don't clash with ProcessManager */
+        setpgid(0, 0);
+
+        /* full path to optimizer */
+        androidRoot = getenv("ANDROID_ROOT");
+        if (androidRoot == NULL) {
+            ALOGW("ANDROID_ROOT not set, defaulting to /system");
+            androidRoot = "/system";
+        }
+        execFile = (char*)alloca(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+        strcpy(execFile, androidRoot);
+        strcat(execFile, kDexOptBin);
+
+        /*
+         * Create arg vector.
+         */
+        int curArg = 0;
+
+        if (kUseValgrind) {
+            /* probably shouldn't ship the hard-coded path */
+            argv[curArg++] = (char*)kValgrinder;
+            argv[curArg++] = "--tool=memcheck";
+            argv[curArg++] = "--leak-check=yes";        // check for leaks too
+            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
+            argv[curArg++] = "--num-callers=16";        // default is 12
+            assert(curArg == kValgrindArgCount);
+        }
+        argv[curArg++] = execFile;
+
+        argv[curArg++] = "--dex";
+
+        sprintf(values[2], "%d", DALVIK_VM_BUILD);
+        argv[curArg++] = values[2];
+
+        sprintf(values[3], "%d", fd);
+        argv[curArg++] = values[3];
+
+        sprintf(values[4], "%d", (int) dexOffset);
+        argv[curArg++] = values[4];
+
+        sprintf(values[5], "%d", (int) dexLength);
+        argv[curArg++] = values[5];
+
+        argv[curArg++] = (char*)fileName;
+
+        sprintf(values[7], "%d", (int) modWhen);
+        argv[curArg++] = values[7];
+
+        sprintf(values[8], "%d", (int) crc);
+        argv[curArg++] = values[8];
+
+        flags = 0;
+        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
+            flags |= DEXOPT_OPT_ENABLED;
+            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
+                flags |= DEXOPT_OPT_ALL;
+        }
+        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
+            flags |= DEXOPT_VERIFY_ENABLED;
+            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
+                flags |= DEXOPT_VERIFY_ALL;
+        }
+        if (isBootstrap)
+            flags |= DEXOPT_IS_BOOTSTRAP;
+        if (gDvm.generateRegisterMaps)
+            flags |= DEXOPT_GEN_REGISTER_MAPS;
+        sprintf(values[9], "%d", flags);
+        argv[curArg++] = values[9];
+
+        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
+               ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
+
+        ClassPathEntry* cpe;
+        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+            argv[curArg++] = cpe->fileName;
+        }
+        assert(curArg == argc);
+
+        argv[curArg] = NULL;
+
+        if (kUseValgrind)
+            execv(kValgrinder, const_cast<char**>(argv));
+        else
+            execv(execFile, const_cast<char**>(argv));
+
+        ALOGE("execv '%s'%s failed: %s", execFile,
+            kUseValgrind ? " [valgrind]" : "", strerror(errno));
+        exit(1);
+    } else {
+        ALOGV("DexOpt: waiting for verify+opt, pid=%d", (int) pid);
+        int status;
+        pid_t gotPid;
+
+        /*
+         * Wait for the optimization process to finish.  We go into VMWAIT
+         * mode here so GC suspension won't have to wait for us.
+         */
+        ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+        while (true) {
+            gotPid = waitpid(pid, &status, 0);
+            if (gotPid == -1 && errno == EINTR) {
+                ALOGD("waitpid interrupted, retrying");
+            } else {
+                break;
+            }
+        }
+        dvmChangeStatus(NULL, oldStatus);
+        if (gotPid != pid) {
+            ALOGE("waitpid failed: wanted %d, got %d: %s",
+                (int) pid, (int) gotPid, strerror(errno));
+            return false;
+        }
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+            ALOGD("DexOpt: --- END '%s' (success) ---", lastPart);
+            return true;
+        } else {
+            ALOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed",
+                lastPart, status);
+            return false;
+        }
+    }
+}
+
+/*
+ * Do the actual optimization.  This is executed in the dexopt process.
+ *
+ * For best use of disk/memory, we want to extract once and perform
+ * optimizations in place.  If the file has to expand or contract
+ * to match local structure padding/alignment expectations, we want
+ * to do the rewrite as part of the extract, rather than extracting
+ * into a temp file and slurping it back out.  (The structure alignment
+ * is currently correct for all platforms, and this isn't expected to
+ * change, so we should be okay with having it already extracted.)
+ *
+ * Returns "true" on success.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+    DexClassLookup* pClassLookup = NULL;
+    RegisterMapBuilder* pRegMapBuilder = NULL;
+
+    assert(gDvm.optimizing);
+
+    ALOGV("Continuing optimization (%s, isb=%d)", fileName, isBootstrap);
+
+    assert(dexOffset >= 0);
+
+    /* quick test so we don't blow up on empty file */
+    if (dexLength < (int) sizeof(DexHeader)) {
+        ALOGE("too small to be DEX");
+        return false;
+    }
+    if (dexOffset < (int) sizeof(DexOptHeader)) {
+        ALOGE("not enough room for opt header");
+        return false;
+    }
+
+    bool result = false;
+
+    /*
+     * Drop this into a global so we don't have to pass it around.  We could
+     * also add a field to DexFile, but since it only pertains to DEX
+     * creation that probably doesn't make sense.
+     */
+    gDvm.optimizingBootstrapClass = isBootstrap;
+
+    {
+        /*
+         * Map the entire file (so we don't have to worry about page
+         * alignment).  The expectation is that the output file contains
+         * our DEX data plus room for a small header.
+         */
+        bool success;
+        void* mapAddr;
+        mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
+                    MAP_SHARED, fd, 0);
+        if (mapAddr == MAP_FAILED) {
+            ALOGE("unable to mmap DEX cache: %s", strerror(errno));
+            goto bail;
+        }
+
+        bool doVerify, doOpt;
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
+            doVerify = false;
+        } else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE) {
+            doVerify = !gDvm.optimizingBootstrapClass;
+        } else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/ {
+            doVerify = true;
+        }
+
+        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE) {
+            doOpt = false;
+        } else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                   gDvm.dexOptMode == OPTIMIZE_MODE_FULL) {
+            doOpt = doVerify;
+        } else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/ {
+            doOpt = true;
+        }
+
+        /*
+         * Rewrite the file.  Byte reordering, structure realigning,
+         * class verification, and bytecode optimization are all performed
+         * here.
+         *
+         * In theory the file could change size and bits could shift around.
+         * In practice this would be annoying to deal with, so the file
+         * layout is designed so that it can always be rewritten in place.
+         *
+         * This creates the class lookup table as part of doing the processing.
+         */
+        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+                    doVerify, doOpt, &pClassLookup, NULL);
+
+        if (success) {
+            DvmDex* pDvmDex = NULL;
+            u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+                ALOGE("Unable to create DexFile");
+                success = false;
+            } else {
+                /*
+                 * If configured to do so, generate register map output
+                 * for all verified classes.  The register maps were
+                 * generated during verification, and will now be serialized.
+                 */
+                if (gDvm.generateRegisterMaps) {
+                    pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
+                    if (pRegMapBuilder == NULL) {
+                        ALOGE("Failed generating register maps");
+                        success = false;
+                    }
+                }
+
+                DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+                updateChecksum(dexAddr, dexLength, pHeader);
+
+                dvmDexFileFree(pDvmDex);
+            }
+        }
+
+        /* unmap the read-write version, forcing writes to disk */
+        if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
+            ALOGW("msync failed: %s", strerror(errno));
+            // weird, but keep going
+        }
+#if 1
+        /*
+         * This causes clean shutdown to fail, because we have loaded classes
+         * that point into it.  For the optimizer this isn't a problem,
+         * because it's more efficient for the process to simply exit.
+         * Exclude this code when doing clean shutdown for valgrind.
+         */
+        if (munmap(mapAddr, dexOffset + dexLength) != 0) {
+            ALOGE("munmap failed: %s", strerror(errno));
+            goto bail;
+        }
+#endif
+
+        if (!success)
+            goto bail;
+    }
+
+    /* get start offset, and adjust deps start for 64-bit alignment */
+    off_t depsOffset, optOffset, endOffset, adjOffset;
+    int depsLength, optLength;
+    u4 optChecksum;
+
+    depsOffset = lseek(fd, 0, SEEK_END);
+    if (depsOffset < 0) {
+        ALOGE("lseek to EOF failed: %s", strerror(errno));
+        goto bail;
+    }
+    adjOffset = (depsOffset + 7) & ~(0x07);
+    if (adjOffset != depsOffset) {
+        ALOGV("Adjusting deps start from %d to %d",
+            (int) depsOffset, (int) adjOffset);
+        depsOffset = adjOffset;
+        lseek(fd, depsOffset, SEEK_SET);
+    }
+
+    /*
+     * Append the dependency list.
+     */
+    if (writeDependencies(fd, modWhen, crc) != 0) {
+        ALOGW("Failed writing dependencies");
+        goto bail;
+    }
+
+    /* compute deps length, then adjust opt start for 64-bit alignment */
+    optOffset = lseek(fd, 0, SEEK_END);
+    depsLength = optOffset - depsOffset;
+
+    adjOffset = (optOffset + 7) & ~(0x07);
+    if (adjOffset != optOffset) {
+        ALOGV("Adjusting opt start from %d to %d",
+            (int) optOffset, (int) adjOffset);
+        optOffset = adjOffset;
+        lseek(fd, optOffset, SEEK_SET);
+    }
+
+    /*
+     * Append any optimized pre-computed data structures.
+     */
+    if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
+        ALOGW("Failed writing opt data");
+        goto bail;
+    }
+
+    endOffset = lseek(fd, 0, SEEK_END);
+    optLength = endOffset - optOffset;
+
+    /* compute checksum from start of deps to end of opt area */
+    if (!computeFileChecksum(fd, depsOffset,
+            (optOffset+optLength) - depsOffset, &optChecksum))
+    {
+        goto bail;
+    }
+
+    /*
+     * Output the "opt" header with all values filled in and a correct
+     * magic number.
+     */
+    DexOptHeader optHdr;
+    memset(&optHdr, 0xff, sizeof(optHdr));
+    memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
+    memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
+    optHdr.dexOffset = (u4) dexOffset;
+    optHdr.dexLength = (u4) dexLength;
+    optHdr.depsOffset = (u4) depsOffset;
+    optHdr.depsLength = (u4) depsLength;
+    optHdr.optOffset = (u4) optOffset;
+    optHdr.optLength = (u4) optLength;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+    optHdr.flags = DEX_OPT_FLAG_BIG;
+#else
+    optHdr.flags = 0;
+#endif
+    optHdr.checksum = optChecksum;
+
+    fsync(fd);      /* ensure previous writes go before header is written */
+
+    lseek(fd, 0, SEEK_SET);
+    if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)
+        goto bail;
+
+    ALOGV("Successfully wrote DEX header");
+    result = true;
+
+    //dvmRegisterMapDumpStats();
+
+bail:
+    dvmFreeRegisterMapBuilder(pRegMapBuilder);
+    free(pClassLookup);
+    return result;
+}
+
+/*
+ * Prepare an in-memory DEX file.
+ *
+ * The data was presented to the VM as a byte array rather than a file.
+ * We want to do the same basic set of operations, but we can just leave
+ * them in memory instead of writing them out to a cached optimized DEX file.
+ */
+bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex)
+{
+    DexClassLookup* pClassLookup = NULL;
+
+    /*
+     * Byte-swap, realign, verify basic DEX file structure.
+     *
+     * We could load + verify + optimize here as well, but that's probably
+     * not desirable.
+     *
+     * (The bulk-verification code is currently only setting the DEX
+     * file's "verified" flag, not updating the ClassObject.  This would
+     * also need to be changed, or we will try to verify the class twice,
+     * and possibly reject it when optimized opcodes are encountered.)
+     */
+    if (!rewriteDex(addr, len, false, false, &pClassLookup, ppDvmDex)) {
+        return false;
+    }
+
+    (*ppDvmDex)->pDexFile->pClassLookup = pClassLookup;
+
+    return true;
+}
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * If this is called from a short-lived child process (dexopt), we can
+ * go nutty with loading classes and allocating memory.  When it's
+ * called to prepare classes provided in a byte array, we may want to
+ * be more conservative.
+ *
+ * If "ppClassLookup" is non-NULL, a pointer to a newly-allocated
+ * DexClassLookup will be returned on success.
+ *
+ * If "ppDvmDex" is non-NULL, a newly-allocated DvmDex struct will be
+ * returned on success.
+ */
+static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
+    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
+{
+    DexClassLookup* pClassLookup = NULL;
+    u8 prepWhen, loadWhen, verifyOptWhen;
+    DvmDex* pDvmDex = NULL;
+    bool result = false;
+    const char* msgStr = "???";
+
+    /* if the DEX is in the wrong byte order, swap it now */
+    if (dexSwapAndVerify(addr, len) != 0)
+        goto bail;
+
+    /*
+     * Now that the DEX file can be read directly, create a DexFile struct
+     * for it.
+     */
+    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+        ALOGE("Unable to create DexFile");
+        goto bail;
+    }
+
+    /*
+     * Create the class lookup table.  This will eventually be appended
+     * to the end of the .odex.
+     *
+     * We create a temporary link from the DexFile for the benefit of
+     * class loading, below.
+     */
+    pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+    if (pClassLookup == NULL)
+        goto bail;
+    pDvmDex->pDexFile->pClassLookup = pClassLookup;
+
+    /*
+     * If we're not going to attempt to verify or optimize the classes,
+     * there's no value in loading them, so bail out early.
+     */
+    if (!doVerify && !doOpt) {
+        result = true;
+        goto bail;
+    }
+
+    prepWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Load all classes found in this DEX file.  If they fail to load for
+     * some reason, they won't get verified (which is as it should be).
+     */
+    if (!loadAllClasses(pDvmDex))
+        goto bail;
+    loadWhen = dvmGetRelativeTimeUsec();
+
+    /*
+     * Create a data structure for use by the bytecode optimizer.
+     * We need to look up methods in a few classes, so this may cause
+     * a bit of class loading.  We usually do this during VM init, but
+     * for dexopt on core.jar the order of operations gets a bit tricky,
+     * so we defer it to here.
+     */
+    if (!dvmCreateInlineSubsTable())
+        goto bail;
+
+    /*
+     * Verify and optimize all classes in the DEX file (command-line
+     * options permitting).
+     *
+     * This is best-effort, so there's really no way for dexopt to
+     * fail at this point.
+     */
+    verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
+    verifyOptWhen = dvmGetRelativeTimeUsec();
+
+    if (doVerify && doOpt)
+        msgStr = "verify+opt";
+    else if (doVerify)
+        msgStr = "verify";
+    else if (doOpt)
+        msgStr = "opt";
+    ALOGD("DexOpt: load %dms, %s %dms, %d bytes",
+        (int) (loadWhen - prepWhen) / 1000,
+        msgStr,
+        (int) (verifyOptWhen - loadWhen) / 1000,
+        gDvm.pBootLoaderAlloc->curOffset);
+
+    result = true;
+
+bail:
+    /*
+     * On success, return the pieces that the caller asked for.
+     */
+
+    if (pDvmDex != NULL) {
+        /* break link between the two */
+        pDvmDex->pDexFile->pClassLookup = NULL;
+    }
+
+    if (ppDvmDex == NULL || !result) {
+        dvmDexFileFree(pDvmDex);
+    } else {
+        *ppDvmDex = pDvmDex;
+    }
+
+    if (ppClassLookup == NULL || !result) {
+        free(pClassLookup);
+    } else {
+        *ppClassLookup = pClassLookup;
+    }
+
+    return result;
+}
+
+/*
+ * Try to load all classes in the specified DEX.  If they have some sort
+ * of broken dependency, e.g. their superclass lives in a different DEX
+ * that wasn't previously loaded into the bootstrap class path, loading
+ * will fail.  This is the desired behavior.
+ *
+ * We have no notion of class loader at this point, so we load all of
+ * the classes with the bootstrap class loader.  It turns out this has
+ * exactly the behavior we want, and has no ill side effects because we're
+ * running in a separate process and anything we load here will be forgotten.
+ *
+ * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
+ * This works because we only call here as part of optimization / pre-verify,
+ * not during verification as part of loading a class into a running VM.
+ *
+ * This returns "false" if the world is too screwed up to do anything
+ * useful at all.
+ */
+static bool loadAllClasses(DvmDex* pDvmDex)
+{
+    u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
+    u4 idx;
+    int loaded = 0;
+
+    ALOGV("DexOpt: +++ trying to load %d classes", count);
+
+    dvmSetBootPathExtraDex(pDvmDex);
+
+    /*
+     * At this point, it is safe -- and necessary! -- to look up the
+     * VM's required classes and members, even when what we are in the
+     * process of processing is the core library that defines these
+     * classes itself. (The reason it is necessary is that in the act
+     * of initializing the class Class, below, the system will end up
+     * referring to many of the class references that got set up by
+     * this call.)
+     */
+    if (!dvmFindRequiredClassesAndMembers()) {
+        return false;
+    }
+
+    /*
+     * We have some circularity issues with Class and Object that are
+     * most easily avoided by ensuring that Object is never the first
+     * thing we try to find-and-initialize. The call to
+     * dvmFindSystemClass() here takes care of that situation. (We
+     * only need to do this when loading classes from the DEX file
+     * that contains Object, and only when Object comes first in the
+     * list, but it costs very little to do it in all cases.)
+     */
+    if (!dvmInitClass(gDvm.classJavaLangClass)) {
+        ALOGE("ERROR: failed to initialize the class Class!");
+        return false;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* newClass;
+
+        pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
+        classDescriptor =
+            dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
+
+        ALOGV("+++  loading '%s'", classDescriptor);
+        //newClass = dvmDefineClass(pDexFile, classDescriptor,
+        //        NULL);
+        newClass = dvmFindSystemClassNoInit(classDescriptor);
+        if (newClass == NULL) {
+            ALOGV("DexOpt: failed loading '%s'", classDescriptor);
+            dvmClearOptException(dvmThreadSelf());
+        } else if (newClass->pDvmDex != pDvmDex) {
+            /*
+             * We don't load the new one, and we tag the first one found
+             * with the "multiple def" flag so the resolver doesn't try
+             * to make it available.
+             */
+            ALOGD("DexOpt: '%s' has an earlier definition; blocking out",
+                classDescriptor);
+            SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+        } else {
+            loaded++;
+        }
+    }
+    ALOGV("DexOpt: +++ successfully loaded %d classes", loaded);
+
+    dvmSetBootPathExtraDex(NULL);
+    return true;
+}
+
+/*
+ * Verify and/or optimize all classes that were successfully loaded from
+ * this DEX file.
+ */
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+    bool doOpt)
+{
+    u4 count = pDexFile->pHeader->classDefsSize;
+    u4 idx;
+
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* clazz;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        /* all classes are loaded into the bootstrap class loader */
+        clazz = dvmLookupClass(classDescriptor, NULL, false);
+        if (clazz != NULL) {
+            verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt);
+
+        } else {
+            // TODO: log when in verbose mode
+            ALOGV("DexOpt: not optimizing unavailable class '%s'",
+                classDescriptor);
+        }
+    }
+
+#ifdef VERIFIER_STATS
+    ALOGI("Verifier stats:");
+    ALOGI(" methods examined        : %u", gDvm.verifierStats.methodsExamined);
+    ALOGI(" monitor-enter methods   : %u", gDvm.verifierStats.monEnterMethods);
+    ALOGI(" instructions examined   : %u", gDvm.verifierStats.instrsExamined);
+    ALOGI(" instructions re-examined: %u", gDvm.verifierStats.instrsReexamined);
+    ALOGI(" copying of register sets: %u", gDvm.verifierStats.copyRegCount);
+    ALOGI(" merging of register sets: %u", gDvm.verifierStats.mergeRegCount);
+    ALOGI(" ...that caused changes  : %u", gDvm.verifierStats.mergeRegChanged);
+    ALOGI(" uninit searches         : %u", gDvm.verifierStats.uninitSearches);
+    ALOGI(" max memory required     : %u", gDvm.verifierStats.biggestAlloc);
+#endif
+}
+
+/*
+ * Verify and/or optimize a specific class.
+ */
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+    const DexClassDef* pClassDef, bool doVerify, bool doOpt)
+{
+    const char* classDescriptor;
+    bool verified = false;
+
+    if (clazz->pDvmDex->pDexFile != pDexFile) {
+        /*
+         * The current DEX file defined a class that is also present in the
+         * bootstrap class path.  The class loader favored the bootstrap
+         * version, which means that we have a pointer to a class that is
+         * (a) not the one we want to examine, and (b) mapped read-only,
+         * so we will seg fault if we try to rewrite instructions inside it.
+         */
+        ALOGD("DexOpt: not verifying/optimizing '%s': multiple definitions",
+            clazz->descriptor);
+        return;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+    /*
+     * First, try to verify it.
+     */
+    if (doVerify) {
+        if (dvmVerifyClass(clazz)) {
+            /*
+             * Set the "is preverified" flag in the DexClassDef.  We
+             * do it here, rather than in the ClassObject structure,
+             * because the DexClassDef is part of the odex file.
+             */
+            assert((clazz->accessFlags & JAVA_FLAGS_MASK) ==
+                pClassDef->accessFlags);
+            ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISPREVERIFIED;
+            verified = true;
+        } else {
+            // TODO: log when in verbose mode
+            ALOGV("DexOpt: '%s' failed verification", classDescriptor);
+        }
+    }
+
+    if (doOpt) {
+        bool needVerify = (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED ||
+                           gDvm.dexOptMode == OPTIMIZE_MODE_FULL);
+        if (!verified && needVerify) {
+            ALOGV("DexOpt: not optimizing '%s': not verified",
+                classDescriptor);
+        } else {
+            dvmOptimizeClass(clazz, false);
+
+            /* set the flag whether or not we actually changed anything */
+            ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED;
+        }
+    }
+}
+
+
+/*
+ * Get the cache file name from a ClassPathEntry.
+ */
+static const char* getCacheFileName(const ClassPathEntry* cpe)
+{
+    switch (cpe->kind) {
+    case kCpeJar:
+        return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
+    case kCpeDex:
+        return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
+    default:
+        ALOGE("DexOpt: unexpected cpe kind %d", cpe->kind);
+        dvmAbort();
+        return NULL;
+    }
+}
+
+/*
+ * Get the SHA-1 signature.
+ */
+static const u1* getSignature(const ClassPathEntry* cpe)
+{
+    DvmDex* pDvmDex;
+
+    switch (cpe->kind) {
+    case kCpeJar:
+        pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
+        break;
+    case kCpeDex:
+        pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
+        break;
+    default:
+        ALOGE("unexpected cpe kind %d", cpe->kind);
+        dvmAbort();
+        pDvmDex = NULL;         // make gcc happy
+    }
+
+    assert(pDvmDex != NULL);
+    return pDvmDex->pDexFile->pHeader->signature;
+}
+
+
+/*
+ * Dependency layout:
+ *  4b  Source file modification time, in seconds since 1970 UTC
+ *  4b  CRC-32 from Zip entry, or Adler32 from source DEX header
+ *  4b  Dalvik VM build number
+ *  4b  Number of dependency entries that follow
+ *  Dependency entries:
+ *    4b  Name length (including terminating null)
+ *    var Full path of cache entry (null terminated)
+ *    20b SHA-1 signature from source DEX file
+ *
+ * If this changes, update DEX_OPT_MAGIC_VERS.
+ */
+static const size_t kMinDepSize = 4 * 4;
+static const size_t kMaxDepSize = 4 * 4 + 2048;     // sanity check
+
+/*
+ * Read the "opt" header, verify it, then read the dependencies section
+ * and verify that data as well.
+ *
+ * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
+ * match up with what is stored in the header.  If they don't, we reject
+ * the file so that it can be recreated from the updated original.  If
+ * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * "opt" header.
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+    u4 crc, bool expectVerify, bool expectOpt)
+{
+    DexOptHeader optHdr;
+    u1* depData = NULL;
+    const u1* magic;
+    off_t posn;
+    int result = false;
+    ssize_t actual;
+
+    /*
+     * Start at the start.  The "opt" header, when present, will always be
+     * the first thing in the file.
+     */
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        ALOGE("DexOpt: failed to seek to start of file: %s", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read and do trivial verification on the opt header.  The header is
+     * always in host byte order.
+     */
+    actual = read(fd, &optHdr, sizeof(optHdr));
+    if (actual < 0) {
+        ALOGE("DexOpt: failed reading opt header: %s", strerror(errno));
+        goto bail;
+    } else if (actual != sizeof(optHdr)) {
+        ALOGE("DexOpt: failed reading opt header (got %d of %zd)",
+            (int) actual, sizeof(optHdr));
+        goto bail;
+    }
+
+    magic = optHdr.magic;
+    if (memcmp(magic, DEX_MAGIC, 4) == 0) {
+        /* somebody probably pointed us at the wrong file */
+        ALOGD("DexOpt: expected optimized DEX, found unoptimized");
+        goto bail;
+    } else if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+        /* not a DEX file, or previous attempt was interrupted */
+        ALOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)",
+            magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+        ALOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)",
+            magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+    if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+        ALOGW("DexOpt: weird deps length %d, bailing", optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Do the header flags match up with what we want?
+     *
+     * The only thing we really can't handle is incorrect byte ordering.
+     */
+    {
+        const u4 matchMask = DEX_OPT_FLAG_BIG;
+        u4 expectedFlags = 0;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+        expectedFlags |= DEX_OPT_FLAG_BIG;
+#endif
+        if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+            ALOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)",
+                expectedFlags, optHdr.flags, matchMask);
+            goto bail;
+        }
+    }
+
+    posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+    if (posn < 0) {
+        ALOGW("DexOpt: seek to deps failed: %s", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read all of the dependency stuff into memory.
+     */
+    depData = (u1*) malloc(optHdr.depsLength);
+    if (depData == NULL) {
+        ALOGW("DexOpt: unable to allocate %d bytes for deps",
+            optHdr.depsLength);
+        goto bail;
+    }
+    actual = read(fd, depData, optHdr.depsLength);
+    if (actual < 0) {
+        ALOGW("DexOpt: failed reading deps: %s", strerror(errno));
+        goto bail;
+    } else if (actual != (ssize_t) optHdr.depsLength) {
+        ALOGW("DexOpt: failed reading deps: got %d of %d",
+            (int) actual, optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Verify simple items.
+     */
+    const u1* ptr;
+    u4 val;
+
+    ptr = depData;
+    val = read4LE(&ptr);
+    if (sourceAvail && val != modWhen) {
+        ALOGI("DexOpt: source file mod time mismatch (%08x vs %08x)",
+            val, modWhen);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (sourceAvail && val != crc) {
+        ALOGI("DexOpt: source file CRC mismatch (%08x vs %08x)", val, crc);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (val != DALVIK_VM_BUILD) {
+        ALOGD("DexOpt: VM build version mismatch (%d vs %d)",
+            val, DALVIK_VM_BUILD);
+        goto bail;
+    }
+
+    /*
+     * Verify dependencies on other cached DEX files.  It must match
+     * exactly with what is currently defined in the bootclasspath.
+     */
+    ClassPathEntry* cpe;
+    u4 numDeps;
+
+    numDeps = read4LE(&ptr);
+    ALOGV("+++ DexOpt: numDeps = %d", numDeps);
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        const u1* signature = getSignature(cpe);
+        size_t len = strlen(cacheFileName) +1;
+        u4 storedStrLen;
+
+        if (numDeps == 0) {
+            /* more entries in bootclasspath than in deps list */
+            ALOGI("DexOpt: not all deps represented");
+            goto bail;
+        }
+
+        storedStrLen = read4LE(&ptr);
+        if (len != storedStrLen ||
+            strcmp(cacheFileName, (const char*) ptr) != 0)
+        {
+            ALOGI("DexOpt: mismatch dep name: '%s' vs. '%s'",
+                cacheFileName, ptr);
+            goto bail;
+        }
+
+        ptr += storedStrLen;
+
+        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+            ALOGI("DexOpt: mismatch dep signature for '%s'", cacheFileName);
+            goto bail;
+        }
+        ptr += kSHA1DigestLen;
+
+        ALOGV("DexOpt: dep match on '%s'", cacheFileName);
+
+        numDeps--;
+    }
+
+    if (numDeps != 0) {
+        /* more entries in deps list than in classpath */
+        ALOGI("DexOpt: Some deps went away");
+        goto bail;
+    }
+
+    // consumed all data and no more?
+    if (ptr != depData + optHdr.depsLength) {
+        ALOGW("DexOpt: Spurious dep data? %d vs %d",
+            (int) (ptr - depData), optHdr.depsLength);
+        assert(false);
+    }
+
+    result = true;
+
+bail:
+    free(depData);
+    return result;
+}
+
+/*
+ * Write the dependency info to "fd" at the current file position.
+ */
+static int writeDependencies(int fd, u4 modWhen, u4 crc)
+{
+    u1* buf = NULL;
+    int result = -1;
+    ssize_t bufLen;
+    ClassPathEntry* cpe;
+    int numDeps;
+
+    /*
+     * Count up the number of completed entries in the bootclasspath.
+     */
+    numDeps = 0;
+    bufLen = 0;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        ALOGV("+++ DexOpt: found dep '%s'", cacheFileName);
+
+        numDeps++;
+        bufLen += strlen(cacheFileName) +1;
+    }
+
+    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+    buf = (u1*)malloc(bufLen);
+
+    set4LE(buf+0, modWhen);
+    set4LE(buf+4, crc);
+    set4LE(buf+8, DALVIK_VM_BUILD);
+    set4LE(buf+12, numDeps);
+
+    // TODO: do we want to add dvmGetInlineOpsTableLength() here?  Won't
+    // help us if somebody replaces an existing entry, but it'd catch
+    // additions/removals.
+
+    u1* ptr = buf + 4*4;
+    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+        const char* cacheFileName =
+            dvmPathToAbsolutePortion(getCacheFileName(cpe));
+        assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+        const u1* signature = getSignature(cpe);
+        int len = strlen(cacheFileName) +1;
+
+        if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
+            ALOGE("DexOpt: overran buffer");
+            dvmAbort();
+        }
+
+        set4LE(ptr, len);
+        ptr += 4;
+        memcpy(ptr, cacheFileName, len);
+        ptr += len;
+        memcpy(ptr, signature, kSHA1DigestLen);
+        ptr += kSHA1DigestLen;
+    }
+
+    assert(ptr == buf + bufLen);
+
+    result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
+
+    free(buf);
+    return result;
+}
+
+
+/*
+ * Write a block of data in "chunk" format.
+ *
+ * The chunk header fields are always in "native" byte order.  If "size"
+ * is not a multiple of 8 bytes, the data area is padded out.
+ */
+static bool writeChunk(int fd, u4 type, const void* data, size_t size)
+{
+    union {             /* save a syscall by grouping these together */
+        char raw[8];
+        struct {
+            u4 type;
+            u4 size;
+        } ts;
+    } header;
+
+    assert(sizeof(header) == 8);
+
+    ALOGV("Writing chunk, type=%.4s size=%d", (char*) &type, size);
+
+    header.ts.type = type;
+    header.ts.size = (u4) size;
+    if (sysWriteFully(fd, &header, sizeof(header),
+            "DexOpt opt chunk header write") != 0)
+    {
+        return false;
+    }
+
+    if (size > 0) {
+        if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
+            return false;
+    }
+
+    /* if necessary, pad to 64-bit alignment */
+    if ((size & 7) != 0) {
+        int padSize = 8 - (size & 7);
+        ALOGV("size was %d, inserting %d pad bytes", size, padSize);
+        lseek(fd, padSize, SEEK_CUR);
+    }
+
+    assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
+
+    return true;
+}
+
+/*
+ * Write opt data.
+ *
+ * We have different pieces, some of which may be optional.  To make the
+ * most effective use of space, we use a "chunk" format, with a 4-byte
+ * type and a 4-byte length.  We guarantee 64-bit alignment for the data,
+ * so it can be used directly when the file is mapped for reading.
+ */
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,
+    const RegisterMapBuilder* pRegMapBuilder)
+{
+    /* pre-computed class lookup hash table */
+    if (!writeChunk(fd, (u4) kDexChunkClassLookup,
+            pClassLookup, pClassLookup->size))
+    {
+        return false;
+    }
+
+    /* register maps (optional) */
+    if (pRegMapBuilder != NULL) {
+        if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
+                pRegMapBuilder->data, pRegMapBuilder->size))
+        {
+            return false;
+        }
+    }
+
+    /* write the end marker */
+    if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+    unsigned char readBuf[8192];
+    ssize_t actual;
+    uLong adler;
+
+    if (lseek(fd, start, SEEK_SET) != start) {
+        ALOGE("Unable to seek to start of checksum area (%ld): %s",
+            (long) start, strerror(errno));
+        return false;
+    }
+
+    adler = adler32(0L, Z_NULL, 0);
+
+    while (length != 0) {
+        size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+        actual = read(fd, readBuf, wanted);
+        if (actual <= 0) {
+            ALOGE("Read failed (%d) while computing checksum (len=%zu): %s",
+                (int) actual, length, strerror(errno));
+            return false;
+        }
+
+        adler = adler32(adler, readBuf, actual);
+
+        length -= actual;
+    }
+
+    *pSum = adler;
+    return true;
+}
+
+/*
+ * Update the Adler-32 checksum stored in the DEX file.  This covers the
+ * swapped and optimized DEX data, but does not include the opt header
+ * or optimized data.
+ */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
+{
+    /*
+     * Rewrite the checksum.  We leave the SHA-1 signature alone.
+     */
+    uLong adler = adler32(0L, Z_NULL, 0);
+    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+    adler = adler32(adler, addr + nonSum, len - nonSum);
+    pHeader->checksum = adler;
+}
diff --git a/vm/analysis/DexPrepare.h b/vm/analysis/DexPrepare.h
new file mode 100644
index 0000000..297bbc6
--- /dev/null
+++ b/vm/analysis/DexPrepare.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * DEX preparation declarations.
+ */
+#ifndef DALVIK_DEXPREPARE_H_
+#define DALVIK_DEXPREPARE_H_
+
+/*
+ * Global DEX optimizer control.  Determines the circumstances in which we
+ * try to rewrite instructions in the DEX file.
+ *
+ * Optimizing is performed ahead-of-time by dexopt and, in some cases, at
+ * load time by the VM.
+ */
+enum DexOptimizerMode {
+    OPTIMIZE_MODE_UNKNOWN = 0,
+    OPTIMIZE_MODE_NONE,         /* never optimize (except "essential") */
+    OPTIMIZE_MODE_VERIFIED,     /* only optimize verified classes (default) */
+    OPTIMIZE_MODE_ALL,          /* optimize verified & unverified (risky) */
+    OPTIMIZE_MODE_FULL          /* fully opt verified classes at load time */
+};
+
+/* some additional bit flags for dexopt */
+enum DexoptFlags {
+    DEXOPT_OPT_ENABLED       = 1,       /* optimizations enabled? */
+    DEXOPT_OPT_ALL           = 1 << 1,  /* optimize when verify fails? */
+    DEXOPT_VERIFY_ENABLED    = 1 << 2,  /* verification enabled? */
+    DEXOPT_VERIFY_ALL        = 1 << 3,  /* verify bootstrap classes? */
+    DEXOPT_IS_BOOTSTRAP      = 1 << 4,  /* is dex in bootstrap class path? */
+    DEXOPT_GEN_REGISTER_MAPS = 1 << 5,  /* generate register maps during vfy */
+    DEXOPT_UNIPROCESSOR      = 1 << 6,  /* specify uniprocessor target */
+    DEXOPT_SMP               = 1 << 7   /* specify SMP target */
+};
+
+/*
+ * An enumeration of problems that can turn up during verification.
+ */
+enum VerifyError {
+    VERIFY_ERROR_NONE = 0,      /* no error; must be zero */
+    VERIFY_ERROR_GENERIC,       /* VerifyError */
+
+    VERIFY_ERROR_NO_CLASS,      /* NoClassDefFoundError */
+    VERIFY_ERROR_NO_FIELD,      /* NoSuchFieldError */
+    VERIFY_ERROR_NO_METHOD,     /* NoSuchMethodError */
+    VERIFY_ERROR_ACCESS_CLASS,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_FIELD,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
+    VERIFY_ERROR_CLASS_CHANGE,  /* IncompatibleClassChangeError */
+    VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+};
+
+/*
+ * Identifies the type of reference in the instruction that generated the
+ * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method,
+ * field, or class reference).
+ *
+ * This must fit in two bits.
+ */
+enum VerifyErrorRefType {
+    VERIFY_ERROR_REF_CLASS  = 0,
+    VERIFY_ERROR_REF_FIELD  = 1,
+    VERIFY_ERROR_REF_METHOD = 2,
+};
+
+#define kVerifyErrorRefTypeShift 6
+
+#define VERIFY_OK(_failure) ((_failure) == VERIFY_ERROR_NONE)
+
+/*
+ * Given the full path to a DEX or Jar file, and (if appropriate) the name
+ * within the Jar, open the optimized version from the cache.
+ *
+ * If "*pNewFile" is set, a new file has been created with only a stub
+ * "opt" header, and the caller is expected to fill in the blanks.
+ *
+ * Returns the file descriptor, locked and seeked past the "opt" header.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cachedFile,
+    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing);
+
+/*
+ * Unlock the specified file descriptor.  Use in conjunction with
+ * dvmOpenCachedDexFile().
+ *
+ * Returns true on success.
+ */
+bool dvmUnlockCachedDexFile(int fd);
+
+/*
+ * Verify the contents of the "opt" header, and check the DEX file's
+ * dependencies on its source zip (if available).
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+    u4 crc, bool expectVerify, bool expectOpt);
+
+/*
+ * Optimize a DEX file.  The file must start with the "opt" header, followed
+ * by the plain DEX data.  It must be mmap()able.
+ *
+ * "fileName" is only used for debug output.
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLen,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+/*
+ * Continue the optimization process on the other side of a fork/exec.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+/*
+ * Prepare DEX data that is only available to the VM as in-memory data.
+ */
+bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex);
+
+/*
+ * Prep data structures.
+ */
+bool dvmCreateInlineSubsTable(void);
+void dvmFreeInlineSubsTable(void);
+
+#endif  // DALVIK_DEXPREPARE_H_
diff --git a/vm/analysis/DexVerify.cpp b/vm/analysis/DexVerify.cpp
new file mode 100644
index 0000000..c47c301
--- /dev/null
+++ b/vm/analysis/DexVerify.cpp
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik classfile verification.  This file contains the verifier entry
+ * points and the static constraint checks.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "libdex/DexCatch.h"
+
+
+/* fwd */
+static bool verifyMethod(Method* meth);
+static bool verifyInstructions(VerifierData* vdata);
+
+
+/*
+ * Verify a class.
+ *
+ * By the time we get here, the value of gDvm.classVerifyMode should already
+ * have been factored in.  If you want to call into the verifier even
+ * though verification is disabled, that's your business.
+ *
+ * Returns "true" on success.
+ */
+bool dvmVerifyClass(ClassObject* clazz)
+{
+    int i;
+
+    if (dvmIsClassVerified(clazz)) {
+        ALOGD("Ignoring duplicate verify attempt on %s", clazz->descriptor);
+        return true;
+    }
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        if (!verifyMethod(&clazz->directMethods[i])) {
+            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
+            return false;
+        }
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        if (!verifyMethod(&clazz->virtualMethods[i])) {
+            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Compute the width of the instruction at each address in the instruction
+ * stream, and store it in vdata->insnFlags.  Addresses that are in the
+ * middle of an instruction, or that are part of switch table data, are not
+ * touched (so the caller should probably initialize "insnFlags" to zero).
+ *
+ * The "newInstanceCount" and "monitorEnterCount" fields in vdata are
+ * also set.
+ *
+ * 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 computeWidthsAndCountOps(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    size_t insnCount = vdata->insnsSize;
+    const u2* insns = meth->insns;
+    bool result = false;
+    int newInstanceCount = 0;
+    int monitorEnterCount = 0;
+    int i;
+
+    for (i = 0; i < (int) insnCount; /**/) {
+        size_t width = dexGetWidthFromInstruction(insns);
+        if (width == 0) {
+            LOG_VFY_METH(meth, "VFY: invalid instruction (0x%04x)", *insns);
+            goto bail;
+        } else if (width > 65535) {
+            LOG_VFY_METH(meth,
+                "VFY: warning: unusually large instr width (%d)", width);
+        }
+
+        Opcode opcode = dexOpcodeFromCodeUnit(*insns);
+        if (opcode == OP_NEW_INSTANCE)
+            newInstanceCount++;
+        if (opcode == OP_MONITOR_ENTER)
+            monitorEnterCount++;
+
+        insnFlags[i] |= width;
+        i += width;
+        insns += width;
+    }
+    if (i != (int) vdata->insnsSize) {
+        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)",
+            i, dvmGetMethodInsnsSize(meth));
+        goto bail;
+    }
+
+    result = true;
+    vdata->newInstanceCount = newInstanceCount;
+    vdata->monitorEnterCount = monitorEnterCount;
+
+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 idx;
+
+    if (triesSize == 0) {
+        return true;
+    }
+
+    pTries = dexGetTries(pCode);
+
+    for (idx = 0; idx < triesSize; idx++) {
+        const DexTry* pTry = &pTries[idx];
+        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)",
+                start, end, insnsSize);
+            return false;
+        }
+
+        if (dvmInsnGetWidth(insnFlags, start) == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: 'try' block starts inside an instruction (%d)",
+                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. */
+    u4 handlersSize = dexGetHandlersSize(pCode);
+    u4 offset = dexGetFirstHandlerOffset(pCode);
+    for (idx = 0; idx < handlersSize; idx++) {
+        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)",
+                    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:
+ *  (1) Walk through all code units, determining instruction locations,
+ *      widths, and other characteristics.
+ *  (2) Walk through all code units, performing static checks on
+ *      operands.
+ *  (3) Iterate through the method, checking type safety and looking
+ *      for code flow problems.
+ *
+ * Some checks may be bypassed depending on the verification mode.  We can't
+ * turn this stuff off completely if we want to do "exact" GC.
+ *
+ * TODO: cite source?
+ * Confirmed here:
+ * - code array must not be empty
+ * - (N/A) code_length must be less than 65536
+ * Confirmed by computeWidthsAndCountOps():
+ * - 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)
+ */
+static bool verifyMethod(Method* meth)
+{
+    bool result = false;
+
+    /*
+     * Verifier state blob.  Various values will be cached here so we
+     * can avoid expensive lookups and pass fewer arguments around.
+     */
+    VerifierData vdata;
+#if 1   // ndef NDEBUG
+    memset(&vdata, 0x99, sizeof(vdata));
+#endif
+
+    vdata.method = meth;
+    vdata.insnsSize = dvmGetMethodInsnsSize(meth);
+    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
+     * exit successfully.  Note: for native methods, meth->insns gets set
+     * to a native function pointer on first call, so don't use that as
+     * an indicator.
+     */
+    if (vdata.insnsSize == 0) {
+        if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: zero-length code in concrete non-native method");
+            goto bail;
+        }
+
+        goto success;
+    }
+
+    /*
+     * Sanity-check the register counts.  ins + locals = registers, so make
+     * sure that ins <= registers.
+     */
+    if (meth->insSize > meth->registersSize) {
+        LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)",
+            meth->insSize, meth->registersSize);
+        goto bail;
+    }
+
+    /*
+     * Allocate and populate an array to hold instruction data.
+     *
+     * TODO: Consider keeping a reusable pre-allocated array sitting
+     * around for smaller methods.
+     */
+    vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags));
+    if (vdata.insnFlags == NULL)
+        goto bail;
+
+    /*
+     * Compute the width of each instruction and store the result in insnFlags.
+     * Count up the #of occurrences of certain opcodes while we're at it.
+     */
+    if (!computeWidthsAndCountOps(&vdata))
+        goto bail;
+
+    /*
+     * Allocate a map to hold the classes of uninitialized instances.
+     */
+    vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags,
+        vdata.newInstanceCount);
+    if (vdata.uninitMap == NULL)
+        goto bail;
+
+    /*
+     * 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.  Also sets the "branch
+     * target" flags.
+     */
+    if (!verifyInstructions(&vdata))
+        goto bail;
+
+    /*
+     * Do code-flow analysis.
+     *
+     * 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)) {
+        //ALOGD("+++ %s failed code flow", meth->name);
+        goto bail;
+    }
+
+success:
+    result = true;
+
+bail:
+    dvmFreeVfyBasicBlocks(&vdata);
+    dvmFreeUninitInstanceMap(vdata.uninitMap);
+    free(vdata.insnFlags);
+    return result;
+}
+
+
+/*
+ * Verify an array data table.  "curOffset" is the offset of the
+ * fill-array-data instruction.
+ */
+static bool checkArrayData(const Method* meth, u4 curOffset)
+{
+    const u4 insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + curOffset;
+    const u2* arrayData;
+    u4 valueCount, valueWidth, tableSize;
+    s4 offsetToArrayData;
+
+    assert(curOffset < insnCount);
+
+    /* make sure the start of the array data table is in range */
+    offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
+    if ((s4)curOffset + offsetToArrayData < 0 ||
+        curOffset + offsetToArrayData + 2 >= insnCount)
+    {
+        LOG_VFY("VFY: invalid array data start: at %d, data offset %d, "
+                "count %d",
+            curOffset, offsetToArrayData, insnCount);
+        return false;
+    }
+
+    /* offset to array data table is a relative branch-style offset */
+    arrayData = insns + offsetToArrayData;
+
+    /* make sure the table is 32-bit aligned */
+    if ((((u4) arrayData) & 0x03) != 0) {
+        LOG_VFY("VFY: unaligned array data table: at %d, data offset %d",
+            curOffset, offsetToArrayData);
+        return false;
+    }
+
+    valueWidth = arrayData[1];
+    valueCount = *(u4*)(&arrayData[2]);
+
+    tableSize = 4 + (valueWidth * valueCount + 1) / 2;
+
+    /* make sure the end of the switch is in range */
+    if (curOffset + offsetToArrayData + tableSize > insnCount) {
+        LOG_VFY("VFY: invalid array data end: at %d, data offset %d, end %d, "
+                "count %d",
+            curOffset, offsetToArrayData,
+            curOffset + offsetToArrayData + tableSize, insnCount);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on a "new-instance" instruction.  Specifically,
+ * make sure the class reference isn't for an array class.
+ *
+ * We don't need the actual class, just a pointer to the class name.
+ */
+static bool checkNewInstance(const DvmDex* pDvmDex, u4 idx)
+{
+    const char* classDescriptor;
+
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+    if (classDescriptor[0] != 'L') {
+        LOG_VFY("VFY: can't call new-instance on type '%s'",
+            classDescriptor);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on a "new-array" instruction.  Specifically, make
+ * sure they aren't creating an array of arrays that causes the number of
+ * dimensions to exceed 255.
+ */
+static bool checkNewArray(const DvmDex* pDvmDex, u4 idx)
+{
+    const char* classDescriptor;
+
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+
+    int bracketCount = 0;
+    const char* cp = classDescriptor;
+    while (*cp++ == '[')
+        bracketCount++;
+
+    if (bracketCount == 0) {
+        /* The given class must be an array type. */
+        LOG_VFY("VFY: can't new-array class '%s' (not an array)",
+            classDescriptor);
+        return false;
+    } else if (bracketCount > 255) {
+        /* It is illegal to create an array of more than 255 dimensions. */
+        LOG_VFY("VFY: can't new-array class '%s' (exceeds limit)",
+            classDescriptor);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on an instruction that takes a class constant.
+ * Ensure that the class index is in the valid range.
+ */
+static bool checkTypeIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY("VFY: bad type index %d (max %d)",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Perform static checks on a field get or set instruction.  All we do
+ * here is ensure that the field index is in the valid range.
+ */
+static bool checkFieldIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->fieldIdsSize) {
+        LOG_VFY("VFY: bad field index %d (max %d)",
+            idx, pDvmDex->pHeader->fieldIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Perform static checks on a method invocation instruction.  All we do
+ * here is ensure that the method index is in the valid range.
+ */
+static bool checkMethodIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->methodIdsSize) {
+        LOG_VFY("VFY: bad method index %d (max %d)",
+            idx, pDvmDex->pHeader->methodIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the string index is in the valid range.
+ */
+static bool checkStringIndex(const DvmDex* pDvmDex, u4 idx)
+{
+    if (idx >= pDvmDex->pHeader->stringIdsSize) {
+        LOG_VFY("VFY: bad string index %d (max %d)",
+            idx, pDvmDex->pHeader->stringIdsSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the register index is valid for this method.
+ */
+static bool checkRegisterIndex(const Method* meth, u4 idx)
+{
+    if (idx >= meth->registersSize) {
+        LOG_VFY("VFY: register index out of range (%d >= %d)",
+            idx, meth->registersSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Ensure that the wide register index is valid for this method.
+ */
+static bool checkWideRegisterIndex(const Method* meth, u4 idx)
+{
+    if (idx+1 >= meth->registersSize) {
+        LOG_VFY("VFY: wide register index out of range (%d+1 >= %d)",
+            idx, meth->registersSize);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Check the register indices used in a "vararg" instruction, such as
+ * invoke-virtual or filled-new-array.
+ *
+ * vA holds word count (0-5), args[] have values.
+ *
+ * There are some tests we don't do here, e.g. we don't try to verify
+ * that invoking a method that takes a double is done with consecutive
+ * registers.  This requires parsing the target method signature, which
+ * we will be doing later on during the code flow analysis.
+ */
+static bool checkVarargRegs(const Method* meth,
+    const DecodedInstruction* pDecInsn)
+{
+    u2 registersSize = meth->registersSize;
+    unsigned int idx;
+
+    if (pDecInsn->vA > 5) {
+        LOG_VFY("VFY: invalid arg count (%d) in non-range invoke)",
+            pDecInsn->vA);
+        return false;
+    }
+
+    for (idx = 0; idx < pDecInsn->vA; idx++) {
+        if (pDecInsn->arg[idx] > registersSize) {
+            LOG_VFY("VFY: invalid reg index (%d) in non-range invoke (> %d)",
+                pDecInsn->arg[idx], registersSize);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Check the register indices used in a "vararg/range" instruction, such as
+ * invoke-virtual/range or filled-new-array/range.
+ *
+ * vA holds word count, vC holds index of first reg.
+ */
+static bool checkVarargRangeRegs(const Method* meth,
+    const DecodedInstruction* pDecInsn)
+{
+    u2 registersSize = meth->registersSize;
+
+    /*
+     * vA/vC are unsigned 8-bit/16-bit quantities for /range instructions,
+     * so there's no risk of integer overflow when adding them here.
+     */
+    if (pDecInsn->vA + pDecInsn->vC > registersSize) {
+        LOG_VFY("VFY: invalid reg index %d+%d in range invoke (> %d)",
+            pDecInsn->vA, pDecInsn->vC, registersSize);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Verify a switch table.  "curOffset" is the offset of the switch
+ * instruction.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+static bool checkSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+    u4 curOffset)
+{
+    const u4 insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + curOffset;
+    const u2* switchInsns;
+    u2 expectedSignature;
+    u4 switchCount, tableSize;
+    s4 offsetToSwitch, offsetToKeys, offsetToTargets;
+    s4 offset, absOffset;
+    u4 targ;
+
+    assert(curOffset < insnCount);
+
+    /* make sure the start of the switch is in range */
+    offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
+    if ((s4) curOffset + offsetToSwitch < 0 ||
+        curOffset + offsetToSwitch + 2 >= insnCount)
+    {
+        LOG_VFY("VFY: invalid switch start: at %d, switch offset %d, "
+                "count %d",
+            curOffset, offsetToSwitch, insnCount);
+        return false;
+    }
+
+    /* offset to switch table is a relative branch-style offset */
+    switchInsns = insns + offsetToSwitch;
+
+    /* make sure the table is 32-bit aligned */
+    if ((((u4) switchInsns) & 0x03) != 0) {
+        LOG_VFY("VFY: unaligned switch table: at %d, switch offset %d",
+            curOffset, offsetToSwitch);
+        return false;
+    }
+
+    switchCount = switchInsns[1];
+
+    if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+        /* 0=sig, 1=count, 2/3=firstKey */
+        offsetToTargets = 4;
+        offsetToKeys = -1;
+        expectedSignature = kPackedSwitchSignature;
+    } else {
+        /* 0=sig, 1=count, 2..count*2 = keys */
+        offsetToKeys = 2;
+        offsetToTargets = 2 + 2*switchCount;
+        expectedSignature = kSparseSwitchSignature;
+    }
+    tableSize = offsetToTargets + switchCount*2;
+
+    if (switchInsns[0] != expectedSignature) {
+        LOG_VFY("VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)",
+            switchInsns[0], expectedSignature);
+        return false;
+    }
+
+    /* make sure the end of the switch is in range */
+    if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
+        LOG_VFY("VFY: invalid switch end: at %d, switch offset %d, end %d, "
+                "count %d",
+            curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
+            insnCount);
+        return false;
+    }
+
+    /* for a sparse switch, verify the keys are in ascending order */
+    if (offsetToKeys > 0 && switchCount > 1) {
+        s4 lastKey;
+
+        lastKey = switchInsns[offsetToKeys] |
+                  (switchInsns[offsetToKeys+1] << 16);
+        for (targ = 1; targ < switchCount; targ++) {
+            s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
+                    (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
+            if (key <= lastKey) {
+                LOG_VFY("VFY: invalid packed switch: last key=%d, this=%d",
+                    lastKey, key);
+                return false;
+            }
+
+            lastKey = key;
+        }
+    }
+
+    /* verify each switch target */
+    for (targ = 0; targ < switchCount; targ++) {
+        offset = (s4) switchInsns[offsetToTargets + targ*2] |
+                (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
+        absOffset = curOffset + offset;
+
+        if (absOffset < 0 || absOffset >= (s4)insnCount ||
+            !dvmInsnIsOpcode(insnFlags, absOffset))
+        {
+            LOG_VFY("VFY: invalid switch target %d (-> %#x) at %#x[%d]",
+                offset, absOffset, curOffset, targ);
+            return false;
+        }
+        dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+    }
+
+    return true;
+}
+
+/*
+ * Verify that the target of a branch instruction is valid.
+ *
+ * We don't expect code to jump directly into an exception handler, but
+ * it's valid to do so as long as the target isn't a "move-exception"
+ * instruction.  We verify that in a later stage.
+ *
+ * The VM spec doesn't forbid an instruction from branching to itself,
+ * but the Dalvik spec declares that only certain instructions can do so.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+static bool checkBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, bool selfOkay)
+{
+    const int insnCount = dvmGetMethodInsnsSize(meth);
+    s4 offset, absOffset;
+    bool isConditional;
+
+    if (!dvmGetBranchOffset(meth, insnFlags, curOffset, &offset,
+            &isConditional))
+        return false;
+
+    if (!selfOkay && offset == 0) {
+        LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at %#x",
+            curOffset);
+        return false;
+    }
+
+    /*
+     * Check for 32-bit overflow.  This isn't strictly necessary if we can
+     * depend on the VM to have identical "wrap-around" behavior, but
+     * it's unwise to depend on that.
+     */
+    if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) {
+        LOG_VFY_METH(meth, "VFY: branch target overflow %#x +%d",
+            curOffset, offset);
+        return false;
+    }
+    absOffset = curOffset + offset;
+    if (absOffset < 0 || absOffset >= insnCount ||
+        !dvmInsnIsOpcode(insnFlags, absOffset))
+    {
+        LOG_VFY_METH(meth,
+            "VFY: invalid branch target %d (-> %#x) at %#x",
+            offset, absOffset, curOffset);
+        return false;
+    }
+    dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+
+    return true;
+}
+
+
+/*
+ * Perform static verification on instructions.
+ *
+ * As a side effect, this sets the "branch target" flags in InsnFlags.
+ *
+ * "(CF)" items are handled during code-flow analysis.
+ *
+ * v3 4.10.1
+ * - target of each jump and branch instruction must be valid
+ * - targets of switch statements must be valid
+ * - operands referencing constant pool entries must be valid
+ * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+ * - (new) verify operands of "quick" field ops
+ * - (CF) operands of method invocation instructions must be valid
+ * - (new) verify operands of "quick" method invoke ops
+ * - (CF) only invoke-direct can call a method starting with '<'
+ * - (CF) <clinit> must never be called explicitly
+ * - operands of instanceof, checkcast, new (and variants) must be valid
+ * - new-array[-type] limited to 255 dimensions
+ * - can't use "new" on an array class
+ * - (?) limit dimensions in multi-array creation
+ * - local variable load/store register values must be in valid range
+ *
+ * v3 4.11.1.2
+ * - branches must be within the bounds of the code array
+ * - targets of all control-flow instructions are the start of an instruction
+ * - register accesses fall within range of allocated registers
+ * - (N/A) access to constant pool must be of appropriate type
+ * - code does not end in the middle of an instruction
+ * - execution cannot fall off the end of the code
+ * - (earlier) for each exception handler, the "try" area must begin and
+ *   end at the start of an instruction (end can be at the end of the code)
+ * - (earlier) for each exception handler, the handler must start at a valid
+ *   instruction
+ */
+static bool verifyInstructions(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+    const DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    const u2* insns = meth->insns;
+    unsigned int codeOffset;
+
+    /* the start of the method is a "branch target" */
+    dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+    for (codeOffset = 0; codeOffset < vdata->insnsSize; /**/) {
+        /*
+         * Pull the instruction apart.
+         */
+        int width = dvmInsnGetWidth(insnFlags, codeOffset);
+        DecodedInstruction decInsn;
+        bool okay = true;
+
+        dexDecodeInstruction(meth->insns + codeOffset, &decInsn);
+
+        /*
+         * Check register, type, class, field, method, and string indices
+         * for out-of-range values.  Do additional checks on branch targets
+         * and some special cases like new-instance and new-array.
+         */
+        switch (decInsn.opcode) {
+        case OP_NOP:
+        case OP_RETURN_VOID:
+            /* nothing to check */
+            break;
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT:
+        case OP_MOVE_EXCEPTION:
+        case OP_RETURN:
+        case OP_RETURN_OBJECT:
+        case OP_CONST_4:
+        case OP_CONST_16:
+        case OP_CONST:
+        case OP_CONST_HIGH16:
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_THROW:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            break;
+        case OP_MOVE_RESULT_WIDE:
+        case OP_RETURN_WIDE:
+        case OP_CONST_WIDE_16:
+        case OP_CONST_WIDE_32:
+        case OP_CONST_WIDE:
+        case OP_CONST_WIDE_HIGH16:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            break;
+        case OP_GOTO:
+        case OP_GOTO_16:
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_GOTO_32:
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, true);
+            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:
+        case OP_ARRAY_LENGTH:
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+        case OP_NEG_FLOAT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_INT_TO_BYTE:
+        case OP_INT_TO_CHAR:
+        case OP_INT_TO_SHORT:
+        case OP_ADD_INT_2ADDR:
+        case OP_SUB_INT_2ADDR:
+        case OP_MUL_INT_2ADDR:
+        case OP_DIV_INT_2ADDR:
+        case OP_REM_INT_2ADDR:
+        case OP_AND_INT_2ADDR:
+        case OP_OR_INT_2ADDR:
+        case OP_XOR_INT_2ADDR:
+        case OP_SHL_INT_2ADDR:
+        case OP_SHR_INT_2ADDR:
+        case OP_USHR_INT_2ADDR:
+        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:
+        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_AND_INT_LIT8:
+        case OP_OR_INT_LIT8:
+        case OP_XOR_INT_LIT8:
+        case OP_SHL_INT_LIT8:
+        case OP_SHR_INT_LIT8:
+        case OP_USHR_INT_LIT8:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_INT_TO_LONG:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_SHL_LONG_2ADDR:
+        case OP_SHR_LONG_2ADDR:
+        case OP_USHR_LONG_2ADDR:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_LONG_TO_INT:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_MOVE_WIDE:
+        case OP_MOVE_WIDE_FROM16:
+        case OP_MOVE_WIDE_16:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+        case OP_NEG_DOUBLE:
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+        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_ADD_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE_2ADDR:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            break;
+        case OP_CONST_STRING:
+        case OP_CONST_STRING_JUMBO:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkStringIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_CONST_CLASS:
+        case OP_CHECK_CAST:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_INSTANCE_OF:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkTypeIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_NEW_INSTANCE:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkNewInstance(pDvmDex, decInsn.vB);
+            break;
+        case OP_NEW_ARRAY:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkNewArray(pDvmDex, decInsn.vC);
+            break;
+        case OP_FILL_ARRAY_DATA:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkArrayData(meth, codeOffset);
+            break;
+        case OP_PACKED_SWITCH:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
+            break;
+        case OP_SPARSE_SWITCH:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
+            break;
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+        case OP_AGET_BOOLEAN:
+        case OP_AGET_BYTE:
+        case OP_AGET_CHAR:
+        case OP_AGET_SHORT:
+        case OP_APUT:
+        case OP_APUT_OBJECT:
+        case OP_APUT_BOOLEAN:
+        case OP_APUT_BYTE:
+        case OP_APUT_CHAR:
+        case OP_APUT_SHORT:
+        case OP_ADD_INT:
+        case OP_SUB_INT:
+        case OP_MUL_INT:
+        case OP_DIV_INT:
+        case OP_REM_INT:
+        case OP_AND_INT:
+        case OP_OR_INT:
+        case OP_XOR_INT:
+        case OP_SHL_INT:
+        case OP_SHR_INT:
+        case OP_USHR_INT:
+        case OP_ADD_FLOAT:
+        case OP_SUB_FLOAT:
+        case OP_MUL_FLOAT:
+        case OP_DIV_FLOAT:
+        case OP_REM_FLOAT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_AGET_WIDE:
+        case OP_APUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+        case OP_CMP_LONG:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkWideRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_ADD_DOUBLE:
+        case OP_SUB_DOUBLE:
+        case OP_MUL_DOUBLE:
+        case OP_DIV_DOUBLE:
+        case OP_REM_DOUBLE:
+        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:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkWideRegisterIndex(meth, decInsn.vC);
+            break;
+        case OP_SHL_LONG:
+        case OP_SHR_LONG:
+        case OP_USHR_LONG:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkWideRegisterIndex(meth, decInsn.vB);
+            okay &= checkRegisterIndex(meth, decInsn.vC);
+            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:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
+            break;
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_IGET_WIDE:
+        case OP_IPUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkRegisterIndex(meth, decInsn.vB);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
+            break;
+        case OP_SGET:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            okay &= checkRegisterIndex(meth, decInsn.vA);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_SGET_WIDE:
+        case OP_SPUT_WIDE:
+            okay &= checkWideRegisterIndex(meth, decInsn.vA);
+            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
+            break;
+        case OP_FILLED_NEW_ARRAY:
+            /* decoder uses B, not C, for type ref */
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRegs(meth, &decInsn);
+            break;
+        case OP_FILLED_NEW_ARRAY_RANGE:
+            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRangeRegs(meth, &decInsn);
+            break;
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_INTERFACE:
+            /* decoder uses B, not C, for type ref */
+            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRegs(meth, &decInsn);
+            break;
+        case OP_INVOKE_VIRTUAL_RANGE:
+        case OP_INVOKE_SUPER_RANGE:
+        case OP_INVOKE_DIRECT_RANGE:
+        case OP_INVOKE_STATIC_RANGE:
+        case OP_INVOKE_INTERFACE_RANGE:
+            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
+            okay &= checkVarargRangeRegs(meth, &decInsn);
+            break;
+
+        /* verifier/optimizer output; we should never see these */
+        case OP_IGET_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_SGET_VOLATILE:
+        case OP_SPUT_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+        case OP_BREAKPOINT:
+        case OP_THROW_VERIFICATION_ERROR:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_RETURN_VOID_BARRIER:
+        case OP_IGET_QUICK:
+        case OP_IGET_WIDE_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+        case OP_IPUT_QUICK:
+        case OP_IPUT_WIDE_QUICK:
+        case OP_IPUT_OBJECT_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+        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_FF:
+            ALOGE("VFY: unexpected opcode %04x", decInsn.opcode);
+            okay = false;
+            break;
+
+        /*
+         * DO NOT add a "default" clause here.  Without it the compiler will
+         * complain if an instruction is missing (which is desirable).
+         */
+        }
+
+        if (!okay) {
+            LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
+                decInsn.opcode, codeOffset);
+            return false;
+        }
+
+        OpcodeFlags opFlags = dexGetFlagsFromOpcode(decInsn.opcode);
+        if ((opFlags & VERIFY_GC_INST_MASK) != 0) {
+            /*
+             * This instruction is a GC point.  If space is a concern,
+             * the set of GC points could be reduced by eliminating
+             * foward branches.
+             *
+             * 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.
+             */
+            dvmInsnSetGcPoint(insnFlags, codeOffset, true);
+        }
+
+        assert(width > 0);
+        codeOffset += width;
+        insns += width;
+    }
+
+    /* make sure the last instruction ends at the end of the insn area */
+    if (codeOffset != vdata->insnsSize) {
+        LOG_VFY_METH(meth,
+            "VFY: code did not end when expected (end at %d, count %d)",
+            codeOffset, vdata->insnsSize);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/analysis/DexVerify.h b/vm/analysis/DexVerify.h
new file mode 100644
index 0000000..4487720
--- /dev/null
+++ b/vm/analysis/DexVerify.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik classfile verification.
+ */
+#ifndef DALVIK_DEXVERIFY_H_
+#define DALVIK_DEXVERIFY_H_
+
+/*
+ * Global verification mode.  These must be in order from least verification
+ * to most.  If we're using "exact GC", we may need to perform some of
+ * the verification steps anyway.
+ */
+enum DexClassVerifyMode {
+    VERIFY_MODE_UNKNOWN = 0,
+    VERIFY_MODE_NONE,
+    VERIFY_MODE_REMOTE,
+    VERIFY_MODE_ALL
+};
+
+/* some verifier counters, for debugging */
+struct VerifierStats {
+    size_t methodsExamined;    /* number of methods examined */
+    size_t monEnterMethods;    /* number of methods with monitor-enter */
+    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 */
+    size_t uninitSearches;     /* times we've had to search the uninit table */
+    size_t biggestAlloc;       /* largest RegisterLine table alloc */
+};
+
+/*
+ * Certain types of instructions can be GC points.  To support precise
+ * GC, all such instructions must export the PC in the interpreter,
+ * or the GC won't be able to identify the current PC for the thread.
+ */
+#define VERIFY_GC_INST_MASK (kInstrCanBranch | kInstrCanSwitch |\
+                             kInstrCanThrow | kInstrCanReturn)
+
+/*
+ * Verify a single class.
+ */
+bool dvmVerifyClass(ClassObject* clazz);
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap);
+
+#endif  // DALVIK_DEXVERIFY_H_
diff --git a/vm/analysis/Liveness.cpp b/vm/analysis/Liveness.cpp
new file mode 100644
index 0000000..361d3cb
--- /dev/null
+++ b/vm/analysis/Liveness.cpp
@@ -0,0 +1,824 @@
+/*
+ * 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.
+ */
+
+/*
+ * Liveness analysis for Dalvik bytecode.
+ */
+#include "Dalvik.h"
+#include "analysis/Liveness.h"
+#include "analysis/CodeVerify.h"
+
+static bool processInstruction(VerifierData* vdata, u4 curIdx,
+    BitVector* workBits);
+static bool markDebugLocals(VerifierData* vdata);
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+    const BitVector* workBits);
+
+
+/*
+ * Create a table of instruction widths that indicate the width of the
+ * *previous* instruction.  The values are copied from the width table
+ * in "vdata", not derived from the instruction stream.
+ *
+ * Caller must free the return value.
+ */
+static InstructionWidth* createBackwardWidthTable(VerifierData* vdata)
+{
+    InstructionWidth* widths;
+
+    widths = (InstructionWidth*)
+            calloc(vdata->insnsSize, sizeof(InstructionWidth));
+    if (widths == NULL)
+        return NULL;
+
+    u4 insnWidth = 0;
+    for (u4 idx = 0; idx < vdata->insnsSize; ) {
+        widths[idx] = insnWidth;
+        insnWidth = dvmInsnGetWidth(vdata->insnFlags, idx);
+        idx += insnWidth;
+    }
+
+    return widths;
+}
+
+/*
+ * Compute the "liveness" of every register at all GC points.
+ */
+bool dvmComputeLiveness(VerifierData* vdata)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    InstructionWidth* backwardWidth;
+    VfyBasicBlock* startGuess = NULL;
+    BitVector* workBits = NULL;
+    bool result = false;
+
+    bool verbose = false; //= dvmWantVerboseVerification(vdata->method);
+    if (verbose) {
+        const Method* meth = vdata->method;
+        ALOGI("Computing liveness for %s.%s:%s",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+    }
+
+    assert(vdata->registerLines != NULL);
+
+    backwardWidth = createBackwardWidthTable(vdata);
+    if (backwardWidth == NULL)
+        goto bail;
+
+    /*
+     * Allocate space for intra-block work set.  Does not include space
+     * for method result "registers", which aren't visible to the GC.
+     * (They would be made live by move-result and then die on the
+     * instruction immediately before it.)
+     */
+    workBits = dvmAllocBitVector(vdata->insnRegCount, false);
+    if (workBits == NULL)
+        goto bail;
+
+    /*
+     * We continue until all blocks have been visited, and no block
+     * requires further attention ("visited" is set and "changed" is
+     * clear).
+     *
+     * TODO: consider creating a "dense" array of basic blocks to make
+     * the walking faster.
+     */
+    for (int iter = 0;;) {
+        VfyBasicBlock* workBlock = NULL;
+
+        if (iter++ > 100000) {
+            LOG_VFY_METH(vdata->method, "oh dear");
+            dvmAbort();
+        }
+
+        /*
+         * If a block is marked "changed", we stop and handle it.  If it
+         * just hasn't been visited yet, we remember it but keep searching
+         * for one that has been changed.
+         *
+         * The thought here is that this is more likely to let us work
+         * from end to start, which reduces the amount of re-evaluation
+         * required (both by using "changed" as a work list, and by picking
+         * un-visited blocks from the tail end of the method).
+         */
+        if (startGuess != NULL) {
+            assert(startGuess->changed);
+            workBlock = startGuess;
+        } else {
+            for (u4 idx = 0; idx < vdata->insnsSize; idx++) {
+                VfyBasicBlock* block = vdata->basicBlocks[idx];
+                if (block == NULL)
+                    continue;
+
+                if (block->changed) {
+                    workBlock = block;
+                    break;
+                } else if (!block->visited) {
+                    workBlock = block;
+                }
+            }
+        }
+
+        if (workBlock == NULL) {
+            /* all done */
+            break;
+        }
+
+        assert(workBlock->changed || !workBlock->visited);
+        startGuess = NULL;
+
+        /*
+         * Load work bits.  These represent the liveness of registers
+         * after the last instruction in the block has finished executing.
+         */
+        assert(workBlock->liveRegs != NULL);
+        dvmCopyBitVector(workBits, workBlock->liveRegs);
+        if (verbose) {
+            ALOGI("Loaded work bits from last=0x%04x", workBlock->lastAddr);
+            dumpLiveState(vdata, 0xfffd, workBlock->liveRegs);
+            dumpLiveState(vdata, 0xffff, workBits);
+        }
+
+        /*
+         * Process a single basic block.
+         *
+         * If this instruction is a GC point, we want to save the result
+         * in the RegisterLine.
+         *
+         * We don't break basic blocks on every GC point -- in particular,
+         * instructions that might throw but have no "try" block don't
+         * end a basic block -- so there could be more than one GC point
+         * in a given basic block.
+         *
+         * We could change this, but it turns out to be not all that useful.
+         * At first glance it appears that we could share the liveness bit
+         * vector between the basic block struct and the register line,
+         * but the basic block needs to reflect the state *after* the
+         * instruction has finished, while the GC points need to describe
+         * the state before the instruction starts.
+         */
+        u4 curIdx = workBlock->lastAddr;
+        while (true) {
+            if (!processInstruction(vdata, curIdx, workBits))
+                goto bail;
+
+            if (verbose) {
+                dumpLiveState(vdata, curIdx + 0x8000, workBits);
+            }
+
+            if (dvmInsnIsGcPoint(insnFlags, curIdx)) {
+                BitVector* lineBits = vdata->registerLines[curIdx].liveRegs;
+                if (lineBits == NULL) {
+                    lineBits = vdata->registerLines[curIdx].liveRegs =
+                        dvmAllocBitVector(vdata->insnRegCount, false);
+                }
+                dvmCopyBitVector(lineBits, workBits);
+            }
+
+            if (curIdx == workBlock->firstAddr)
+                break;
+            assert(curIdx >= backwardWidth[curIdx]);
+            curIdx -= backwardWidth[curIdx];
+        }
+
+        workBlock->visited = true;
+        workBlock->changed = false;
+
+        if (verbose) {
+            dumpLiveState(vdata, curIdx, workBits);
+        }
+
+        /*
+         * Merge changes to all predecessors.  If the new bits don't match
+         * the old bits, set the "changed" flag.
+         */
+        PointerSet* preds = workBlock->predecessors;
+        size_t numPreds = dvmPointerSetGetCount(preds);
+        unsigned int predIdx;
+
+        for (predIdx = 0; predIdx < numPreds; predIdx++) {
+            VfyBasicBlock* pred =
+                    (VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+
+            pred->changed = dvmCheckMergeBitVectors(pred->liveRegs, workBits);
+            if (verbose) {
+                ALOGI("merging cur=%04x into pred last=%04x (ch=%d)",
+                    curIdx, pred->lastAddr, pred->changed);
+                dumpLiveState(vdata, 0xfffa, pred->liveRegs);
+                dumpLiveState(vdata, 0xfffb, workBits);
+            }
+
+            /*
+             * We want to set the "changed" flag on unvisited predecessors
+             * as a way of guiding the verifier through basic blocks in
+             * a reasonable order.  We can't count on variable liveness
+             * changing, so we force "changed" to true even if it hasn't.
+             */
+            if (!pred->visited)
+                pred->changed = true;
+
+            /*
+             * Keep track of one of the changed blocks so we can start
+             * there instead of having to scan through the list.
+             */
+            if (pred->changed)
+                startGuess = pred;
+        }
+    }
+
+#ifndef NDEBUG
+    /*
+     * Sanity check: verify that all GC point register lines have a
+     * liveness bit vector allocated.  Also, we're not expecting non-GC
+     * points to have them.
+     */
+    u4 checkIdx;
+    for (checkIdx = 0; checkIdx < vdata->insnsSize; ) {
+        if (dvmInsnIsGcPoint(insnFlags, checkIdx)) {
+            if (vdata->registerLines[checkIdx].liveRegs == NULL) {
+                LOG_VFY_METH(vdata->method,
+                    "GLITCH: no liveRegs for GC point 0x%04x", checkIdx);
+                dvmAbort();
+            }
+        } else if (vdata->registerLines[checkIdx].liveRegs != NULL) {
+            LOG_VFY_METH(vdata->method,
+                "GLITCH: liveRegs for non-GC point 0x%04x", checkIdx);
+            dvmAbort();
+        }
+        u4 insnWidth = dvmInsnGetWidth(insnFlags, checkIdx);
+        checkIdx += insnWidth;
+    }
+#endif
+
+    /*
+     * Factor in the debug info, if any.
+     */
+    if (!markDebugLocals(vdata))
+        goto bail;
+
+    result = true;
+
+bail:
+    free(backwardWidth);
+    dvmFreeBitVector(workBits);
+    return result;
+}
+
+
+/*
+ * Add a register to the LIVE set.
+ */
+static inline void GEN(BitVector* workBits, u4 regIndex)
+{
+    dvmSetBit(workBits, regIndex);
+}
+
+/*
+ * Add a register pair to the LIVE set.
+ */
+static inline void GENW(BitVector* workBits, u4 regIndex)
+{
+    dvmSetBit(workBits, regIndex);
+    dvmSetBit(workBits, regIndex+1);
+}
+
+/*
+ * Remove a register from the LIVE set.
+ */
+static inline void KILL(BitVector* workBits, u4 regIndex)
+{
+    dvmClearBit(workBits, regIndex);
+}
+
+/*
+ * Remove a register pair from the LIVE set.
+ */
+static inline void KILLW(BitVector* workBits, u4 regIndex)
+{
+    dvmClearBit(workBits, regIndex);
+    dvmClearBit(workBits, regIndex+1);
+}
+
+/*
+ * Process a single instruction.
+ *
+ * Returns "false" if something goes fatally wrong.
+ */
+static bool processInstruction(VerifierData* vdata, u4 insnIdx,
+    BitVector* workBits)
+{
+    const Method* meth = vdata->method;
+    const u2* insns = meth->insns + insnIdx;
+    DecodedInstruction decInsn;
+
+    dexDecodeInstruction(insns, &decInsn);
+
+    /*
+     * Add registers to the "GEN" or "KILL" sets.  We want to do KILL
+     * before GEN to handle cases where the source and destination
+     * register is the same.
+     */
+    switch (decInsn.opcode) {
+    case OP_NOP:
+    case OP_RETURN_VOID:
+    case OP_GOTO:
+    case OP_GOTO_16:
+    case OP_GOTO_32:
+        /* no registers are used */
+        break;
+
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+    case OP_MONITOR_ENTER:
+    case OP_MONITOR_EXIT:
+    case OP_CHECK_CAST:
+    case OP_THROW:
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+    case OP_FILL_ARRAY_DATA:
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+    case OP_SPUT:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+    case OP_SPUT_OBJECT:
+        /* action <- vA */
+        GEN(workBits, decInsn.vA);
+        break;
+
+    case OP_RETURN_WIDE:
+    case OP_SPUT_WIDE:
+        /* action <- vA(wide) */
+        GENW(workBits, decInsn.vA);
+        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_IPUT:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_OBJECT:
+        /* action <- vA, vB */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_IPUT_WIDE:
+        /* action <- vA(wide), vB */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_APUT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+    case OP_APUT_OBJECT:
+        /* action <- vA, vB, vC */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_APUT_WIDE:
+        /* action <- vA(wide), vB, vC */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_FILLED_NEW_ARRAY:
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+        /* action <- vararg */
+        {
+            unsigned int idx;
+            for (idx = 0; idx < decInsn.vA; idx++) {
+                GEN(workBits, decInsn.arg[idx]);
+            }
+        }
+        break;
+
+    case OP_FILLED_NEW_ARRAY_RANGE:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        /* action <- vararg/range */
+        {
+            unsigned int idx;
+            for (idx = 0; idx < decInsn.vA; idx++) {
+                GEN(workBits, decInsn.vC + idx);
+            }
+        }
+        break;
+
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_WIDE:
+    case OP_MOVE_RESULT_OBJECT:
+    case OP_MOVE_EXCEPTION:
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+    case OP_CONST_HIGH16:
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+    case OP_NEW_INSTANCE:
+    case OP_SGET:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SGET_OBJECT:
+        /* vA <- value */
+        KILL(workBits, decInsn.vA);
+        break;
+
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+    case OP_SGET_WIDE:
+        /* vA(wide) <- value */
+        KILLW(workBits, decInsn.vA);
+        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:
+    case OP_INSTANCE_OF:
+    case OP_ARRAY_LENGTH:
+    case OP_NEW_ARRAY:
+    case OP_IGET:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_OBJECT:
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_FLOAT:
+    case OP_INT_TO_FLOAT:
+    case OP_FLOAT_TO_INT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+    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:
+        /* vA <- vB */
+        KILL(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_IGET_WIDE:
+    case OP_INT_TO_LONG:
+    case OP_INT_TO_DOUBLE:
+    case OP_FLOAT_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+        /* vA(wide) <- vB */
+        KILLW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_LONG_TO_INT:
+    case OP_LONG_TO_FLOAT:
+    case OP_DOUBLE_TO_INT:
+    case OP_DOUBLE_TO_FLOAT:
+        /* vA <- vB(wide) */
+        KILL(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_DOUBLE:
+    case OP_LONG_TO_DOUBLE:
+    case OP_DOUBLE_TO_LONG:
+        /* vA(wide) <- vB(wide) */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+    case OP_AGET:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+    case OP_AGET_OBJECT:
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        /* vA <- vB, vC */
+        KILL(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_AGET_WIDE:
+        /* vA(wide) <- vB, vC */
+        KILLW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        break;
+
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+    case OP_CMP_LONG:
+        /* vA <- vB(wide), vC(wide) */
+        KILL(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GENW(workBits, decInsn.vC);
+        break;
+
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        /* vA(wide) <- vB(wide), vC */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GEN(workBits, decInsn.vC);
+        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_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        /* vA(wide) <- vB(wide), vC(wide) */
+        KILLW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        GENW(workBits, decInsn.vC);
+        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:
+        /* vA <- vA, vB */
+        /* KILL(workBits, decInsn.vA); */
+        GEN(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        break;
+
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        /* vA(wide) <- vA(wide), vB */
+        /* KILLW(workBits, decInsn.vA); */
+        GENW(workBits, decInsn.vA);
+        GEN(workBits, decInsn.vB);
+        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_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+    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:
+        /* vA(wide) <- vA(wide), vB(wide) */
+        /* KILLW(workBits, decInsn.vA); */
+        GENW(workBits, decInsn.vA);
+        GENW(workBits, decInsn.vB);
+        break;
+
+    /* we will only see this if liveness analysis is done after general vfy */
+    case OP_THROW_VERIFICATION_ERROR:
+        /* no registers used */
+        break;
+
+    /* quickened instructions, not expected to appear */
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        /* fall through to failure */
+
+    /* correctness fixes, not expected to appear */
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+    case OP_RETURN_VOID_BARRIER:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+        /* fall through to failure */
+
+    /* these should never appear during verification */
+    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_BREAKPOINT:
+    case OP_UNUSED_FF:
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * This is a dexDecodeDebugInfo callback, used by markDebugLocals().
+ */
+static void markLocalsCb(void* ctxt, u2 reg, u4 startAddress, u4 endAddress,
+    const char* name, const char* descriptor, const char* signature)
+{
+    VerifierData* vdata = (VerifierData*) ctxt;
+    bool verbose = dvmWantVerboseVerification(vdata->method);
+
+    if (verbose) {
+        ALOGI("%04x-%04x %2d (%s %s)",
+            startAddress, endAddress, reg, name, descriptor);
+    }
+
+    bool wide = (descriptor[0] == 'D' || descriptor[0] == 'J');
+    assert(reg <= vdata->insnRegCount + (wide ? 1 : 0));
+
+    /*
+     * Set the bit in all GC point instructions in the range
+     * [startAddress, endAddress).
+     */
+    unsigned int idx;
+    for (idx = startAddress; idx < endAddress; idx++) {
+        BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+        if (liveRegs != NULL) {
+            if (wide) {
+                GENW(liveRegs, reg);
+            } else {
+                GEN(liveRegs, reg);
+            }
+        }
+    }
+}
+
+/*
+ * Mark all debugger-visible locals as live.
+ *
+ * The "locals" table describes the positions of the various locals in the
+ * stack frame based on the current execution address.  If the debugger
+ * wants to display one, it issues a request by "slot number".  We need
+ * to ensure that references in stack slots that might be queried by the
+ * debugger aren't GCed.
+ *
+ * (If the GC had some way to mark the slot as invalid we wouldn't have
+ * to do this.  We could also have the debugger interface check the
+ * register map and simply refuse to return a "dead" value, but that's
+ * potentially confusing since the referred-to object might actually be
+ * alive, and being able to see it without having to hunt around for a
+ * "live" stack frame is useful.)
+ */
+static bool markDebugLocals(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+
+    dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile, dvmGetMethodCode(meth),
+        meth->clazz->descriptor, meth->prototype.protoIdx, meth->accessFlags,
+        NULL, markLocalsCb, vdata);
+
+    return true;
+}
+
+
+/*
+ * Dump the liveness bits to the log.
+ *
+ * "curIdx" is for display only.
+ */
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+    const BitVector* workBits)
+{
+    u4 insnRegCount = vdata->insnRegCount;
+    size_t regCharSize = insnRegCount + (insnRegCount-1)/4 + 2 +1;
+    char regChars[regCharSize +1];
+    unsigned int idx;
+
+    memset(regChars, ' ', regCharSize);
+    regChars[0] = '[';
+    if (insnRegCount == 0)
+        regChars[1] = ']';
+    else
+        regChars[1 + (insnRegCount-1) + (insnRegCount-1)/4 +1] = ']';
+    regChars[regCharSize] = '\0';
+
+    for (idx = 0; idx < insnRegCount; idx++) {
+        char ch = dvmIsBitSet(workBits, idx) ? '+' : '-';
+        regChars[1 + idx + (idx/4)] = ch;
+    }
+
+    ALOGI("0x%04x %s", curIdx, regChars);
+}
diff --git a/vm/analysis/Liveness.h b/vm/analysis/Liveness.h
new file mode 100644
index 0000000..5f4acfe
--- /dev/null
+++ b/vm/analysis/Liveness.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/*
+ * Liveness analysis.
+ */
+#ifndef DALVIK_LIVENESS_H_
+#define DALVIK_LIVENESS_H_
+
+struct VerifierData;
+
+bool dvmComputeLiveness(struct VerifierData* vdata);
+
+#endif  // DALVIK_LIVENESS_H_
diff --git a/vm/analysis/Optimize.cpp b/vm/analysis/Optimize.cpp
new file mode 100644
index 0000000..b61b82c
--- /dev/null
+++ b/vm/analysis/Optimize.cpp
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Perform some simple bytecode optimizations, chiefly "quickening" of
+ * opcodes.
+ */
+#include "Dalvik.h"
+#include "libdex/InstrUtils.h"
+#include "Optimize.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+
+/*
+ * Virtual/direct calls to "method" are replaced with an execute-inline
+ * instruction with index "idx".
+ */
+struct InlineSub {
+    Method* method;
+    int     inlineIdx;
+};
+
+
+/* fwd */
+static void optimizeMethod(Method* method, bool essentialOnly);
+static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc,
+    Opcode volatileOpc);
+static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc);
+static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc);
+static bool rewriteInvokeObjectInit(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType);
+static void rewriteReturnVoid(Method* method, u2* insns);
+static bool needsReturnBarrier(Method* method);
+
+
+/*
+ * Create a table of inline substitutions.  Sets gDvm.inlineSubs.
+ *
+ * TODO: this is currently just a linear array.  We will want to put this
+ * into a hash table as the list size increases.
+ */
+bool dvmCreateInlineSubsTable()
+{
+    const InlineOperation* ops = dvmGetInlineOpsTable();
+    const int count = dvmGetInlineOpsTableLength();
+    InlineSub* table;
+    int i, tableIndex;
+
+    assert(gDvm.inlineSubs == NULL);
+
+    /*
+     * One slot per entry, plus an end-of-list marker.
+     */
+    table = (InlineSub*) calloc(count + 1, sizeof(InlineSub));
+
+    tableIndex = 0;
+    for (i = 0; i < count; i++) {
+        Method* method = dvmFindInlinableMethod(ops[i].classDescriptor,
+            ops[i].methodName, ops[i].methodSignature);
+        if (method == NULL) {
+            /*
+             * Not expected.  We only use this for key methods in core
+             * classes, so we should always be able to find them.
+             */
+            ALOGE("Unable to find method for inlining: %s.%s:%s",
+                ops[i].classDescriptor, ops[i].methodName,
+                ops[i].methodSignature);
+            free(table);
+            return false;
+        }
+
+        table[tableIndex].method = method;
+        table[tableIndex].inlineIdx = i;
+        tableIndex++;
+    }
+
+    /* mark end of table */
+    table[tableIndex].method = NULL;
+
+    gDvm.inlineSubs = table;
+    return true;
+}
+
+/*
+ * Release inline sub data structure.
+ */
+void dvmFreeInlineSubsTable()
+{
+    free(gDvm.inlineSubs);
+    gDvm.inlineSubs = NULL;
+}
+
+
+/*
+ * Optimize the specified class.
+ *
+ * If "essentialOnly" is true, we only do essential optimizations.  For
+ * example, accesses to volatile 64-bit fields must be replaced with
+ * "-wide-volatile" instructions or the program could behave incorrectly.
+ * (Skipping non-essential optimizations makes us a little bit faster, and
+ * more importantly avoids dirtying DEX pages.)
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
+{
+    int i;
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        optimizeMethod(&clazz->directMethods[i], essentialOnly);
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
+    }
+}
+
+/*
+ * Optimize instructions in a method.
+ *
+ * This does a single pass through the code, examining each instruction.
+ *
+ * This is not expected to fail if the class was successfully verified.
+ * The only significant failure modes on unverified code occur when an
+ * "essential" update fails, but we can't generally identify those: if we
+ * can't look up a field, we can't know if the field access was supposed
+ * to be handled as volatile.
+ *
+ * Instead, we give it our best effort, and hope for the best.  For 100%
+ * reliability, only optimize a class after verification succeeds.
+ */
+static void optimizeMethod(Method* method, bool essentialOnly)
+{
+    bool needRetBar, forSmp;
+    u4 insnsSize;
+    u2* insns;
+
+    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+        return;
+
+    forSmp = gDvm.dexOptForSmp;
+    needRetBar = needsReturnBarrier(method);
+
+    insns = (u2*) method->insns;
+    assert(insns != NULL);
+    insnsSize = dvmGetMethodInsnsSize(method);
+
+    while (insnsSize > 0) {
+        Opcode opc, quickOpc, volatileOpc;
+        size_t width;
+        bool matched = true;
+
+        opc = dexOpcodeFromCodeUnit(*insns);
+        width = dexGetWidthFromInstruction(insns);
+        volatileOpc = OP_NOP;
+
+        /*
+         * Each instruction may have:
+         * - "volatile" replacement
+         *   - may be essential or essential-on-SMP
+         * - correctness replacement
+         *   - may be essential or essential-on-SMP
+         * - performance replacement
+         *   - always non-essential
+         *
+         * Replacements are considered in the order shown, and the first
+         * match is applied.  For example, iget-wide will convert to
+         * iget-wide-volatile rather than iget-wide-quick if the target
+         * field is volatile.
+         */
+
+        /*
+         * essential substitutions:
+         *  {iget,iput,sget,sput}-wide --> {op}-wide-volatile
+         *  invoke-direct[/range] --> invoke-object-init/range
+         *
+         * essential-on-SMP substitutions:
+         *  {iget,iput,sget,sput}-* --> {op}-volatile
+         *  return-void --> return-void-barrier
+         *
+         * non-essential substitutions:
+         *  {iget,iput}-* --> {op}-quick
+         *
+         * TODO: might be time to merge this with the other two switches
+         */
+        switch (opc) {
+        case OP_IGET:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            quickOpc = OP_IGET_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IGET_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IGET_WIDE:
+            quickOpc = OP_IGET_WIDE_QUICK;
+            volatileOpc = OP_IGET_WIDE_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IGET_OBJECT:
+            quickOpc = OP_IGET_OBJECT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IGET_OBJECT_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            quickOpc = OP_IPUT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IPUT_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT_WIDE:
+            quickOpc = OP_IPUT_WIDE_QUICK;
+            volatileOpc = OP_IPUT_WIDE_VOLATILE;
+            goto rewrite_inst_field;
+        case OP_IPUT_OBJECT:
+            quickOpc = OP_IPUT_OBJECT_QUICK;
+            if (forSmp)
+                volatileOpc = OP_IPUT_OBJECT_VOLATILE;
+            /* fall through */
+rewrite_inst_field:
+            if (essentialOnly)
+                quickOpc = OP_NOP;      /* if essential-only, no "-quick" sub */
+            if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
+                rewriteInstField(method, insns, quickOpc, volatileOpc);
+            break;
+
+        case OP_SGET:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+            if (forSmp)
+                volatileOpc = OP_SGET_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_WIDE:
+            volatileOpc = OP_SGET_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_OBJECT:
+            if (forSmp)
+                volatileOpc = OP_SGET_OBJECT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            if (forSmp)
+                volatileOpc = OP_SPUT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_WIDE:
+            volatileOpc = OP_SPUT_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_OBJECT:
+            if (forSmp)
+                volatileOpc = OP_SPUT_OBJECT_VOLATILE;
+            /* fall through */
+rewrite_static_field:
+            if (volatileOpc != OP_NOP)
+                rewriteStaticField(method, insns, volatileOpc);
+            break;
+
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE:
+            if (!rewriteInvokeObjectInit(method, insns)) {
+                /* may want to try execute-inline, below */
+                matched = false;
+            }
+            break;
+        case OP_RETURN_VOID:
+            if (needRetBar)
+                rewriteReturnVoid(method, insns);
+            break;
+        default:
+            matched = false;
+            break;
+        }
+
+
+        /*
+         * non-essential substitutions:
+         *  invoke-{virtual,direct,static}[/range] --> execute-inline
+         *  invoke-{virtual,super}[/range] --> invoke-*-quick
+         */
+        if (!matched && !essentialOnly) {
+            switch (opc) {
+            case OP_INVOKE_VIRTUAL:
+                if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                        OP_INVOKE_VIRTUAL_QUICK);
+                }
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+                if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                        OP_INVOKE_VIRTUAL_QUICK_RANGE);
+                }
+                break;
+            case OP_INVOKE_SUPER:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
+                break;
+            case OP_INVOKE_DIRECT:
+                rewriteExecuteInline(method, insns, METHOD_DIRECT);
+                break;
+            case OP_INVOKE_DIRECT_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
+                break;
+            case OP_INVOKE_STATIC:
+                rewriteExecuteInline(method, insns, METHOD_STATIC);
+                break;
+            case OP_INVOKE_STATIC_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
+                break;
+            default:
+                /* nothing to do for this instruction */
+                ;
+            }
+        }
+
+        assert(width > 0);
+        assert(width <= insnsSize);
+        assert(width == dexGetWidthFromInstruction(insns));
+
+        insns += width;
+        insnsSize -= width;
+    }
+
+    assert(insnsSize == 0);
+}
+
+/*
+ * Update a 16-bit code unit in "meth".  The way in which the DEX data was
+ * loaded determines how we go about the write.
+ *
+ * This will be operating on post-byte-swap DEX data, so values will
+ * be in host order.
+ */
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+
+    if (!pDvmDex->isMappedReadOnly) {
+        /* in-memory DEX (dexopt or byte[]), alter the output directly */
+        *ptr = newVal;
+    } else {
+        /* memory-mapped file, toggle the page read/write status */
+        dvmDexChangeDex2(pDvmDex, ptr, newVal);
+    }
+}
+
+/*
+ * Update an instruction's opcode.
+ *
+ * If "opcode" is an 8-bit op, we just replace that portion.  If it's a
+ * 16-bit op, we convert the opcode from "packed" form (e.g. 0x0108) to
+ * bytecode form (e.g. 0x08ff).
+ */
+static inline void updateOpcode(const Method* meth, u2* ptr, u2 opcode)
+{
+    if (opcode >= 256) {
+        /* opcode low byte becomes high byte, low byte becomes 0xff */
+        assert((ptr[0] & 0xff) == 0xff);
+        dvmUpdateCodeUnit(meth, ptr, (opcode << 8) | 0x00ff);
+    } else {
+        /* 8-bit op, just replace the low byte */
+        assert((ptr[0] & 0xff) != 0xff);
+        dvmUpdateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | opcode);
+    }
+}
+
+/*
+ * If "referrer" and "resClass" don't come from the same DEX file, and
+ * the DEX we're working on is not destined for the bootstrap class path,
+ * tweak the class loader so package-access checks work correctly.
+ *
+ * Only do this if we're doing pre-verification or optimization.
+ */
+static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing)
+        return;
+    assert(referrer->classLoader == NULL);
+    assert(resClass->classLoader == NULL);
+
+    if (!gDvm.optimizingBootstrapClass) {
+        /* class loader for an array class comes from element type */
+        if (dvmIsArrayClass(resClass))
+            resClass = resClass->elementClass;
+        if (referrer->pDvmDex != resClass->pDvmDex)
+            resClass->classLoader = (Object*) 0xdead3333;
+    }
+}
+
+/*
+ * Undo the effects of tweakLoader.
+ */
+static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+    if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
+        return;
+
+    if (dvmIsArrayClass(resClass))
+        resClass = resClass->elementClass;
+    resClass->classLoader = NULL;
+}
+
+
+/*
+ * Alternate version of dvmResolveClass for use with verification and
+ * optimization.  Performs access checks on every resolve, and refuses
+ * to acknowledge the existence of classes defined in more than one DEX
+ * file.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+
+    /*
+     * Check the table first.  If not there, do the lookup by name.
+     */
+    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+    if (resClass == NULL) {
+        const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+        if (className[0] != '\0' && className[1] == '\0') {
+            /* primitive type */
+            resClass = dvmFindPrimitiveClass(className[0]);
+        } else {
+            resClass = dvmFindClassNoInit(className, referrer->classLoader);
+        }
+        if (resClass == NULL) {
+            /* not found, exception should be raised */
+            ALOGV("DexOpt: class %d (%s) not found",
+                classIdx,
+                dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+            if (pFailure != NULL) {
+                /* dig through the wrappers to find the original failure */
+                Object* excep = dvmGetException(dvmThreadSelf());
+                while (true) {
+                    Object* cause = dvmGetExceptionCause(excep);
+                    if (cause == NULL)
+                        break;
+                    excep = cause;
+                }
+                if (strcmp(excep->clazz->descriptor,
+                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
+                {
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                } else {
+                    *pFailure = VERIFY_ERROR_NO_CLASS;
+                }
+            }
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+    }
+
+    /* multiple definitions? */
+    if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
+        ALOGI("DexOpt: not resolving ambiguous class '%s'",
+            resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_NO_CLASS;
+        return NULL;
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resClass);
+    bool allowed = dvmCheckClassAccess(referrer, resClass);
+    untweakLoader(referrer, resClass);
+    if (!allowed) {
+        ALOGW("DexOpt: resolve class illegal access: %s -> %s",
+            referrer->descriptor, resClass->descriptor);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_CLASS;
+        return NULL;
+    }
+
+    return resClass;
+}
+
+/*
+ * Alternate version of dvmResolveInstField().
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    InstField* resField;
+
+    resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        resField = (InstField*)dvmFindFieldHier(resClass,
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+            dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            ALOGD("DexOpt: couldn't find field %s.%s",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (dvmIsStaticField(resField)) {
+            ALOGD("DexOpt: wanted instance, got static for field %s.%s",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->clazz);
+    if (!allowed) {
+        ALOGI("DexOpt: access denied from %s to field %s.%s",
+            referrer->descriptor, resField->clazz->descriptor,
+            resField->name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+/*
+ * Alternate version of dvmResolveStaticField().
+ *
+ * Does not force initialization of the resolved field's class.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    StaticField* resField;
+
+    resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
+    if (resField == NULL) {
+        const DexFieldId* pFieldId;
+        ClassObject* resClass;
+
+        pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+        /*
+         * Find the field's class.
+         */
+        resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+        if (resClass == NULL) {
+            //dvmClearOptException(dvmThreadSelf());
+            assert(!dvmCheckException(dvmThreadSelf()));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+
+        const char* fieldName =
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+        resField = (StaticField*)dvmFindFieldHier(resClass, fieldName,
+                    dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            ALOGD("DexOpt: couldn't find static field %s.%s",
+                resClass->descriptor, fieldName);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (!dvmIsStaticField(resField)) {
+            ALOGD("DexOpt: wanted static, got instance for field %s.%s",
+                resClass->descriptor, fieldName);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this if we're in "dexopt", because the presence
+         * of a valid value in the resolution table implies that the class
+         * containing the static field has been initialized.
+         */
+        if (gDvm.optimizing)
+            dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    }
+
+    /* access allowed? */
+    tweakLoader(referrer, resField->clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->clazz);
+    if (!allowed) {
+        ALOGI("DexOpt: access denied from %s to field %s.%s",
+            referrer->descriptor, resField->clazz->descriptor,
+            resField->name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction if appropriate.  These all have the form:
+ *   op vA, vB, field@CCCC
+ *
+ * Where vA holds the value, vB holds the object reference, and CCCC is
+ * the field reference constant pool offset.  For a non-volatile field,
+ * we want to replace the opcode with "quickOpc" and replace CCCC with
+ * the byte offset from the start of the object.  For a volatile field,
+ * we just want to replace the opcode with "volatileOpc".
+ *
+ * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile
+ * field.  If "quickOpc" is OP_NOP, and this is a non-volatile field,
+ * we don't do anything.
+ *
+ * "method" is the referring method.
+ */
+static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc,
+    Opcode volatileOpc)
+{
+    ClassObject* clazz = method->clazz;
+    u2 fieldIdx = insns[1];
+    InstField* instField;
+
+    instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+    if (instField == NULL) {
+        ALOGI("DexOpt: unable to optimize instance field ref "
+             "0x%04x at 0x%02x in %s.%s",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    if (volatileOpc != OP_NOP && dvmIsVolatileField(instField)) {
+        updateOpcode(method, insns, volatileOpc);
+        ALOGV("DexOpt: rewrote ifield access %s.%s --> volatile",
+            instField->clazz->descriptor, instField->name);
+    } else if (quickOpc != OP_NOP && instField->byteOffset < 65536) {
+        updateOpcode(method, insns, quickOpc);
+        dvmUpdateCodeUnit(method, insns+1, (u2) instField->byteOffset);
+        ALOGV("DexOpt: rewrote ifield access %s.%s --> %d",
+            instField->clazz->descriptor, instField->name,
+            instField->byteOffset);
+    } else {
+        ALOGV("DexOpt: no rewrite of ifield access %s.%s",
+            instField->clazz->descriptor, instField->name);
+    }
+
+    return;
+}
+
+/*
+ * Rewrite a static field access instruction if appropriate.  If
+ * the target field is volatile, we replace the opcode with "volatileOpc".
+ *
+ * "method" is the referring method.
+ */
+static void rewriteStaticField0(Method* method, u2* insns, Opcode volatileOpc,
+    u4 fieldIdx)
+{
+    ClassObject* clazz = method->clazz;
+    StaticField* staticField;
+
+    assert(volatileOpc != OP_NOP);
+
+    staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
+    if (staticField == NULL) {
+        ALOGI("DexOpt: unable to optimize static field ref "
+             "0x%04x at 0x%02x in %s.%s",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    if (dvmIsVolatileField(staticField)) {
+        updateOpcode(method, insns, volatileOpc);
+        ALOGV("DexOpt: rewrote sfield access %s.%s --> volatile",
+            staticField->clazz->descriptor, staticField->name);
+    }
+}
+
+static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc)
+{
+    u2 fieldIdx = insns[1];
+    rewriteStaticField0(method, insns, volatileOpc, fieldIdx);
+}
+
+/*
+ * Alternate version of dvmResolveMethod().
+ *
+ * Doesn't throw exceptions, and checks access on every lookup.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+
+    assert(methodType == METHOD_DIRECT ||
+           methodType == METHOD_VIRTUAL ||
+           methodType == METHOD_STATIC);
+
+    LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
+        referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
+        if (resClass == NULL) {
+            /*
+             * Can't find the class that the method is a part of, or don't
+             * have permission to access the class.
+             */
+            ALOGV("DexOpt: can't find called method's class (?.%s)",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+            return NULL;
+        }
+        if (dvmIsInterfaceClass(resClass)) {
+            /* method is part of an interface; this is wrong method for that */
+            ALOGW("DexOpt: method is in an interface");
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * We need to chase up the class hierarchy to find methods defined
+         * in super-classes.  (We only want to check the current class
+         * if we're looking for a constructor.)
+         */
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        if (methodType == METHOD_DIRECT) {
+            resMethod = dvmFindDirectMethod(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        } else {
+            /* METHOD_STATIC or METHOD_VIRTUAL */
+            resMethod = dvmFindMethodHier(resClass,
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+        }
+
+        if (resMethod == NULL) {
+            ALOGV("DexOpt: couldn't find method '%s'",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_METHOD;
+            return NULL;
+        }
+        if (methodType == METHOD_STATIC) {
+            if (!dvmIsStaticMethod(resMethod)) {
+                ALOGD("DexOpt: wanted static, got instance for method %s.%s",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        } else if (methodType == METHOD_VIRTUAL) {
+            if (dvmIsStaticMethod(resMethod)) {
+                ALOGD("DexOpt: wanted instance, got static for method %s.%s",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        }
+
+        /* see if this is a pure-abstract method */
+        if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+            ALOGW("DexOpt: pure-abstract method '%s' in %s",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
+                resClass->descriptor);
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_GENERIC;
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         *
+         * We can only do this for static methods if we're not in "dexopt",
+         * because the presence of a valid value in the resolution table
+         * implies that the class containing the static field has been
+         * initialized.
+         */
+        if (methodType != METHOD_STATIC || gDvm.optimizing)
+            dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found method %d (%s.%s)",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* access allowed? */
+    tweakLoader(referrer, resMethod->clazz);
+    bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+    untweakLoader(referrer, resMethod->clazz);
+    if (!allowed) {
+        IF_ALOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            ALOGI("DexOpt: illegal method access (call %s.%s %s from %s)",
+                resMethod->clazz->descriptor, resMethod->name, desc,
+                referrer->descriptor);
+            free(desc);
+        }
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_METHOD;
+        return NULL;
+    }
+
+    return resMethod;
+}
+
+/*
+ * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
+ * invoke-super/range if appropriate.  These all have the form:
+ *   op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * We want to replace the method constant pool index BBBB with the
+ * vtable index.
+ */
+static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc)
+{
+    ClassObject* clazz = method->clazz;
+    Method* baseMethod;
+    u2 methodIdx = insns[1];
+
+    baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
+    if (baseMethod == NULL) {
+        ALOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return;
+    }
+
+    assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
+           (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER ||
+           (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
+
+    /*
+     * Note: Method->methodIndex is a u2 and is range checked during the
+     * initial load.
+     */
+    updateOpcode(method, insns, newOpc);
+    dvmUpdateCodeUnit(method, insns+1, baseMethod->methodIndex);
+
+    //ALOGI("DexOpt: rewrote call to %s.%s --> %s.%s",
+    //    method->clazz->descriptor, method->name,
+    //    baseMethod->clazz->descriptor, baseMethod->name);
+
+    return;
+}
+
+/*
+ * Rewrite invoke-direct[/range] if the target is Object.<init>.
+ *
+ * This is useful as an optimization, because otherwise every object
+ * instantiation will cause us to call a method that does nothing.
+ * It also allows us to inexpensively mark objects as finalizable at the
+ * correct time.
+ *
+ * TODO: verifier should ensure Object.<init> contains only return-void,
+ * and issue a warning if not.
+ */
+static bool rewriteInvokeObjectInit(Method* method, u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+    if (calledMethod == NULL) {
+        ALOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s",
+            methodIdx, (int) (insns - method->insns),
+            clazz->descriptor, method->name);
+        return false;
+    }
+
+    if (calledMethod->clazz == gDvm.classJavaLangObject &&
+        dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+    {
+        /*
+         * Replace the instruction.  If the debugger is attached, the
+         * interpreter will forward execution to the invoke-direct/range
+         * handler.  If this was an invoke-direct/range instruction we can
+         * just replace the opcode, but if it was an invoke-direct we
+         * have to set the argument count (high 8 bits of first code unit)
+         * to 1.
+         */
+        u1 origOp = insns[0] & 0xff;
+        if (origOp == OP_INVOKE_DIRECT) {
+            dvmUpdateCodeUnit(method, insns,
+                OP_INVOKE_OBJECT_INIT_RANGE | 0x100);
+        } else {
+            assert(origOp == OP_INVOKE_DIRECT_RANGE);
+            assert((insns[0] >> 8) == 1);
+            updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT_RANGE);
+        }
+
+        LOGVV("DexOpt: replaced Object.<init> in %s.%s",
+            method->clazz->descriptor, method->name);
+    }
+
+    return true;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * No method access check here -- interface methods are always public.
+ *
+ * Returns NULL if the method was not found.  Does not throw an exception.
+ */
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    Method* resMethod;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)",
+        methodIdx, referrer->descriptor);
+
+    resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (resMethod == NULL) {
+        const DexMethodId* pMethodId;
+        ClassObject* resClass;
+
+        pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+        resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
+        if (resClass == NULL) {
+            /* can't find the class that the method is a part of */
+            dvmClearOptException(dvmThreadSelf());
+            return NULL;
+        }
+        if (!dvmIsInterfaceClass(resClass)) {
+            /* whoops */
+            ALOGI("Interface method not part of interface class");
+            return NULL;
+        }
+
+        const char* methodName =
+            dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+        DexProto proto;
+        dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+        LOGVV("+++ looking for '%s' '%s' in resClass='%s'",
+            methodName, methodSig, resClass->descriptor);
+        resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
+        if (resMethod == NULL) {
+            return NULL;
+        }
+
+        /* we're expecting this to be abstract */
+        if (!dvmIsAbstractMethod(resMethod)) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            ALOGW("Found non-abstract interface method %s.%s %s",
+                resMethod->clazz->descriptor, resMethod->name, desc);
+            free(desc);
+            return NULL;
+        }
+
+        /*
+         * Add it to the resolved table so we're faster on the next lookup.
+         */
+        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* interface methods are always public; no need to check access */
+
+    return resMethod;
+}
+
+/*
+ * Replace invoke-virtual, invoke-direct, or invoke-static with an
+ * execute-inline operation if appropriate.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType)
+{
+    const InlineSub* inlineSubs = gDvm.inlineSubs;
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    //return false;
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        ALOGV("+++ DexOpt inline: can't find %d", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        /*
+        if (extra) {
+            ALOGI("comparing %p vs %p %s.%s %s",
+                inlineSubs->method, calledMethod,
+                inlineSubs->method->clazz->descriptor,
+                inlineSubs->method->name,
+                inlineSubs->method->signature);
+        }
+        */
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
+            updateOpcode(method, insns, OP_EXECUTE_INLINE);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //ALOGI("DexOpt: execute-inline %s.%s --> %s.%s",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * Replace invoke-virtual/range, invoke-direct/range, or invoke-static/range
+ * with an execute-inline operation if appropriate.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType)
+{
+    const InlineSub* inlineSubs = gDvm.inlineSubs;
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+    if (calledMethod == NULL) {
+        ALOGV("+++ DexOpt inline/range: can't find %d", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        if (inlineSubs->method == calledMethod) {
+            assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
+                   (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
+            updateOpcode(method, insns, OP_EXECUTE_INLINE_RANGE);
+            dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //ALOGI("DexOpt: execute-inline/range %s.%s --> %s.%s",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * Returns "true" if the return-void instructions in this method should
+ * be converted to return-void-barrier.
+ *
+ * This is needed to satisfy a Java Memory Model requirement regarding
+ * the construction of objects with final fields.  (This does not apply
+ * to <clinit> or static fields, since appropriate barriers are guaranteed
+ * by the class initialization process.)
+ */
+static bool needsReturnBarrier(Method* method)
+{
+    if (!gDvm.dexOptForSmp)
+        return false;
+    if (strcmp(method->name, "<init>") != 0)
+        return false;
+
+    /*
+     * Check to see if the class is finalizable.  The loader sets a flag
+     * if the class or one of its superclasses overrides finalize().
+     */
+    const ClassObject* clazz = method->clazz;
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE))
+        return true;
+
+    /*
+     * Check to see if the class has any final fields.  If not, we don't
+     * need to generate a barrier instruction.
+     *
+     * In theory, we only need to do this if the method actually modifies
+     * a final field.  In practice, non-constructor methods are allowed
+     * to modify final fields, and there are 3rd-party tools that rely on
+     * this behavior.  (The compiler does not allow it, but the VM does.)
+     *
+     * If we alter the verifier to restrict final-field updates to
+     * constructors, we can tighten this up as well.
+     */
+    int idx = clazz->ifieldCount;
+    while (--idx >= 0) {
+        if (dvmIsFinalField(&clazz->ifields[idx]))
+            return true;
+    }
+
+    return false;
+}
+
+/*
+ * Convert a return-void to a return-void-barrier.
+ */
+static void rewriteReturnVoid(Method* method, u2* insns)
+{
+    assert((insns[0] & 0xff) == OP_RETURN_VOID);
+    updateOpcode(method, insns, OP_RETURN_VOID_BARRIER);
+}
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..e1825d0
--- /dev/null
+++ b/vm/analysis/Optimize.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef DALVIK_OPTIMIZE_H_
+#define DALVIK_OPTIMIZE_H_
+
+/*
+ * Entry point from DEX preparation.
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
+
+/*
+ * Update a 16-bit code unit.
+ */
+void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal);
+
+/*
+ * Abbreviated resolution functions, for use by optimization and verification
+ * code.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+    VerifyError* pFailure);
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+    MethodType methodType, VerifyError* pFailure);
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+    VerifyError* pFailure);
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+    VerifyError* pFailure);
+
+#endif  // DALVIK_OPTIMIZE_H_
diff --git a/vm/analysis/RegisterMap.cpp b/vm/analysis/RegisterMap.cpp
new file mode 100644
index 0000000..197fb7a
--- /dev/null
+++ b/vm/analysis/RegisterMap.cpp
@@ -0,0 +1,1835 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This code generate "register maps" for Dalvik bytecode.  In a stack-based
+ * VM we might call these "stack maps".  They are used to increase the
+ * precision in the garbage collector when scanning references in the
+ * interpreter thread stacks.
+ */
+#include "Dalvik.h"
+#include "UniquePtr.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/Leb128.h"
+
+#include <stddef.h>
+
+/* double-check the compression */
+#define REGISTER_MAP_VERIFY     false
+
+/* verbose logging */
+#define REGISTER_MAP_VERBOSE    false
+
+//#define REGISTER_MAP_STATS
+
+// fwd
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data);
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap);
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2);
+
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method);
+#endif
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,\
+    const Method* meth);
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap);
+
+#ifdef REGISTER_MAP_STATS
+/*
+ * Generate some statistics on the register maps we create and use.
+ */
+#define kMaxGcPointGap      50
+#define kUpdatePosnMinRegs  24
+#define kNumUpdatePosns     8
+#define kMaxDiffBits        20
+struct MapStats {
+    /*
+     * Buckets measuring the distance between GC points.  This tells us how
+     * many bits we need to encode the advancing program counter.  We ignore
+     * some of the "long tail" entries.
+     */
+    int gcPointGap[kMaxGcPointGap];
+
+    /*
+     * Number of gaps.  Equal to (number of gcPoints - number of methods),
+     * since the computation isn't including the initial gap.
+     */
+    int gcGapCount;
+
+    /*
+     * Number of gaps.
+     */
+    int totalGcPointCount;
+
+    /*
+     * For larger methods (>= 24 registers), measure in which octant register
+     * updates occur.  This should help us understand whether register
+     * changes tend to cluster in the low regs even for large methods.
+     */
+    int updatePosn[kNumUpdatePosns];
+
+    /*
+     * For all methods, count up the number of changes to registers < 16
+     * and >= 16.
+     */
+    int updateLT16;
+    int updateGE16;
+
+    /*
+     * Histogram of the number of bits that differ between adjacent entries.
+     */
+    int numDiffBits[kMaxDiffBits];
+
+
+    /*
+     * Track the number of expanded maps, and the heap space required to
+     * hold them.
+     */
+    int numExpandedMaps;
+    int totalExpandedMapSize;
+};
+#endif
+
+/*
+ * Prepare some things.
+ */
+bool dvmRegisterMapStartup()
+{
+#ifdef REGISTER_MAP_STATS
+    MapStats* pStats = calloc(1, sizeof(MapStats));
+    gDvm.registerMapStats = pStats;
+#endif
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmRegisterMapShutdown()
+{
+#ifdef REGISTER_MAP_STATS
+    free(gDvm.registerMapStats);
+#endif
+}
+
+/*
+ * Write stats to log file.
+ */
+void dvmRegisterMapDumpStats()
+{
+#ifdef REGISTER_MAP_STATS
+    MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+    int i, end;
+
+    for (end = kMaxGcPointGap-1; end >= 0; end--) {
+        if (pStats->gcPointGap[end] != 0)
+            break;
+    }
+
+    ALOGI("Register Map gcPointGap stats (diff count=%d, total=%d):",
+        pStats->gcGapCount, pStats->totalGcPointCount);
+    assert(pStats->gcPointGap[0] == 0);
+    for (i = 1; i <= end; i++) {
+        ALOGI(" %2d %d", i, pStats->gcPointGap[i]);
+    }
+
+
+    for (end = kMaxDiffBits-1; end >= 0; end--) {
+        if (pStats->numDiffBits[end] != 0)
+            break;
+    }
+
+    ALOGI("Register Map bit difference stats:");
+    for (i = 0; i <= end; i++) {
+        ALOGI(" %2d %d", i, pStats->numDiffBits[i]);
+    }
+
+
+    ALOGI("Register Map update position stats (lt16=%d ge16=%d):",
+        pStats->updateLT16, pStats->updateGE16);
+    for (i = 0; i < kNumUpdatePosns; i++) {
+        ALOGI(" %2d %d", i, pStats->updatePosn[i]);
+    }
+#endif
+}
+
+
+/*
+ * ===========================================================================
+ *      Map generation
+ * ===========================================================================
+ */
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * For type-precise determination we have all the data we need, so we
+ * just need to encode it in some clever fashion.
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    RegisterMap* pMap = NULL;
+    RegisterMap* pResult = NULL;
+    RegisterMapFormat format;
+    u1 regWidth;
+    u1* mapData;
+    int i, bytesForAddr, gcPointCount;
+    int bufSize;
+
+    if (vdata->method->registersSize >= 2048) {
+        ALOGE("ERROR: register map can't handle %d registers",
+            vdata->method->registersSize);
+        goto bail;
+    }
+    regWidth = (vdata->method->registersSize + 7) / 8;
+
+    /*
+     * Decide if we need 8 or 16 bits to hold the address.  Strictly speaking
+     * we only need 16 bits if we actually encode an address >= 256 -- if
+     * the method has a section at the end without GC points (e.g. array
+     * data) we don't need to count it.  The situation is unusual, and
+     * detecting it requires scanning the entire method, so we don't bother.
+     */
+    if (vdata->insnsSize < 256) {
+        format = kRegMapFormatCompact8;
+        bytesForAddr = 1;
+    } else {
+        format = kRegMapFormatCompact16;
+        bytesForAddr = 2;
+    }
+
+    /*
+     * Count up the number of GC point instructions.
+     *
+     * NOTE: this does not automatically include the first instruction,
+     * since we don't count method entry as a GC point.
+     */
+    gcPointCount = 0;
+    for (i = 0; i < (int) vdata->insnsSize; i++) {
+        if (dvmInsnIsGcPoint(vdata->insnFlags, i))
+            gcPointCount++;
+    }
+    if (gcPointCount >= 65536) {
+        /* we could handle this, but in practice we don't get near this */
+        ALOGE("ERROR: register map can't handle %d gc points in one method",
+            gcPointCount);
+        goto bail;
+    }
+
+    /*
+     * Allocate a buffer to hold the map data.
+     */
+    bufSize = kHeaderSize + gcPointCount * (bytesForAddr + regWidth);
+
+    ALOGV("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)",
+        vdata->method->clazz->descriptor, vdata->method->name,
+        bytesForAddr, gcPointCount, regWidth, bufSize);
+
+    pMap = (RegisterMap*) malloc(bufSize);
+    dvmRegisterMapSetFormat(pMap, format);
+    dvmRegisterMapSetOnHeap(pMap, true);
+    dvmRegisterMapSetRegWidth(pMap, regWidth);
+    dvmRegisterMapSetNumEntries(pMap, gcPointCount);
+
+    /*
+     * Populate it.
+     */
+    mapData = pMap->data;
+    for (i = 0; i < (int) vdata->insnsSize; i++) {
+        if (dvmInsnIsGcPoint(vdata->insnFlags, i)) {
+            assert(vdata->registerLines[i].regTypes != NULL);
+            if (format == kRegMapFormatCompact8) {
+                *mapData++ = i;
+            } else /*kRegMapFormatCompact16*/ {
+                *mapData++ = i & 0xff;
+                *mapData++ = i >> 8;
+            }
+            outputTypeVector(vdata->registerLines[i].regTypes,
+                vdata->insnRegCount, mapData);
+            mapData += regWidth;
+        }
+    }
+
+    ALOGV("mapData=%p pMap=%p bufSize=%d", mapData, pMap, bufSize);
+    assert(mapData - (const u1*) pMap == bufSize);
+
+    if (REGISTER_MAP_VERIFY && !verifyMap(vdata, pMap))
+        goto bail;
+#ifdef REGISTER_MAP_STATS
+    computeMapStats(pMap, vdata->method);
+#endif
+
+    /*
+     * Try to compress the map.
+     */
+    RegisterMap* pCompMap;
+
+    pCompMap = compressMapDifferential(pMap, vdata->method);
+    if (pCompMap != NULL) {
+        if (REGISTER_MAP_VERIFY) {
+            /*
+             * Expand the compressed map we just created, and compare it
+             * to the original.  Abort the VM if it doesn't match up.
+             */
+            RegisterMap* pUncompMap;
+            pUncompMap = uncompressMapDifferential(pCompMap);
+            if (pUncompMap == NULL) {
+                ALOGE("Map failed to uncompress - %s.%s",
+                    vdata->method->clazz->descriptor,
+                    vdata->method->name);
+                free(pCompMap);
+                /* bad - compression is broken or we're out of memory */
+                dvmAbort();
+            } else {
+                if (compareMaps(pMap, pUncompMap) != 0) {
+                    ALOGE("Map comparison failed - %s.%s",
+                        vdata->method->clazz->descriptor,
+                        vdata->method->name);
+                    free(pCompMap);
+                    /* bad - compression is broken */
+                    dvmAbort();
+                }
+
+                /* verify succeeded */
+                delete pUncompMap;
+            }
+        }
+
+        if (REGISTER_MAP_VERBOSE) {
+            ALOGD("Good compress on %s.%s",
+                vdata->method->clazz->descriptor,
+                vdata->method->name);
+        }
+        free(pMap);
+        pMap = pCompMap;
+    } else {
+        if (REGISTER_MAP_VERBOSE) {
+            ALOGD("Unable to compress %s.%s (ent=%d rw=%d)",
+                vdata->method->clazz->descriptor,
+                vdata->method->name,
+                dvmRegisterMapGetNumEntries(pMap),
+                dvmRegisterMapGetRegWidth(pMap));
+        }
+    }
+
+    pResult = pMap;
+
+bail:
+    return pResult;
+}
+
+/*
+ * Release the storage held by a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap)
+{
+    if (pMap == NULL)
+        return;
+
+    assert(dvmRegisterMapGetOnHeap(pMap));
+    free(pMap);
+}
+
+/*
+ * Determine if the RegType value is a reference type.
+ *
+ * Ordinarily we include kRegTypeZero in the "is it a reference"
+ * check.  There's no value in doing so here, because we know
+ * the register can't hold anything but zero.
+ */
+static inline bool isReferenceType(RegType type)
+{
+    return (type > kRegTypeMAX || type == kRegTypeUninit);
+}
+
+/*
+ * Given a line of registers, output a bit vector that indicates whether
+ * or not the register holds a reference type (which could be null).
+ *
+ * We use '1' to indicate it's a reference, '0' for anything else (numeric
+ * value, uninitialized data, merge conflict).  Register 0 will be found
+ * in the low bit of the first byte.
+ */
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data)
+{
+    u1 val = 0;
+    int i;
+
+    for (i = 0; i < insnRegCount; i++) {
+        RegType type = *regs++;
+        val >>= 1;
+        if (isReferenceType(type))
+            val |= 0x80;        /* set hi bit */
+
+        if ((i & 0x07) == 7)
+            *data++ = val;
+    }
+    if ((i & 0x07) != 0) {
+        /* flush bits from last byte */
+        val >>= 8 - (i & 0x07);
+        *data++ = val;
+    }
+}
+
+/*
+ * Print the map as a series of binary strings.
+ *
+ * Pass in method->registersSize if known, or -1 if not.
+ */
+static void dumpRegisterMap(const RegisterMap* pMap, int registersSize)
+{
+    const u1* rawMap = pMap->data;
+    const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+    const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+    const int regWidth = dvmRegisterMapGetRegWidth(pMap);
+    int addrWidth;
+
+    switch (format) {
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        /* can't happen */
+        ALOGE("Can only dump Compact8 / Compact16 maps (not %d)", format);
+        return;
+    }
+
+    if (registersSize < 0)
+        registersSize = 8 * regWidth;
+    assert(registersSize <= regWidth * 8);
+
+    int ent;
+    for (ent = 0; ent < numEntries; ent++) {
+        int i, addr;
+
+        addr = *rawMap++;
+        if (addrWidth > 1)
+            addr |= (*rawMap++) << 8;
+
+        const u1* dataStart = rawMap;
+        u1 val = 0;
+
+        /* create binary string */
+        char outBuf[registersSize +1];
+        for (i = 0; i < registersSize; i++) {
+            val >>= 1;
+            if ((i & 0x07) == 0)
+                val = *rawMap++;
+
+            outBuf[i] = '0' + (val & 0x01);
+        }
+        outBuf[i] = '\0';
+
+        /* back up and create hex dump */
+        char hexBuf[regWidth * 3 +1];
+        char* cp = hexBuf;
+        rawMap = dataStart;
+        for (i = 0; i < regWidth; i++) {
+            sprintf(cp, " %02x", *rawMap++);
+            cp += 3;
+        }
+        hexBuf[i * 3] = '\0';
+
+        ALOGD("  %04x %s %s", addr, outBuf, hexBuf);
+    }
+}
+
+/*
+ * Double-check the map.
+ *
+ * We run through all of the data in the map, and compare it to the original.
+ * Only works on uncompressed data.
+ */
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap)
+{
+    const u1* rawMap = pMap->data;
+    const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+    const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+    int ent;
+    bool dumpMap = false;
+
+    if (false) {
+        const char* cd = "Landroid/net/http/Request;";
+        const char* mn = "readResponse";
+        if (strcmp(vdata->method->clazz->descriptor, cd) == 0 &&
+            strcmp(vdata->method->name, mn) == 0)
+        {
+            char* desc;
+            desc = dexProtoCopyMethodDescriptor(&vdata->method->prototype);
+            ALOGI("Map for %s.%s %s", vdata->method->clazz->descriptor,
+                vdata->method->name, desc);
+            free(desc);
+
+            dumpMap = true;
+        }
+    }
+
+    if ((vdata->method->registersSize + 7) / 8 != pMap->regWidth) {
+        ALOGE("GLITCH: registersSize=%d, regWidth=%d",
+            vdata->method->registersSize, pMap->regWidth);
+        return false;
+    }
+
+    for (ent = 0; ent < numEntries; ent++) {
+        int addr;
+
+        switch (format) {
+        case kRegMapFormatCompact8:
+            addr = *rawMap++;
+            break;
+        case kRegMapFormatCompact16:
+            addr = *rawMap++;
+            addr |= (*rawMap++) << 8;
+            break;
+        default:
+            /* shouldn't happen */
+            ALOGE("GLITCH: bad format (%d)", format);
+            dvmAbort();
+            /* Make compiler happy */
+            addr = 0;
+        }
+
+        const RegType* regs = vdata->registerLines[addr].regTypes;
+        if (regs == NULL) {
+            ALOGE("GLITCH: addr %d has no data", addr);
+            return false;
+        }
+
+        u1 val = 0;
+        int i;
+
+        for (i = 0; i < vdata->method->registersSize; i++) {
+            bool bitIsRef, regIsRef;
+
+            val >>= 1;
+            if ((i & 0x07) == 0) {
+                /* load next byte of data */
+                val = *rawMap++;
+            }
+
+            bitIsRef = val & 0x01;
+
+            RegType type = regs[i];
+            regIsRef = isReferenceType(type);
+
+            if (bitIsRef != regIsRef) {
+                ALOGE("GLITCH: addr %d reg %d: bit=%d reg=%d(%d)",
+                    addr, i, bitIsRef, regIsRef, type);
+                return false;
+            }
+        }
+
+        /* rawMap now points to the address field of the next entry */
+    }
+
+    if (dumpMap)
+        dumpRegisterMap(pMap, vdata->method->registersSize);
+
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      DEX generation & parsing
+ * ===========================================================================
+ */
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline u1* align32(u1* ptr)
+{
+    return (u1*) (((int) ptr + 3) & ~0x03);
+}
+
+/*
+ * Compute the size, in bytes, of a register map.
+ */
+static size_t computeRegisterMapSize(const RegisterMap* pMap)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    assert(pMap != NULL);
+
+    switch (format) {
+    case kRegMapFormatNone:
+        return 1;
+    case kRegMapFormatCompact8:
+        return kHeaderSize + (1 + pMap->regWidth) * numEntries;
+    case kRegMapFormatCompact16:
+        return kHeaderSize + (2 + pMap->regWidth) * numEntries;
+    case kRegMapFormatDifferential:
+        {
+            /* kHeaderSize + decoded ULEB128 length */
+            const u1* ptr = pMap->data;
+            int len = readUnsignedLeb128(&ptr);
+            return len + (ptr - (u1*) pMap);
+        }
+    default:
+        ALOGE("Bad register map format %d", format);
+        dvmAbort();
+        return 0;
+    }
+}
+
+/*
+ * Output the map for a single method, if it has one.
+ *
+ * Abstract and native methods have no map.  All others are expected to
+ * have one, since we know the class verified successfully.
+ *
+ * This strips the "allocated on heap" flag from the format byte, so that
+ * direct-mapped maps are correctly identified as such.
+ */
+static bool writeMapForMethod(const Method* meth, u1** pPtr)
+{
+    if (meth->registerMap == NULL) {
+        if (!dvmIsAbstractMethod(meth) && !dvmIsNativeMethod(meth)) {
+            ALOGW("Warning: no map available for %s.%s",
+                meth->clazz->descriptor, meth->name);
+            /* weird, but keep going */
+        }
+        *(*pPtr)++ = kRegMapFormatNone;
+        return true;
+    }
+
+    /* serialize map into the buffer */
+    size_t mapSize = computeRegisterMapSize(meth->registerMap);
+    memcpy(*pPtr, meth->registerMap, mapSize);
+
+    /* strip the "on heap" flag out of the format byte, which is always first */
+    assert(**pPtr == meth->registerMap->format);
+    **pPtr &= ~(kRegMapFormatOnHeap);
+
+    *pPtr += mapSize;
+
+    return true;
+}
+
+/*
+ * Write maps for all methods in the specified class to the buffer, which
+ * can hold at most "length" bytes.  "*pPtr" will be advanced past the end
+ * of the data we write.
+ */
+static bool writeMapsAllMethods(DvmDex* pDvmDex, const ClassObject* clazz,
+    u1** pPtr, size_t length)
+{
+    RegisterMapMethodPool* pMethodPool;
+    u1* ptr = *pPtr;
+    int i, methodCount;
+
+    /* artificial limit */
+    if (clazz->virtualMethodCount + clazz->directMethodCount >= 65536) {
+        ALOGE("Too many methods in %s", clazz->descriptor);
+        return false;
+    }
+
+    pMethodPool = (RegisterMapMethodPool*) ptr;
+    ptr += offsetof(RegisterMapMethodPool, methodData);
+    methodCount = 0;
+
+    /*
+     * Run through all methods, direct then virtual.  The class loader will
+     * traverse them in the same order.  (We could split them into two
+     * distinct pieces, but there doesn't appear to be any value in doing
+     * so other than that it makes class loading slightly less fragile.)
+     *
+     * The class loader won't know about miranda methods at the point
+     * where it parses this, so we omit those.
+     *
+     * TODO: consider omitting all native/abstract definitions.  Should be
+     * safe, though we lose the ability to sanity-check against the
+     * method counts in the DEX file.
+     */
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        const Method* meth = &clazz->directMethods[i];
+        if (dvmIsMirandaMethod(meth))
+            continue;
+        if (!writeMapForMethod(&clazz->directMethods[i], &ptr)) {
+            return false;
+        }
+        methodCount++;
+        //ptr = align32(ptr);
+    }
+
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        const Method* meth = &clazz->virtualMethods[i];
+        if (dvmIsMirandaMethod(meth))
+            continue;
+        if (!writeMapForMethod(&clazz->virtualMethods[i], &ptr)) {
+            return false;
+        }
+        methodCount++;
+        //ptr = align32(ptr);
+    }
+
+    pMethodPool->methodCount = methodCount;
+
+    *pPtr = ptr;
+    return true;
+}
+
+/*
+ * Write maps for all classes to the specified buffer, which can hold at
+ * most "length" bytes.
+ *
+ * Returns the actual length used, or 0 on failure.
+ */
+static size_t writeMapsAllClasses(DvmDex* pDvmDex, u1* basePtr, size_t length)
+{
+    DexFile* pDexFile = pDvmDex->pDexFile;
+    u4 count = pDexFile->pHeader->classDefsSize;
+    RegisterMapClassPool* pClassPool;
+    u4* offsetTable;
+    u1* ptr = basePtr;
+    u4 idx;
+
+    assert(gDvm.optimizing);
+
+    pClassPool = (RegisterMapClassPool*) ptr;
+    ptr += offsetof(RegisterMapClassPool, classDataOffset);
+    offsetTable = (u4*) ptr;
+    ptr += count * sizeof(u4);
+
+    pClassPool->numClasses = count;
+
+    /*
+     * We want an entry for every class, loaded or not.
+     */
+    for (idx = 0; idx < count; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+        ClassObject* clazz;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        /*
+         * All classes have been loaded into the bootstrap class loader.
+         * If we can find it, and it was successfully pre-verified, we
+         * run through its methods and add the register maps.
+         *
+         * If it wasn't pre-verified then we know it can't have any
+         * register maps.  Classes that can't be loaded or failed
+         * verification get an empty slot in the index.
+         */
+        clazz = NULL;
+        if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) != 0)
+            clazz = dvmLookupClass(classDescriptor, NULL, false);
+
+        if (clazz != NULL) {
+            offsetTable[idx] = ptr - basePtr;
+            LOGVV("%d -> offset %d (%p-%p)",
+                idx, offsetTable[idx], ptr, basePtr);
+
+            if (!writeMapsAllMethods(pDvmDex, clazz, &ptr,
+                    length - (ptr - basePtr)))
+            {
+                return 0;
+            }
+
+            ptr = align32(ptr);
+            LOGVV("Size %s (%d+%d methods): %d", clazz->descriptor,
+                clazz->directMethodCount, clazz->virtualMethodCount,
+                (ptr - basePtr) - offsetTable[idx]);
+        } else {
+            ALOGV("%4d NOT mapadding '%s'", idx, classDescriptor);
+            assert(offsetTable[idx] == 0);
+        }
+    }
+
+    if (ptr - basePtr >= (int)length) {
+        /* a bit late */
+        ALOGE("Buffer overrun");
+        dvmAbort();
+    }
+
+    return ptr - basePtr;
+}
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex)
+{
+    RegisterMapBuilder* pBuilder;
+
+    pBuilder = (RegisterMapBuilder*) calloc(1, sizeof(RegisterMapBuilder));
+    if (pBuilder == NULL)
+        return NULL;
+
+    /*
+     * We have a couple of options here:
+     *  (1) Compute the size of the output, and malloc a buffer.
+     *  (2) Create a "large-enough" anonymous mmap region.
+     *
+     * The nice thing about option #2 is that we don't have to traverse
+     * all of the classes and methods twice.  The risk is that we might
+     * not make the region large enough.  Since the pages aren't mapped
+     * until used we can allocate a semi-absurd amount of memory without
+     * worrying about the effect on the rest of the system.
+     *
+     * The basic encoding on the largest jar file requires about 1MB of
+     * storage.  We map out 4MB here.  (TODO: guarantee that the last
+     * page of the mapping is marked invalid, so we reliably fail if
+     * we overrun.)
+     */
+    if (sysCreatePrivateMap(4 * 1024 * 1024, &pBuilder->memMap) != 0) {
+        free(pBuilder);
+        return NULL;
+    }
+
+    /*
+     * Create the maps.
+     */
+    size_t actual = writeMapsAllClasses(pDvmDex, (u1*)pBuilder->memMap.addr,
+                                        pBuilder->memMap.length);
+    if (actual == 0) {
+        dvmFreeRegisterMapBuilder(pBuilder);
+        return NULL;
+    }
+
+    ALOGV("TOTAL size of register maps: %d", actual);
+
+    pBuilder->data = pBuilder->memMap.addr;
+    pBuilder->size = actual;
+    return pBuilder;
+}
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder)
+{
+    if (pBuilder == NULL)
+        return;
+
+    sysReleaseShmem(&pBuilder->memMap);
+    free(pBuilder);
+}
+
+
+/*
+ * Find the data for the specified class.
+ *
+ * If there's no register map data, or none for this class, we return NULL.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+    u4* pNumMaps)
+{
+    const RegisterMapClassPool* pClassPool;
+    const RegisterMapMethodPool* pMethodPool;
+
+    pClassPool = (const RegisterMapClassPool*) pDexFile->pRegisterMapPool;
+    if (pClassPool == NULL)
+        return NULL;
+
+    if (classIdx >= pClassPool->numClasses) {
+        ALOGE("bad class index (%d vs %d)", classIdx, pClassPool->numClasses);
+        dvmAbort();
+    }
+
+    u4 classOffset = pClassPool->classDataOffset[classIdx];
+    if (classOffset == 0) {
+        ALOGV("+++ no map for classIdx=%d", classIdx);
+        return NULL;
+    }
+
+    pMethodPool =
+        (const RegisterMapMethodPool*) (((u1*) pClassPool) + classOffset);
+    if (pNumMaps != NULL)
+        *pNumMaps = pMethodPool->methodCount;
+    return pMethodPool->methodData;
+}
+
+/*
+ * This advances "*pPtr" and returns its original value.
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr)
+{
+    const RegisterMap* pMap = (const RegisterMap*) *pPtr;
+
+    *pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap));
+    LOGVV("getNext: %p -> %p (f=%#x w=%d e=%d)",
+        pMap, *pPtr, pMap->format, pMap->regWidth,
+        dvmRegisterMapGetNumEntries(pMap));
+    return pMap;
+}
+
+
+/*
+ * ===========================================================================
+ *      Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Return the data for the specified address, or NULL if not found.
+ *
+ * The result must be released with dvmReleaseRegisterMapLine().
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr)
+{
+    int addrWidth, lineWidth;
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    assert(numEntries > 0);
+
+    switch (format) {
+    case kRegMapFormatNone:
+        return NULL;
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        ALOGE("Unknown format %d", format);
+        dvmAbort();
+        return NULL;
+    }
+
+    lineWidth = addrWidth + pMap->regWidth;
+
+    /*
+     * Find the appropriate entry.  Many maps are very small, some are very
+     * large.
+     */
+    static const int kSearchThreshold = 8;
+    const u1* data = NULL;
+    int lineAddr;
+
+    if (numEntries < kSearchThreshold) {
+        int i;
+        data = pMap->data;
+        for (i = numEntries; i > 0; i--) {
+            lineAddr = data[0];
+            if (addrWidth > 1)
+                lineAddr |= data[1] << 8;
+            if (lineAddr == addr)
+                return data + addrWidth;
+
+            data += lineWidth;
+        }
+        assert(data == pMap->data + lineWidth * numEntries);
+    } else {
+        int hi, lo, mid;
+
+        lo = 0;
+        hi = numEntries -1;
+
+        while (hi >= lo) {
+            mid = (hi + lo) / 2;
+            data = pMap->data + lineWidth * mid;
+
+            lineAddr = data[0];
+            if (addrWidth > 1)
+                lineAddr |= data[1] << 8;
+
+            if (addr > lineAddr) {
+                lo = mid + 1;
+            } else if (addr < lineAddr) {
+                hi = mid - 1;
+            } else {
+                return data + addrWidth;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Compare two register maps.
+ *
+ * Returns 0 if they're equal, nonzero if not.
+ */
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2)
+{
+    size_t size1, size2;
+
+    size1 = computeRegisterMapSize(pMap1);
+    size2 = computeRegisterMapSize(pMap2);
+    if (size1 != size2) {
+        ALOGI("compareMaps: size mismatch (%zd vs %zd)", size1, size2);
+        return -1;
+    }
+
+    if (memcmp(pMap1, pMap2, size1) != 0) {
+        ALOGI("compareMaps: content mismatch");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Get the expanded form of the register map associated with the method.
+ *
+ * If the map is already in one of the uncompressed formats, we return
+ * immediately.  Otherwise, we expand the map and replace method's register
+ * map pointer, freeing it if it was allocated on the heap.
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory
+ * (unless we're in the zygote, where single-threaded access is guaranteed).
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method)
+{
+    const RegisterMap* curMap = method->registerMap;
+    RegisterMap* newMap;
+
+    if (curMap == NULL)
+        return NULL;
+
+    /* sanity check to ensure this isn't called w/o external locking */
+    /* (if we use this at a time other than during GC, fix/remove this test) */
+    if (true) {
+        if (!gDvm.zygote && dvmTryLockMutex(&gDvm.gcHeapLock) == 0) {
+            ALOGE("GLITCH: dvmGetExpandedRegisterMap not called at GC time");
+            dvmAbort();
+        }
+    }
+
+    RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+    switch (format) {
+    case kRegMapFormatCompact8:
+    case kRegMapFormatCompact16:
+        if (REGISTER_MAP_VERBOSE) {
+            if (dvmRegisterMapGetOnHeap(curMap)) {
+                ALOGD("RegMap: already expanded: %s.%s",
+                    method->clazz->descriptor, method->name);
+            } else {
+                ALOGD("RegMap: stored w/o compression: %s.%s",
+                    method->clazz->descriptor, method->name);
+            }
+        }
+        return curMap;
+    case kRegMapFormatDifferential:
+        newMap = uncompressMapDifferential(curMap);
+        break;
+    default:
+        ALOGE("Unknown format %d in dvmGetExpandedRegisterMap", format);
+        dvmAbort();
+        newMap = NULL;      // make gcc happy
+    }
+
+    if (newMap == NULL) {
+        ALOGE("Map failed to uncompress (fmt=%d) %s.%s",
+            format, method->clazz->descriptor, method->name);
+        return NULL;
+    }
+
+#ifdef REGISTER_MAP_STATS
+    /*
+     * Gather and display some stats.
+     */
+    {
+        MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+        pStats->numExpandedMaps++;
+        pStats->totalExpandedMapSize += computeRegisterMapSize(newMap);
+        ALOGD("RMAP: count=%d size=%d",
+            pStats->numExpandedMaps, pStats->totalExpandedMapSize);
+    }
+#endif
+
+    IF_ALOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGV("Expanding map -> %s.%s:%s",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    /*
+     * Update method, and free compressed map if it was sitting on the heap.
+     */
+    dvmSetRegisterMap(method, newMap);
+
+    if (dvmRegisterMapGetOnHeap(curMap))
+        dvmFreeRegisterMap((RegisterMap*) curMap);
+
+    return newMap;
+}
+
+
+/*
+ * ===========================================================================
+ *      Map compression
+ * ===========================================================================
+ */
+
+/*
+Notes on map compression
+
+The idea is to create a compressed form that will be uncompressed before
+use, with the output possibly saved in a cache.  This means we can use an
+approach that is unsuited for random access if we choose.
+
+In the event that a map simply does not work with our compression scheme,
+it's reasonable to store the map without compression.  In the future we
+may want to have more than one compression scheme, and try each in turn,
+retaining the best.  (We certainly want to keep the uncompressed form if it
+turns out to be smaller or even slightly larger than the compressed form.)
+
+Each entry consists of an address and a bit vector.  Adjacent entries are
+strongly correlated, suggesting differential encoding.
+
+
+Ideally we would avoid outputting adjacent entries with identical
+bit vectors.  However, the register values at a given address do not
+imply anything about the set of valid registers at subsequent addresses.
+We therefore cannot omit an entry.
+
+  If the thread stack has a PC at an address without a corresponding
+  entry in the register map, we must conservatively scan the registers in
+  that thread.  This can happen when single-stepping in the debugger,
+  because the debugger is allowed to invoke arbitrary methods when
+  a thread is stopped at a breakpoint.  If we can guarantee that a GC
+  thread scan will never happen while the debugger has that thread stopped,
+  then we can lift this restriction and simply omit entries that don't
+  change the bit vector from its previous state.
+
+Each entry advances the address value by at least 1 (measured in 16-bit
+"code units").  Looking at the bootclasspath entries, advancing by 2 units
+is most common.  Advances by 1 unit are far less common than advances by
+2 units, but more common than 5, and things fall off rapidly.  Gaps of
+up to 220 code units appear in some computationally intensive bits of code,
+but are exceedingly rare.
+
+If we sum up the number of transitions in a couple of ranges in framework.jar:
+  [1,4]: 188998 of 218922 gaps (86.3%)
+  [1,7]: 211647 of 218922 gaps (96.7%)
+Using a 3-bit delta, with one value reserved as an escape code, should
+yield good results for the address.
+
+These results would change dramatically if we reduced the set of GC
+points by e.g. removing instructions like integer divide that are only
+present because they can throw and cause an allocation.
+
+We also need to include an "initial gap", because the first few instructions
+in a method may not be GC points.
+
+
+By observation, many entries simply repeat the previous bit vector, or
+change only one or two bits.  (This is with type-precise information;
+the rate of change of bits will be different if live-precise information
+is factored in).
+
+Looking again at adjacent entries in framework.jar:
+  0 bits changed: 63.0%
+  1 bit changed: 32.2%
+After that it falls off rapidly, e.g. the number of entries with 2 bits
+changed is usually less than 1/10th of the number of entries with 1 bit
+changed.  A solution that allows us to encode 0- or 1- bit changes
+efficiently will do well.
+
+We still need to handle cases where a large number of bits change.  We
+probably want a way to drop in a full copy of the bit vector when it's
+smaller than the representation of multiple bit changes.
+
+
+The bit-change information can be encoded as an index that tells the
+decoder to toggle the state.  We want to encode the index in as few bits
+as possible, but we need to allow for fairly wide vectors (e.g. we have a
+method with 175 registers).  We can deal with this in a couple of ways:
+(1) use an encoding that assumes few registers and has an escape code
+for larger numbers of registers; or (2) use different encodings based
+on how many total registers the method has.  The choice depends to some
+extent on whether methods with large numbers of registers tend to modify
+the first 16 regs more often than the others.
+
+The last N registers hold method arguments.  If the bytecode is expected
+to be examined in a debugger, "dx" ensures that the contents of these
+registers won't change.  Depending upon the encoding format, we may be
+able to take advantage of this.  We still have to encode the initial
+state, but we know we'll never have to output a bit change for the last
+N registers.
+
+Considering only methods with 16 or more registers, the "target octant"
+for register changes looks like this:
+  [ 43.1%, 16.4%, 6.5%, 6.2%, 7.4%, 8.8%, 9.7%, 1.8% ]
+As expected, there are fewer changes at the end of the list where the
+arguments are kept, and more changes at the start of the list because
+register values smaller than 16 can be used in compact Dalvik instructions
+and hence are favored for frequently-used values.  In general, the first
+octant is considerably more active than later entries, the last octant
+is much less active, and the rest are all about the same.
+
+Looking at all bit changes in all methods, 94% are to registers 0-15.  The
+encoding will benefit greatly by favoring the low-numbered registers.
+
+
+Some of the smaller methods have identical maps, and space could be
+saved by simply including a pointer to an earlier definition.  This would
+be best accomplished by specifying a "pointer" format value, followed by
+a 3-byte (or ULEB128) offset.  Implementing this would probably involve
+generating a hash value for each register map and maintaining a hash table.
+
+In some cases there are repeating patterns in the bit vector that aren't
+adjacent.  These could benefit from a dictionary encoding.  This doesn't
+really become useful until the methods reach a certain size though,
+and managing the dictionary may incur more overhead than we want.
+
+Large maps can be compressed significantly.  The trouble is that, when
+we need to use them, we have to uncompress them onto the heap.  We may
+get a better trade-off between storage size and heap usage by refusing to
+compress large maps, so that they can be memory mapped and used directly.
+(OTOH, only about 2% of the maps will ever actually be used.)
+
+
+----- differential format -----
+
+// common header
++00 1B format
++01 1B regWidth
++02 2B numEntries (little-endian)
++04 nB length in bytes of the data that follows, in ULEB128 format
+       (not strictly necessary; allows determination of size w/o full parse)
++05+ 1B initial address (0-127), high bit set if max addr >= 256
++06+ nB initial value for bit vector
+
+// for each entry
++00: CCCCBAAA
+
+  AAA: address difference.  Values from 0 to 6 indicate an increment of 1
+  to 7.  A value of 7 indicates that the address difference is large,
+  and the next byte is a ULEB128-encoded difference value.
+
+  B: determines the meaning of CCCC.
+
+  CCCC: if B is 0, this is the number of the bit to toggle (0-15).
+  If B is 1, this is a count of the number of changed bits (1-14).  A value
+  of 0 means that no bits were changed, and a value of 15 indicates
+  that enough bits were changed that it required less space to output
+  the entire bit vector.
+
++01: (optional) ULEB128-encoded address difference
+
++01+: (optional) one or more ULEB128-encoded bit numbers, OR the entire
+  bit vector.
+
+The most common situation is an entry whose address has changed by 2-4
+code units, has no changes or just a single bit change, and the changed
+register is less than 16.  We should therefore be able to encode a large
+number of entries with a single byte, which is half the size of the
+Compact8 encoding method.
+*/
+
+/*
+ * Compute some stats on an uncompressed register map.
+ */
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method)
+{
+    MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+    const u1 format = dvmRegisterMapGetFormat(pMap);
+    const u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+    const u1* rawMap = pMap->data;
+    const u1* prevData = NULL;
+    int ent, addr, prevAddr = -1;
+
+    for (ent = 0; ent < numEntries; ent++) {
+        switch (format) {
+        case kRegMapFormatCompact8:
+            addr = *rawMap++;
+            break;
+        case kRegMapFormatCompact16:
+            addr = *rawMap++;
+            addr |= (*rawMap++) << 8;
+            break;
+        default:
+            /* shouldn't happen */
+            ALOGE("GLITCH: bad format (%d)", format);
+            dvmAbort();
+        }
+
+        const u1* dataStart = rawMap;
+
+        pStats->totalGcPointCount++;
+
+        /*
+         * Gather "gap size" stats, i.e. the difference in addresses between
+         * successive GC points.
+         */
+        if (prevData != NULL) {
+            assert(prevAddr >= 0);
+            int addrDiff = addr - prevAddr;
+
+            if (addrDiff < 0) {
+                ALOGE("GLITCH: address went backward (0x%04x->0x%04x, %s.%s)",
+                    prevAddr, addr, method->clazz->descriptor, method->name);
+            } else if (addrDiff > kMaxGcPointGap) {
+                if (REGISTER_MAP_VERBOSE) {
+                    ALOGI("HEY: addrDiff is %d, max %d (0x%04x->0x%04x %s.%s)",
+                        addrDiff, kMaxGcPointGap, prevAddr, addr,
+                        method->clazz->descriptor, method->name);
+                }
+                /* skip this one */
+            } else {
+                pStats->gcPointGap[addrDiff]++;
+            }
+            pStats->gcGapCount++;
+
+
+            /*
+             * Compare bit vectors in adjacent entries.  We want to count
+             * up the number of bits that differ (to see if we frequently
+             * change 0 or 1 bits) and get a sense for which part of the
+             * vector changes the most often (near the start, middle, end).
+             *
+             * We only do the vector position quantization if we have at
+             * least 16 registers in the method.
+             */
+            int numDiff = 0;
+            float div = (float) kNumUpdatePosns / method->registersSize;
+            int regByte;
+            for (regByte = 0; regByte < pMap->regWidth; regByte++) {
+                int prev, cur, bit;
+
+                prev = prevData[regByte];
+                cur = dataStart[regByte];
+
+                for (bit = 0; bit < 8; bit++) {
+                    if (((prev >> bit) & 1) != ((cur >> bit) & 1)) {
+                        numDiff++;
+
+                        int bitNum = regByte * 8 + bit;
+
+                        if (bitNum < 16)
+                            pStats->updateLT16++;
+                        else
+                            pStats->updateGE16++;
+
+                        if (method->registersSize < 16)
+                            continue;
+
+                        if (bitNum >= method->registersSize) {
+                            /* stuff off the end should be zero in both */
+                            ALOGE("WEIRD: bit=%d (%d/%d), prev=%02x cur=%02x",
+                                bit, regByte, method->registersSize,
+                                prev, cur);
+                            assert(false);
+                        }
+                        int idx = (int) (bitNum * div);
+                        if (!(idx >= 0 && idx < kNumUpdatePosns)) {
+                            ALOGE("FAIL: bitNum=%d (of %d) div=%.3f idx=%d",
+                                bitNum, method->registersSize, div, idx);
+                            assert(false);
+                        }
+                        pStats->updatePosn[idx]++;
+                    }
+                }
+            }
+
+            if (numDiff > kMaxDiffBits) {
+                if (REGISTER_MAP_VERBOSE) {
+                    ALOGI("WOW: numDiff is %d, max %d", numDiff, kMaxDiffBits);
+                }
+            } else {
+                pStats->numDiffBits[numDiff]++;
+            }
+        }
+
+        /* advance to start of next line */
+        rawMap += pMap->regWidth;
+
+        prevAddr = addr;
+        prevData = dataStart;
+    }
+}
+#endif
+
+/*
+ * Compute the difference between two bit vectors.
+ *
+ * If "lebOutBuf" is non-NULL, we output the bit indices in ULEB128 format
+ * as we go.  Otherwise, we just generate the various counts.
+ *
+ * The bit vectors are compared byte-by-byte, so any unused bits at the
+ * end must be zero.
+ *
+ * Returns the number of bytes required to hold the ULEB128 output.
+ *
+ * If "pFirstBitChanged" or "pNumBitsChanged" are non-NULL, they will
+ * receive the index of the first changed bit and the number of changed
+ * bits, respectively.
+ */
+static int computeBitDiff(const u1* bits1, const u1* bits2, int byteWidth,
+    int* pFirstBitChanged, int* pNumBitsChanged, u1* lebOutBuf)
+{
+    int numBitsChanged = 0;
+    int firstBitChanged = -1;
+    int lebSize = 0;
+    int byteNum;
+
+    /*
+     * Run through the vectors, first comparing them at the byte level.  This
+     * will yield a fairly quick result if nothing has changed between them.
+     */
+    for (byteNum = 0; byteNum < byteWidth; byteNum++) {
+        u1 byte1 = *bits1++;
+        u1 byte2 = *bits2++;
+        if (byte1 != byte2) {
+            /*
+             * Walk through the byte, identifying the changed bits.
+             */
+            int bitNum;
+            for (bitNum = 0; bitNum < 8; bitNum++) {
+                if (((byte1 >> bitNum) & 0x01) != ((byte2 >> bitNum) & 0x01)) {
+                    int bitOffset = (byteNum << 3) + bitNum;
+
+                    if (firstBitChanged < 0)
+                        firstBitChanged = bitOffset;
+                    numBitsChanged++;
+
+                    if (lebOutBuf == NULL) {
+                        lebSize += unsignedLeb128Size(bitOffset);
+                    } else {
+                        u1* curBuf = lebOutBuf;
+                        lebOutBuf = writeUnsignedLeb128(lebOutBuf, bitOffset);
+                        lebSize += lebOutBuf - curBuf;
+                    }
+                }
+            }
+        }
+    }
+
+    if (numBitsChanged > 0)
+        assert(firstBitChanged >= 0);
+
+    if (pFirstBitChanged != NULL)
+        *pFirstBitChanged = firstBitChanged;
+    if (pNumBitsChanged != NULL)
+        *pNumBitsChanged = numBitsChanged;
+
+    return lebSize;
+}
+
+/*
+ * Compress the register map with differential encoding.
+ *
+ * "meth" is only needed for debug output.
+ *
+ * On success, returns a newly-allocated RegisterMap.  If the map is not
+ * compatible for some reason, or fails to get smaller, this will return NULL.
+ */
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,
+    const Method* meth)
+{
+    RegisterMap* pNewMap = NULL;
+    int origSize = computeRegisterMapSize(pMap);
+    u1* tmpPtr;
+    int addrWidth, regWidth, numEntries;
+    bool debug = false;
+
+    if (false &&
+        strcmp(meth->clazz->descriptor, "Landroid/text/StaticLayout;") == 0 &&
+        strcmp(meth->name, "generate") == 0)
+    {
+        debug = true;
+    }
+
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    switch (format) {
+    case kRegMapFormatCompact8:
+        addrWidth = 1;
+        break;
+    case kRegMapFormatCompact16:
+        addrWidth = 2;
+        break;
+    default:
+        ALOGE("ERROR: can't compress map with format=%d", format);
+        return NULL;
+    }
+
+    regWidth = dvmRegisterMapGetRegWidth(pMap);
+    numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    if (debug) {
+        ALOGI("COMPRESS: %s.%s aw=%d rw=%d ne=%d",
+            meth->clazz->descriptor, meth->name,
+            addrWidth, regWidth, numEntries);
+        dumpRegisterMap(pMap, -1);
+    }
+
+    if (numEntries <= 1) {
+        ALOGV("Can't compress map with 0 or 1 entries");
+        return NULL;
+    }
+
+    /*
+     * We don't know how large the compressed data will be.  It's possible
+     * for it to expand and become larger than the original.  The header
+     * itself is variable-sized, so we generate everything into a temporary
+     * buffer and then copy it to form-fitting storage once we know how big
+     * it will be (and that it's smaller than the original).
+     *
+     * If we use a size that is equal to the size of the input map plus
+     * a value longer than a single entry can possibly expand to, we need
+     * only check for overflow at the end of each entry.  The worst case
+     * for a single line is (1 + <ULEB8 address> + <full copy of vector>).
+     * Addresses are 16 bits, so that's (1 + 3 + regWidth).
+     *
+     * The initial address offset and bit vector will take up less than
+     * or equal to the amount of space required when uncompressed -- large
+     * initial offsets are rejected.
+     */
+    UniquePtr<u1[]> tmpBuf(new u1[origSize + (1 + 3 + regWidth)]);
+    if (tmpBuf.get() == NULL)
+        return NULL;
+
+    tmpPtr = tmpBuf.get();
+
+    const u1* mapData = pMap->data;
+    const u1* prevBits;
+    u2 addr, prevAddr;
+
+    addr = *mapData++;
+    if (addrWidth > 1)
+        addr |= (*mapData++) << 8;
+
+    if (addr >= 128) {
+        ALOGV("Can't compress map with starting address >= 128");
+        return NULL;
+    }
+
+    /*
+     * Start by writing the initial address and bit vector data.  The high
+     * bit of the initial address is used to indicate the required address
+     * width (which the decoder can't otherwise determine without parsing
+     * the compressed data).
+     */
+    *tmpPtr++ = addr | (addrWidth > 1 ? 0x80 : 0x00);
+    memcpy(tmpPtr, mapData, regWidth);
+
+    prevBits = mapData;
+    prevAddr = addr;
+
+    tmpPtr += regWidth;
+    mapData += regWidth;
+
+    /*
+     * Loop over all following entries.
+     */
+    for (int entry = 1; entry < numEntries; entry++) {
+        int addrDiff;
+        u1 key;
+
+        /*
+         * Pull out the address and figure out how to encode it.
+         */
+        addr = *mapData++;
+        if (addrWidth > 1)
+            addr |= (*mapData++) << 8;
+
+        if (debug)
+            ALOGI(" addr=0x%04x ent=%d (aw=%d)", addr, entry, addrWidth);
+
+        addrDiff = addr - prevAddr;
+        assert(addrDiff > 0);
+        if (addrDiff < 8) {
+            /* small difference, encode in 3 bits */
+            key = addrDiff -1;          /* set 00000AAA */
+            if (debug)
+                ALOGI(" : small %d, key=0x%02x", addrDiff, key);
+        } else {
+            /* large difference, output escape code */
+            key = 0x07;                 /* escape code for AAA */
+            if (debug)
+                ALOGI(" : large %d, key=0x%02x", addrDiff, key);
+        }
+
+        int numBitsChanged, firstBitChanged, lebSize;
+
+        lebSize = computeBitDiff(prevBits, mapData, regWidth,
+            &firstBitChanged, &numBitsChanged, NULL);
+
+        if (debug) {
+            ALOGI(" : diff fbc=%d nbc=%d ls=%d (rw=%d)",
+                firstBitChanged, numBitsChanged, lebSize, regWidth);
+        }
+
+        if (numBitsChanged == 0) {
+            /* set B to 1 and CCCC to zero to indicate no bits were changed */
+            key |= 0x08;
+            if (debug) ALOGI(" : no bits changed");
+        } else if (numBitsChanged == 1 && firstBitChanged < 16) {
+            /* set B to 0 and CCCC to the index of the changed bit */
+            key |= firstBitChanged << 4;
+            if (debug) ALOGI(" : 1 low bit changed");
+        } else if (numBitsChanged < 15 && lebSize < regWidth) {
+            /* set B to 1 and CCCC to the number of bits */
+            key |= 0x08 | (numBitsChanged << 4);
+            if (debug) ALOGI(" : some bits changed");
+        } else {
+            /* set B to 1 and CCCC to 0x0f so we store the entire vector */
+            key |= 0x08 | 0xf0;
+            if (debug) ALOGI(" : encode original");
+        }
+
+        /*
+         * Encode output.  Start with the key, follow with the address
+         * diff (if it didn't fit in 3 bits), then the changed bit info.
+         */
+        *tmpPtr++ = key;
+        if ((key & 0x07) == 0x07)
+            tmpPtr = writeUnsignedLeb128(tmpPtr, addrDiff);
+
+        if ((key & 0x08) != 0) {
+            int bitCount = key >> 4;
+            if (bitCount == 0) {
+                /* nothing changed, no additional output required */
+            } else if (bitCount == 15) {
+                /* full vector is most compact representation */
+                memcpy(tmpPtr, mapData, regWidth);
+                tmpPtr += regWidth;
+            } else {
+                /* write bit indices in LEB128 format */
+                (void) computeBitDiff(prevBits, mapData, regWidth,
+                    NULL, NULL, tmpPtr);
+                tmpPtr += lebSize;
+            }
+        } else {
+            /* single-bit changed, value encoded in key byte */
+        }
+
+        prevBits = mapData;
+        prevAddr = addr;
+        mapData += regWidth;
+
+        /*
+         * See if we've run past the original size.
+         */
+        if (tmpPtr - tmpBuf.get() >= origSize) {
+            if (debug) {
+                ALOGD("Compressed size >= original (%d vs %d): %s.%s",
+                    tmpPtr - tmpBuf.get(), origSize,
+                    meth->clazz->descriptor, meth->name);
+            }
+            return NULL;
+        }
+    }
+
+    /*
+     * Create a RegisterMap with the contents.
+     *
+     * TODO: consider using a threshold other than merely ">=".  We would
+     * get poorer compression but potentially use less native heap space.
+     */
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    int newDataSize = tmpPtr - tmpBuf.get();
+    int newMapSize;
+
+    newMapSize = kHeaderSize + unsignedLeb128Size(newDataSize) + newDataSize;
+    if (newMapSize >= origSize) {
+        if (debug) {
+            ALOGD("Final comp size >= original (%d vs %d): %s.%s",
+                newMapSize, origSize, meth->clazz->descriptor, meth->name);
+        }
+        return NULL;
+    }
+
+    pNewMap = (RegisterMap*) malloc(newMapSize);
+    if (pNewMap == NULL)
+        return NULL;
+    dvmRegisterMapSetFormat(pNewMap, kRegMapFormatDifferential);
+    dvmRegisterMapSetOnHeap(pNewMap, true);
+    dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+    dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+    tmpPtr = pNewMap->data;
+    tmpPtr = writeUnsignedLeb128(tmpPtr, newDataSize);
+    memcpy(tmpPtr, tmpBuf.get(), newDataSize);
+
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGD("Compression successful (%d -> %d) from aw=%d rw=%d ne=%d",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap),
+            addrWidth, regWidth, numEntries);
+    }
+
+    return pNewMap;
+}
+
+/*
+ * Toggle the value of the "idx"th bit in "ptr".
+ */
+static inline void toggleBit(u1* ptr, int idx)
+{
+    ptr[idx >> 3] ^= 1 << (idx & 0x07);
+}
+
+/*
+ * Expand a compressed map to an uncompressed form.
+ *
+ * Returns a newly-allocated RegisterMap on success, or NULL on failure.
+ *
+ * TODO: consider using the linear allocator or a custom allocator with
+ * LRU replacement for these instead of the native heap.
+ */
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap)
+{
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    RegisterMapFormat newFormat;
+    int regWidth, numEntries, newAddrWidth, newMapSize;
+
+    if (format != kRegMapFormatDifferential) {
+        ALOGE("Not differential (%d)", format);
+        return NULL;
+    }
+
+    regWidth = dvmRegisterMapGetRegWidth(pMap);
+    numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    /* get the data size; we can check this at the end */
+    const u1* srcPtr = pMap->data;
+    int expectedSrcLen = readUnsignedLeb128(&srcPtr);
+    const u1* srcStart = srcPtr;
+
+    /* get the initial address and the 16-bit address flag */
+    int addr = *srcPtr & 0x7f;
+    if ((*srcPtr & 0x80) == 0) {
+        newFormat = kRegMapFormatCompact8;
+        newAddrWidth = 1;
+    } else {
+        newFormat = kRegMapFormatCompact16;
+        newAddrWidth = 2;
+    }
+    srcPtr++;
+
+    /* now we know enough to allocate the new map */
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGI("Expanding to map aw=%d rw=%d ne=%d",
+            newAddrWidth, regWidth, numEntries);
+    }
+    newMapSize = kHeaderSize + (newAddrWidth + regWidth) * numEntries;
+    RegisterMap* pNewMap = (RegisterMap*) malloc(newMapSize);
+
+    if (pNewMap == NULL)
+      return NULL;
+
+    dvmRegisterMapSetFormat(pNewMap, newFormat);
+    dvmRegisterMapSetOnHeap(pNewMap, true);
+    dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+    dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+    /*
+     * Write the start address and initial bits to the new map.
+     */
+    u1* dstPtr = pNewMap->data;
+
+    *dstPtr++ = addr & 0xff;
+    if (newAddrWidth > 1)
+        *dstPtr++ = (u1) (addr >> 8);
+
+    memcpy(dstPtr, srcPtr, regWidth);
+
+    int prevAddr = addr;
+    const u1* prevBits = dstPtr;    /* point at uncompressed data */
+
+    dstPtr += regWidth;
+    srcPtr += regWidth;
+
+    /*
+     * Walk through, uncompressing one line at a time.
+     */
+    int entry;
+    for (entry = 1; entry < numEntries; entry++) {
+        int addrDiff;
+        u1 key;
+
+        key = *srcPtr++;
+
+        /* get the address */
+        if ((key & 0x07) == 7) {
+            /* address diff follows in ULEB128 */
+            addrDiff = readUnsignedLeb128(&srcPtr);
+        } else {
+            addrDiff = (key & 0x07) +1;
+        }
+
+        addr = prevAddr + addrDiff;
+        *dstPtr++ = addr & 0xff;
+        if (newAddrWidth > 1)
+            *dstPtr++ = (u1) (addr >> 8);
+
+        /* unpack the bits */
+        if ((key & 0x08) != 0) {
+            int bitCount = (key >> 4);
+            if (bitCount == 0) {
+                /* no bits changed, just copy previous */
+                memcpy(dstPtr, prevBits, regWidth);
+            } else if (bitCount == 15) {
+                /* full copy of bit vector is present; ignore prevBits */
+                memcpy(dstPtr, srcPtr, regWidth);
+                srcPtr += regWidth;
+            } else {
+                /* copy previous bits and modify listed indices */
+                memcpy(dstPtr, prevBits, regWidth);
+                while (bitCount--) {
+                    int bitIndex = readUnsignedLeb128(&srcPtr);
+                    toggleBit(dstPtr, bitIndex);
+                }
+            }
+        } else {
+            /* copy previous bits and modify the specified one */
+            memcpy(dstPtr, prevBits, regWidth);
+
+            /* one bit, from 0-15 inclusive, was changed */
+            toggleBit(dstPtr, key >> 4);
+        }
+
+        prevAddr = addr;
+        prevBits = dstPtr;
+        dstPtr += regWidth;
+    }
+
+    if (dstPtr - (u1*) pNewMap != newMapSize) {
+        ALOGE("ERROR: output %d bytes, expected %d",
+            dstPtr - (u1*) pNewMap, newMapSize);
+        free(pNewMap);
+        return NULL;
+    }
+
+    if (srcPtr - srcStart != expectedSrcLen) {
+        ALOGE("ERROR: consumed %d bytes, expected %d",
+            srcPtr - srcStart, expectedSrcLen);
+        free(pNewMap);
+        return NULL;
+    }
+
+    if (REGISTER_MAP_VERBOSE) {
+        ALOGD("Expansion successful (%d -> %d)",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap));
+    }
+
+    return pNewMap;
+}
diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h
new file mode 100644
index 0000000..2046899
--- /dev/null
+++ b/vm/analysis/RegisterMap.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Declaration of register map data structure and related functions.
+ *
+ * These structures should be treated as opaque through most of the VM.
+ */
+#ifndef DALVIK_REGISTERMAP_H_
+#define DALVIK_REGISTERMAP_H_
+
+#include "analysis/VerifySubs.h"
+#include "analysis/CodeVerify.h"
+
+/*
+ * Format enumeration for RegisterMap data area.
+ */
+enum RegisterMapFormat {
+    kRegMapFormatUnknown = 0,
+    kRegMapFormatNone,          /* indicates no map data follows */
+    kRegMapFormatCompact8,      /* compact layout, 8-bit addresses */
+    kRegMapFormatCompact16,     /* compact layout, 16-bit addresses */
+    kRegMapFormatDifferential,  /* compressed, differential encoding */
+
+    kRegMapFormatOnHeap = 0x80, /* bit flag, indicates allocation on heap */
+};
+
+/*
+ * This is a single variable-size structure.  It may be allocated on the
+ * heap or mapped out of a (post-dexopt) DEX file.
+ *
+ * 32-bit alignment of the structure is NOT guaranteed.  This makes it a
+ * little awkward to deal with as a structure; to avoid accidents we use
+ * only byte types.  Multi-byte values are little-endian.
+ *
+ * Size of (format==FormatNone): 1 byte
+ * Size of (format==FormatCompact8): 4 + (1 + regWidth) * numEntries
+ * Size of (format==FormatCompact16): 4 + (2 + regWidth) * numEntries
+ */
+struct RegisterMap {
+    /* header */
+    u1      format;         /* enum RegisterMapFormat; MUST be first entry */
+    u1      regWidth;       /* bytes per register line, 1+ */
+    u1      numEntries[2];  /* number of entries */
+
+    /* raw data starts here; need not be aligned */
+    u1      data[1];
+};
+
+bool dvmRegisterMapStartup(void);
+void dvmRegisterMapShutdown(void);
+
+/*
+ * Get the format.
+ */
+INLINE RegisterMapFormat dvmRegisterMapGetFormat(const RegisterMap* pMap) {
+    return (RegisterMapFormat)(pMap->format & ~(kRegMapFormatOnHeap));
+}
+
+/*
+ * Set the format.
+ */
+INLINE void dvmRegisterMapSetFormat(RegisterMap* pMap, RegisterMapFormat format)
+{
+    pMap->format &= kRegMapFormatOnHeap;
+    pMap->format |= format;
+}
+
+/*
+ * Get the "on heap" flag.
+ */
+INLINE bool dvmRegisterMapGetOnHeap(const RegisterMap* pMap) {
+    return (pMap->format & kRegMapFormatOnHeap) != 0;
+}
+
+/*
+ * Get the register bit vector width, in bytes.
+ */
+INLINE u1 dvmRegisterMapGetRegWidth(const RegisterMap* pMap) {
+    return pMap->regWidth;
+}
+
+/*
+ * Set the register bit vector width, in bytes.
+ */
+INLINE void dvmRegisterMapSetRegWidth(RegisterMap* pMap, int regWidth) {
+    pMap->regWidth = regWidth;
+}
+
+/*
+ * Set the "on heap" flag.
+ */
+INLINE void dvmRegisterMapSetOnHeap(RegisterMap* pMap, bool val) {
+    if (val)
+        pMap->format |= kRegMapFormatOnHeap;
+    else
+        pMap->format &= ~(kRegMapFormatOnHeap);
+}
+
+/*
+ * Get the number of entries in this map.
+ */
+INLINE u2 dvmRegisterMapGetNumEntries(const RegisterMap* pMap) {
+    return pMap->numEntries[0] | (pMap->numEntries[1] << 8);
+}
+
+/*
+ * Set the number of entries in this map.
+ */
+INLINE void dvmRegisterMapSetNumEntries(RegisterMap* pMap, u2 numEntries) {
+    pMap->numEntries[0] = (u1) numEntries;
+    pMap->numEntries[1] = numEntries >> 8;
+}
+
+/*
+ * Retrieve the bit vector for the specified address.  This is a pointer
+ * to the bit data from an uncompressed map, or to a temporary copy of
+ * data from a compressed map.
+ *
+ * The caller must call dvmReleaseRegisterMapLine() with the result.
+ *
+ * Returns NULL if not found.
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr);
+
+/*
+ * Release "data".
+ *
+ * If "pMap" points to a compressed map from which we have expanded a
+ * single line onto the heap, this will free "data"; otherwise, it does
+ * nothing.
+ *
+ * TODO: decide if this is still a useful concept.
+ */
+INLINE void dvmReleaseRegisterMapLine(const RegisterMap* pMap, const u1* data)
+{}
+
+
+/*
+ * A pool of register maps for methods associated with a single class.
+ *
+ * Each entry is a 4-byte method index followed by the 32-bit-aligned
+ * RegisterMap.  The size of the RegisterMap is determined by parsing
+ * the map.  The lack of an index reduces random access speed, but we
+ * should be doing that rarely (during class load) and it saves space.
+ *
+ * These structures are 32-bit aligned.
+ */
+struct RegisterMapMethodPool {
+    u2      methodCount;            /* chiefly used as a sanity check */
+
+    /* stream of per-method data starts here */
+    u4      methodData[1];
+};
+
+/*
+ * Header for the memory-mapped RegisterMap pool in the DEX file.
+ *
+ * The classDataOffset table provides offsets from the start of the
+ * RegisterMapPool structure.  There is one entry per class (including
+ * interfaces, which can have static initializers).
+ *
+ * The offset points to a RegisterMapMethodPool.
+ *
+ * These structures are 32-bit aligned.
+ */
+struct RegisterMapClassPool {
+    u4      numClasses;
+
+    /* offset table starts here, 32-bit aligned; offset==0 means no data */
+    u4      classDataOffset[1];
+};
+
+/*
+ * Find the register maps for this class.  (Used during class loading.)
+ * If "pNumMaps" is non-NULL, it will return the number of maps in the set.
+ *
+ * Returns NULL if none is available.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+    u4* pNumMaps);
+
+/*
+ * Get the register map for the next method.  "*pPtr" will be advanced past
+ * the end of the map.  (Used during class loading.)
+ *
+ * This should initially be called with the result from
+ * dvmRegisterMapGetClassData().
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr);
+
+/*
+ * This holds some meta-data while we construct the set of register maps
+ * for a DEX file.
+ *
+ * In particular, it keeps track of our temporary mmap region so we can
+ * free it later.
+ */
+struct RegisterMapBuilder {
+    /* public */
+    void*       data;
+    size_t      size;
+
+    /* private */
+    MemMapping  memMap;
+};
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex);
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder);
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata);
+
+/*
+ * Get the expanded form of the register map associated with the specified
+ * method.  May update method->registerMap, possibly freeing the previous
+ * map.
+ *
+ * Returns NULL on failure (e.g. unable to expand map).
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory.
+ * (This is expected to be called at GC time.)
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method);
+INLINE const RegisterMap* dvmGetExpandedRegisterMap(Method* method)
+{
+    const RegisterMap* curMap = method->registerMap;
+    if (curMap == NULL)
+        return NULL;
+    RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+    if (format == kRegMapFormatCompact8 || format == kRegMapFormatCompact16) {
+        return curMap;
+    } else {
+        return dvmGetExpandedRegisterMap0(method);
+    }
+}
+
+/* dump stats gathered during register map creation process */
+void dvmRegisterMapDumpStats(void);
+
+#endif  // DALVIK_REGISTERMAP_H_
diff --git a/vm/analysis/VerifySubs.cpp b/vm/analysis/VerifySubs.cpp
new file mode 100644
index 0000000..7240dd0
--- /dev/null
+++ b/vm/analysis/VerifySubs.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik verification subroutines.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * This is used when debugging to apply a magnifying glass to the
+ * verification of a particular method.
+ */
+bool dvmWantVerboseVerification(const Method* meth)
+{
+    return false;       /* COMMENT OUT to enable verbose debugging */
+
+    const char* cd = "Lcom/android/server/am/ActivityManagerService;";
+    const char* mn = "trimApplications";
+    const char* sg = "()V";
+    return (strcmp(meth->clazz->descriptor, cd) == 0 &&
+            dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
+}
+
+/*
+ * 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.
+ */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+{
+    va_list ap;
+    int logLevel;
+
+    if (gDvm.optimizing) {
+        return;
+        //logLevel = ANDROID_LOG_DEBUG;
+    } else {
+        logLevel = ANDROID_LOG_WARN;
+    }
+
+    va_start(ap, format);
+    LOG_PRI_VA(logLevel, LOG_TAG, format, ap);
+    if (meth != NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOG_PRI(logLevel, LOG_TAG, "VFY:  rejected %s.%s %s",
+            meth->clazz->descriptor, meth->name, desc);
+        free(desc);
+    }
+}
+
+/*
+ * Show a relatively human-readable message describing the failure to
+ * resolve a class.
+ *
+ * TODO: this is somewhat misleading when resolution fails because of
+ * illegal access rather than nonexistent class.
+ */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+    const Method* meth)
+{
+    if (gDvm.optimizing) {
+        return;
+    }
+
+    std::string dotMissingClass = dvmHumanReadableDescriptor(missingClassDescr);
+    std::string dotFromClass = dvmHumanReadableDescriptor(meth->clazz->descriptor);
+    ALOGE("Could not find class '%s', referenced from method %s.%s",
+            dotMissingClass.c_str(), dotFromClass.c_str(), meth->name);
+}
+
+/*
+ * Extract the relative offset from a branch instruction.
+ *
+ * Returns "false" on failure (e.g. this isn't a branch instruction).
+ */
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+    int curOffset, s4* pOffset, bool* pConditional)
+{
+    const u2* insns = meth->insns + curOffset;
+
+    switch (*insns & 0xff) {
+    case OP_GOTO:
+        *pOffset = ((s2) *insns) >> 8;
+        *pConditional = false;
+        break;
+    case OP_GOTO_32:
+        *pOffset = insns[1] | (((u4) insns[2]) << 16);
+        *pConditional = false;
+        break;
+    case OP_GOTO_16:
+        *pOffset = (s2) insns[1];
+        *pConditional = false;
+        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:
+        *pOffset = (s2) insns[1];
+        *pConditional = true;
+        break;
+    default:
+        return false;
+        break;
+    }
+
+    return true;
+}
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
new file mode 100644
index 0000000..330be57
--- /dev/null
+++ b/vm/analysis/VerifySubs.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik bytecode verification subroutines.
+ */
+#ifndef DALVIK_VERIFYSUBS_H_
+#define DALVIK_VERIFYSUBS_H_
+
+/*
+ * InsnFlags is a 32-bit integer with the following layout:
+ *   0-15  instruction length (or 0 if this address doesn't hold an opcode)
+ *  16-31  single bit flags:
+ *    InTry: in "try" block; exceptions thrown here may be caught locally
+ *    BranchTarget: other instructions can branch to this instruction
+ *    GcPoint: this instruction is a GC safe point
+ *    Visited: verifier has examined this instruction at least once
+ *    Changed: set/cleared as bytecode verifier runs
+ */
+typedef u4 InsnFlags;
+
+#define kInsnFlagWidthMask      0x0000ffff
+#define kInsnFlagInTry          (1 << 16)
+#define kInsnFlagBranchTarget   (1 << 17)
+#define kInsnFlagGcPoint        (1 << 18)
+#define kInsnFlagVisited        (1 << 30)
+#define kInsnFlagChanged        (1 << 31)
+
+/* add opcode widths to InsnFlags */
+bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+    int* pNewInstanceCount);
+
+/* set the "in try" flag for sections of code wrapped with a "try" block */
+bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags);
+
+/* verification failure reporting */
+#define LOG_VFY(...)                dvmLogVerifyFailure(NULL, __VA_ARGS__)
+#define LOG_VFY_METH(_meth, ...)    dvmLogVerifyFailure(_meth, __VA_ARGS__)
+
+/* log verification failure with optional method info */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+/* log verification failure due to resolution trouble */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+    const Method* meth);
+
+/* 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);
+
+/* debugging */
+bool dvmWantVerboseVerification(const Method* meth);
+
+#endif  // DALVIK_VERIFYSUBS_H_
diff --git a/vm/analysis/VfyBasicBlock.cpp b/vm/analysis/VfyBasicBlock.cpp
new file mode 100644
index 0000000..55aa6d4
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.cpp
@@ -0,0 +1,552 @@
+/*
+ * 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;
+
+    /*
+     * TODO: there is no good default size here -- the problem is that most
+     * addresses will only have one predecessor, but a fair number will
+     * have 10+, and a few will have 100+ (e.g. the synthetic "finally"
+     * in a large synchronized method).  We probably want to use a small
+     * base allocation (perhaps two) and then have the first overflow
+     * allocation jump dramatically (to 32 or thereabouts).
+     */
+    newBlock->predecessors = dvmPointerSetAlloc(32);
+    if (newBlock->predecessors == NULL) {
+        free(newBlock);
+        return NULL;
+    }
+
+    newBlock->firstAddr = (u4) -1;      // DEBUG
+
+    newBlock->liveRegs = dvmAllocBitVector(vdata->insnRegCount, false);
+    if (newBlock->liveRegs == NULL) {
+        dvmPointerSetFree(newBlock->predecessors);
+        free(newBlock);
+        return NULL;
+    }
+
+    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.
+         */
+        ALOGV("ODD: point set for targ=0x%04x (%p) already had block "
+             "fir=0x%04x (%p)",
+            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;
+        }
+    }
+
+    if (false) {
+        if (dvmPointerSetGetCount(curBlock->predecessors) > 256) {
+            ALOGI("Lots of preds at 0x%04x in %s.%s:%s", curIdx,
+                meth->clazz->descriptor, meth->name, meth->shorty);
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Dump the contents of the basic blocks.
+ */
+static void dumpBasicBlocks(const VerifierData* vdata)
+{
+    char printBuf[256];
+    unsigned int idx;
+    int count;
+
+    ALOGI("Basic blocks for %s.%s:%s", 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 numPreds = dvmPointerSetGetCount(preds);
+
+        if (numPreds > 0) {
+            count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                    "preds:");
+
+            unsigned int predIdx;
+            for (predIdx = 0; predIdx < numPreds; 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)");
+        }
+
+        printBuf[sizeof(printBuf)-2] = '!';
+        printBuf[sizeof(printBuf)-1] = '\0';
+
+        ALOGI("%s", printBuf);
+    }
+
+    usleep(100 * 1000);      /* ugh...let logcat catch up */
+}
+
+
+/*
+ * 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; //dvmWantVerboseVerification(meth);
+    if (verbose) {
+        ALOGI("Basic blocks for %s.%s:%s",
+            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)
+      return false;
+
+    /*
+     * 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 {
+                    ALOGD("overflow, numHandlers=%d", numHandlers);
+                    handlerListAlloc = (u4*) malloc(sizeof(u4) * numHandlers);
+                    if (handlerListAlloc == NULL)
+                        return false;
+                    extractCatchHandlers(pCode, &pTries[tryIndex],
+                        handlerListAlloc, numHandlers);
+                    handlerList = handlerListAlloc;
+                }
+
+                ALOGV("+++ start=%x end=%x numHan=%d",
+                    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 = "-";
+
+            ALOGI("%04x: %c%c%s #%d", idx, tryc, btc, startEnd, debugBBIndex);
+
+            if (pTries != NULL && idx == tryStart) {
+                assert(numHandlers > 0);
+                ALOGI("  EXC block: [%04x, %04x) %d:(%04x...)",
+                    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);
+        dvmFreeBitVector(block->liveRegs);
+        free(block);
+    }
+
+    free(vdata->basicBlocks);
+}
diff --git a/vm/analysis/VfyBasicBlock.h b/vm/analysis/VfyBasicBlock.h
new file mode 100644
index 0000000..0fc7428
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.h
@@ -0,0 +1,61 @@
+/*
+ * 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_H_
+#define DALVIK_VFYBASICBLOCK_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.
+ *
+ * "liveRegs" indicates the set of registers that are live at the end of
+ * the basic block (after the last instruction has executed).  Successor
+ * blocks will compare their results with this to see if this block needs
+ * to be re-evaluated.  Note that this is not the same as the contents of
+ * the RegisterLine for the last instruction in the block (which reflects
+ * the state *before* the instruction has executed).
+ */
+struct VfyBasicBlock {
+    u4              firstAddr;      /* address of first instruction */
+    u4              lastAddr;       /* address of last instruction */
+    PointerSet*     predecessors;   /* set of basic blocks that can flow here */
+    BitVector*      liveRegs;       /* liveness for each register */
+    bool            changed;        /* input set has changed, must re-eval */
+    bool            visited;        /* block has been visited at least once */
+};
+
+/*
+ * Generate a list of basic blocks.
+ */
+bool dvmComputeVfyBasicBlocks(struct VerifierData* vdata);
+
+/*
+ * Free storage allocated by dvmComputeVfyBasicBlocks.
+ */
+void dvmFreeVfyBasicBlocks(struct VerifierData* vdata);
+
+#endif  // DALVIK_VFYBASICBLOCK_H_
diff --git a/vm/arch/arm/CallEABI.S b/vm/arch/arm/CallEABI.S
new file mode 100644
index 0000000..d870306
--- /dev/null
+++ b/vm/arch/arm/CallEABI.S
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the "new" ARM EABI.
+ */
+
+#include <machine/cpu-features.h>
+
+#ifdef __ARM_EABI__
+
+#ifdef EXTENDED_EABI_DEBUG
+# define DBG
+#else
+# define DBG @
+#endif
+
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+ARM EABI notes:
+
+r0-r3 hold first 4 args to a method
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.  This means
+we have to scan the method signature, identify arguments that must be
+padded, and fix them up appropriately.
+*/
+
+    .text
+    .align  2
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, %function
+
+/*
+ * On entry:
+ *   r0  JNIEnv (can be left alone)
+ *   r1  clazz (NULL for virtual method calls, non-NULL for static)
+ *   r2  arg info
+ *   r3  argc (number of 32-bit values in argv)
+ *   [sp]     argv
+ *   [sp,#4]  short signature
+ *   [sp,#8]  func
+ *   [sp,#12] pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return-type enumeration, really only important for "hard" FP ABI
+ *   L - number of double-words of storage required on stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack
+ *
+ * With this arrangement we can efficiently push up to 24 words of arguments
+ * onto the stack.  Anything requiring more than that -- which should happen
+ * rarely to never -- can do the slow signature scan.
+ *
+ * (We could pack the Fs more efficiently -- we know we never push two pads
+ * in a row, and the first word can never be a pad -- but there's really
+ * no need for it.)
+ *
+ * NOTE: if the called function has more than 4 words of arguments, gdb
+ * will not be able to unwind the stack past this method.  The only way
+ * around this is to convince gdb to respect an explicit frame pointer.
+ * The stack unwinder in debuggerd *does* pay attention to fp if we set it
+ * up appropriately, so at least that will work.
+ */
+dvmPlatformInvoke:
+    .fnstart
+
+    /*
+     * Save regs.
+     *
+     * On entry to a function, "sp" must be 64-bit aligned.  This means
+     * we have to adjust sp manually if we push an odd number of regs here
+     * (both here and when exiting).
+     *
+     * The ARM spec doesn't specify anything about the frame pointer.  gcc
+     * points fp at the first saved argument, so our "full descending"
+     * stack looks like:
+     *
+     *  pReturn
+     *  func
+     *  shorty
+     *  argv        <-- sp on entry
+     *  lr          <-- fp
+     *  fp
+     *  r9...r7
+     *  r6          <-- sp after reg save
+     *
+     * Any arguments that need to be pushed on for the target method
+     * come after this.  The last argument is pushed first.
+     */
+SAVED_REG_COUNT = 6                     @ push 6 regs
+FP_STACK_OFFSET = (SAVED_REG_COUNT-1) * 4 @ offset between fp and post-save sp
+FP_ADJ = 4                              @ fp is initial sp +4
+
+    .save        {r6, r7, r8, r9, fp, lr}
+    stmfd   sp!, {r6, r7, r8, r9, fp, lr}
+
+    .setfp  fp, sp, #FP_STACK_OFFSET    @ point fp at first saved reg
+    add     fp, sp, #FP_STACK_OFFSET
+
+    @.pad    #4                          @ adjust for 64-bit align
+    @sub     sp, sp, #4                  @ (if we save odd number of regs)
+
+    @ Ensure 64-bit alignment.  EABI guarantees sp is aligned on entry, make
+    @ sure we're aligned properly now.
+DBG tst     sp, #4                      @ 64-bit aligned?
+DBG bne     dvmAbort                    @ no, fail
+
+    ldr     r9, [fp, #0+FP_ADJ]         @ r9<- argv
+    cmp     r1, #0                      @ calling a static method?
+
+    @ Not static, grab the "this" pointer.  Note "this" is not explicitly
+    @ described by the method signature.
+    subeq   r3, r3, #1                  @ argc--
+    ldreq   r1, [r9], #4                @ r1<- *argv++
+
+    @ Do we have arg padding flags in "argInfo"? (just need to check hi bit)
+    teq     r2, #0
+    bmi     .Lno_arg_info
+
+    /*
+     * "Fast" path.
+     *
+     * Make room on the stack for the arguments and copy them over,
+     * inserting pad words when appropriate.
+     *
+     * Currently:
+     *  r0  don't touch
+     *  r1  don't touch
+     *  r2  arg info
+     *  r3  argc
+     *  r4-r5  don't touch (not saved)
+     *  r6-r8 (available)
+     *  r9  argv
+     *  fp  frame pointer
+     */
+.Lhave_arg_info:
+    @ Expand the stack by the specified amount.  We want to extract the
+    @ count of double-words from r2, multiply it by 8, and subtract that
+    @ from the stack pointer.
+    and     ip, r2, #0x0f000000         @ ip<- double-words required
+    mov     r6, r2, lsr #28             @ r6<- return type
+    sub     sp, sp, ip, lsr #21         @ shift right 24, then left 3
+    mov     r8, sp                      @ r8<- sp  (arg copy dest)
+
+    @ Stick argv in r7 and advance it past the argv values that will be
+    @ held in r2-r3.  It's possible r3 will hold a pad, so check the
+    @ bit in r2.  We do this by ignoring the first bit (which would
+    @ indicate a pad in r2) and shifting the second into the carry flag.
+    @ If the carry is set, r3 will hold a pad, so we adjust argv less.
+    @
+    @ (This is harmless if argc==0)
+    mov     r7, r9
+    movs    r2, r2, lsr #2
+    addcc   r7, r7, #8                  @ skip past 2 words, for r2 and r3
+    subcc   r3, r3, #2
+    addcs   r7, r7, #4                  @ skip past 1 word, for r2
+    subcs   r3, r3, #1
+
+.Lfast_copy_loop:
+    @ if (--argc < 0) goto invoke
+    subs    r3, r3, #1
+    bmi     .Lcopy_done                 @ NOTE: expects original argv in r9
+
+.Lfast_copy_loop2:
+    @ Get pad flag into carry bit.  If it's set, we don't pull a value
+    @ out of argv.
+    movs    r2, r2, lsr #1
+    ldrcc   ip, [r7], #4                @ ip = *r7++ (pull from argv)
+    strcc   ip, [r8], #4                @ *r8++ = ip (write to stack)
+    bcc     .Lfast_copy_loop
+
+DBG movcs   ip, #-3                     @ DEBUG DEBUG - make pad word obvious
+DBG strcs   ip, [r8]                    @ DEBUG DEBUG
+    add     r8, r8, #4                  @ if pad, just advance ip without store
+    b       .Lfast_copy_loop2           @ don't adjust argc after writing pad
+
+
+.Lcopy_done:
+    /*
+     * Currently:
+     *  r0-r3  args (JNIEnv*, thisOrClass, arg0, arg1)
+     *  r6  return type (enum DalvikJniReturnType)
+     *  r9  original argv
+     *  fp  frame pointer
+     *
+     * The stack copy is complete.  Grab the first two words off of argv
+     * and tuck them into r2/r3.  If the first arg is 32-bit and the second
+     * arg is 64-bit, then r3 "holds" a pad word and the load is unnecessary
+     * but harmless.
+     *
+     * If there are 0 or 1 arg words in argv, we will be loading uninitialized
+     * data into the registers, but since nothing tries to use it it's also
+     * harmless (assuming argv[0] and argv[1] point to valid memory, which
+     * is a reasonable assumption for Dalvik's interpreted stacks).
+     */
+    ldmia   r9, {r2-r3}                 @ r2/r3<- argv[0]/argv[1]
+
+    ldr     ip, [fp, #8+FP_ADJ]         @ ip<- func
+    blx     ip                          @ call func
+
+    @ We're back, result is in r0 or (for long/double) r0-r1.
+    @
+    @ In theory, we need to use the "return type" arg to figure out what
+    @ we have and how to return it.  However, unless we have an FPU and
+    @ "hard" fp calling conventions, all we need to do is copy r0-r1 into
+    @ the JValue union.
+    @
+    @ Thought: could redefine DalvikJniReturnType such that single-word
+    @ and double-word values occupy different ranges; simple comparison
+    @ allows us to choose between str and stm.  Probably not worthwhile.
+    @
+    cmp     r6, #0                      @ DALVIK_JNI_RETURN_VOID?
+    ldrne   ip, [fp, #12+FP_ADJ]        @ pReturn
+    sub     sp, fp, #FP_STACK_OFFSET    @ restore sp to post-reg-save offset
+    stmneia ip, {r0-r1}                 @ pReturn->j <- r0/r1
+
+    @ Restore the registers we saved and return.  On >= ARMv5TE we can
+    @ restore PC directly from the saved LR.
+    ldmfd   sp!, {r6, r7, r8, r9, fp, pc}
+
+
+
+    /*
+     * "Slow" path.
+     * Walk through the argument list, counting up the number of 32-bit words
+     * required to contain it.  Then walk through it a second time, copying
+     * values out to the stack.  (We could pre-compute the size to save
+     * ourselves a trip, but we'd have to store that somewhere -- this is
+     * sufficiently unlikely that it's not worthwhile.)
+     *
+     * Try not to make any assumptions about the number of args -- I think
+     * the class file format allows up to 64K words (need to verify that).
+     *
+     * Currently:
+     *  r0  don't touch
+     *  r1  don't touch
+     *  r2  (available)
+     *  r3  argc
+     *  r4-r5 don't touch (not saved)
+     *  r6-r8 (available)
+     *  r9  argv
+     *  fp  frame pointer
+     */
+.Lno_arg_info:
+    mov     ip, r2, lsr #28             @ ip<- return type
+    ldr     r6, [fp, #4+FP_ADJ]         @ r6<- short signature
+    add     r6, r6, #1                  @ advance past return type
+    mov     r2, #0                      @ r2<- word count, init to zero
+
+.Lcount_loop:
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #0                      @ end?
+    beq     .Lcount_done                @ all done, bail
+    add     r2, r2, #1                  @ count++
+    cmp     ip, #'D'                    @ look for 'D' or 'J', which are 64-bit
+    cmpne   ip, #'J'
+    bne     .Lcount_loop
+
+    @ 64-bit value, insert padding if we're not aligned
+    tst     r2, #1                      @ odd after initial incr?
+    addne   r2, #1                      @ no, add 1 more to cover 64 bits
+    addeq   r2, #2                      @ yes, treat prev as pad, incr 2 now
+    b       .Lcount_loop
+.Lcount_done:
+
+    @ We have the padded-out word count in r2.  We subtract 2 from it
+    @ because we don't push the first two arg words on the stack (they're
+    @ destined for r2/r3).  Pushing them on and popping them off would be
+    @ simpler but slower.
+    subs    r2, r2, #2                  @ subtract 2 (for contents of r2/r3)
+    movmis  r2, #0                      @ if negative, peg at zero, set Z-flag
+    beq     .Lcopy_done                 @ zero args, skip stack copy
+
+DBG tst     sp, #7                      @ DEBUG - make sure sp is aligned now
+DBG bne     dvmAbort                    @ DEBUG
+
+    @ Set up to copy from r7 to r8.  We copy from the second arg to the
+    @ last arg, which means reading and writing to ascending addresses.
+    sub     sp, sp, r2, asl #2          @ sp<- sp - r2*4
+    bic     sp, #4                      @ subtract another 4 ifn
+    mov     r7, r9                      @ r7<- argv
+    mov     r8, sp                      @ r8<- sp
+
+    @ We need to copy words from [r7] to [r8].  We walk forward through
+    @ the signature again, "copying" pad words when appropriate, storing
+    @ upward into the stack.
+    ldr     r6, [fp, #4+FP_ADJ]         @ r6<- signature
+    add     r6, r6, #1                  @ advance past return type
+    add     r7, r7, #8                  @ r7<- r7+8 (assume argv 0/1 in r2/r3)
+
+    @ Eat first arg or two, for the stuff that goes into r2/r3.
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    beq     .Lstack_copy_loop           @ 64-bit arg fills r2+r3
+
+    @ First arg was 32-bit, check the next
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    subeq   r7, #4                      @ r7<- r7-4 (take it back - pad word)
+    beq     .Lstack_copy_loop2          @ start with char we already have
+
+    @ Two 32-bit args, fall through and start with next arg
+
+.Lstack_copy_loop:
+    ldrb    ip, [r6], #1                @ ip<- *signature++
+.Lstack_copy_loop2:
+    cmp     ip, #0                      @ end of shorty?
+    beq     .Lcopy_done                 @ yes
+
+    cmp     ip, #'D'
+    cmpne   ip, #'J'
+    beq     .Lcopy64
+
+    @ Copy a 32-bit value.  [r8] is initially at the end of the stack.  We
+    @ use "full descending" stacks, so we store into [r8] and incr as we
+    @ move toward the end of the arg list.
+.Lcopy32:
+    ldr     ip, [r7], #4
+    str     ip, [r8], #4
+    b       .Lstack_copy_loop
+
+.Lcopy64:
+    @ Copy a 64-bit value.  If necessary, leave a hole in the stack to
+    @ ensure alignment.  We know the [r8] output area is 64-bit aligned,
+    @ so we can just mask the address.
+    add     r8, r8, #7          @ r8<- (r8+7) & ~7
+    ldr     ip, [r7], #4
+    bic     r8, r8, #7
+    ldr     r2, [r7], #4
+    str     ip, [r8], #4
+    str     r2, [r8], #4
+    b       .Lstack_copy_loop
+
+    .fnend
+    .size   dvmPlatformInvoke, .-dvmPlatformInvoke
+
+#if 0
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+     .macro SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, pc}
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+strSqueak:
+    .word   .LstrSqueak
+.LstrSqueak:
+    .asciz  "<%d>"
+
+    .align  2
+
+#endif
+
+#endif /*__ARM_EABI__*/
diff --git a/vm/arch/arm/CallOldABI.S b/vm/arch/arm/CallOldABI.S
new file mode 100644
index 0000000..d75f6f7
--- /dev/null
+++ b/vm/arch/arm/CallOldABI.S
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the "old" ARM ABI.
+ */
+
+#include <machine/cpu-features.h>
+
+#ifndef __ARM_EABI__
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+ */
+
+/*
+ARM ABI notes:
+
+r0-r3 hold first 4 args to a method
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns <= 4 bytes
+r0-r1 hold returns of 5-8 bytes, low word in r0
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+Happily we don't have to do anything special here -- the args from the
+interpreter work directly as C/C++ args on ARM (with the "classic" ABI).
+*/
+
+    .text
+    .align  2
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, %function
+
+/*
+On entry:
+  r0  JNIEnv
+  r1  clazz (NULL for virtual method calls, non-NULL for static)
+  r2  arg info (ignored)
+  r3  argc
+  [sp]     argv
+  [sp,#4]  signature (ignored)
+  [sp,#8]  func
+  [sp,#12] pReturn
+*/
+dvmPlatformInvoke:
+    @ Standard gcc stack frame setup.  We don't need to push the original
+    @ sp or the current pc if "-fomit-frame-pointer" is in use for the
+    @ rest of the code.  If we don't plan to use a debugger we can speed
+    @ this up a little.
+    mov     ip, sp
+    stmfd   sp!, {r4, r5, r6, fp, ip, lr, pc}
+    sub     fp, ip, #4          @ set up fp, same way gdb does
+
+    @ We need to push a variable number of arguments onto the stack.
+    @ Rather than keep a count and pop them off after, we just hold on to
+    @ the stack pointers.
+    @
+    @ In theory we don't need to keep sp -- we can do an ldmdb instead of
+    @ an ldmia -- but we're doing the gcc frame trick where we push the
+    @ pc on with stmfd and don't pop it off.
+    mov     r4, ip
+    mov     r5, sp
+
+    @ argc is already in a scratch register (r3).  Put argv into one.  Note
+    @ argv can't go into r0-r3 because we need to use it to load those.
+    ldr     ip, [r4, #0]        @ ip <-- argv
+
+    @ Is this a static method?
+    cmp     r1, #0
+
+    @ No: set r1 to *argv++, and set argc--.
+    @ (r0=pEnv, r1=this)
+    ldreq   r1, [ip], #4
+    subeq   r3, r3, #1
+
+    @ While we still have the use of r2/r3, copy excess args from argv
+    @ to the stack.  We need to push the last item in argv first, and we
+    @ want the first two items in argv to end up in r2/r3.
+    subs    r3, r3, #2
+    ble     .Lno_copy
+
+    @ If there are N args, we want to skip 0 and 1, and push (N-1)..2.  We
+    @ have N-2 in r3.  If we set argv=argv+1, we can count from N-2 to 1
+    @ inclusive and get the right set of args.
+    add     r6, ip, #4
+
+.Lcopy:
+    @ *--sp = argv[count]
+    ldr     r2, [r6, r3, lsl #2]
+    str     r2, [sp, #-4]!
+    subs    r3, r3, #1
+    bne     .Lcopy
+
+.Lno_copy:
+    @ Load the last two args.  These are coming out of the interpreted stack,
+    @ and the VM preserves an overflow region at the bottom, so it should be
+    @ safe to load two items out of argv even if we're at the end.
+    ldr     r2, [ip]
+    ldr     r3, [ip, #4]
+
+    @ Show time.  Tuck the pc into lr and load the pc from the method
+    @ address supplied by the caller.  The value for "pc" is offset by 8
+    @ due to instruction prefetching.
+    @
+    mov     lr, pc
+    ldr     pc, [r4, #8]
+
+    @ We're back, result is in r0 or (for long/double) r0-r1.
+    @
+    @ In theory, we need to use the "return type" arg to figure out what
+    @ we have and how to return it.  However, unless we have an FPU,
+    @ all we need to do is copy r0-r1 into the JValue union.
+    ldr     ip, [r4, #12]
+    stmia   ip, {r0-r1}
+
+    @ Restore the registers we saved and return.  Note this remaps stuff,
+    @ so that "sp" comes from "ip", "pc" comes from "lr", and the "pc"
+    @ we pushed on evaporates when we restore "sp".
+    ldmfd   r5, {r4, r5, r6, fp, sp, pc}
+
+#endif /*__ARM_EABI__*/
diff --git a/vm/arch/arm/HintsEABI.cpp b/vm/arch/arm/HintsEABI.cpp
new file mode 100644
index 0000000..3e27e5a
--- /dev/null
+++ b/vm/arch/arm/HintsEABI.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces arm-specific hints - specifically a description
+ * of padding required to keep all 64-bit parameters properly aligned.
+ *
+ * ARM JNI hint format
+ *
+ *       LLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   L - number of double-words of storage required on the stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack before copying
+ *       the next 32 bits
+ *
+ * If there are too many arguments to construct valid hints, this function will
+ * return a result with the S bit set.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    int padFlags, jniHints;
+    char sigByte;
+    int stackOffset, padMask;
+
+    stackOffset = padFlags = 0;
+    padMask = 0x00000001;
+
+    /* Skip past the return type */
+    sig++;
+
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            if ((stackOffset & 1) != 0) {
+                padFlags |= padMask;
+                stackOffset++;
+                padMask <<= 1;
+            }
+            stackOffset += 2;
+            padMask <<= 2;
+        } else {
+            stackOffset++;
+            padMask <<= 1;
+        }
+    }
+
+    jniHints = 0;
+
+    if (stackOffset > DALVIK_JNI_COUNT_SHIFT) {
+        /* too big for "fast" version */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0);
+        stackOffset -= 2;           // r2/r3 holds first two items
+        if (stackOffset < 0)
+            stackOffset = 0;
+        jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT;
+        jniHints |= padFlags;
+    }
+
+    return jniHints;
+}
diff --git a/vm/arch/generic/Call.cpp b/vm/arch/generic/Call.cpp
new file mode 100644
index 0000000..3d7cbef
--- /dev/null
+++ b/vm/arch/generic/Call.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * This uses the FFI (Foreign Function Interface) library to abstract away
+ * the system-dependent stuff.  The FFI code is slower than a custom
+ * assembly version, but has the distinct advantage of having been
+ * written already for several platforms.
+ */
+#include "Dalvik.h"
+#include "ffi.h"
+
+/*
+ * Convert a signature type character to an FFI type.
+ */
+static ffi_type* getFfiType(char sigType)
+{
+    switch (sigType) {
+    case 'V': return &ffi_type_void;
+    case 'Z': return &ffi_type_uint8;
+    case 'B': return &ffi_type_sint8;
+    case 'C': return &ffi_type_uint16;
+    case 'S': return &ffi_type_sint16;
+    case 'I': return &ffi_type_sint32;
+    case 'F': return &ffi_type_float;
+    case 'J': return &ffi_type_sint64;
+    case 'D': return &ffi_type_double;
+    case '[':
+    case 'L': return &ffi_type_pointer;
+    default:
+        ALOGE("bad ffitype 0x%02x", sigType);
+        dvmAbort();
+        return NULL;
+    }
+}
+
+/* We will call this generic function if there are no hints */
+#ifdef __mips__
+#define dvmPlatformInvoke dvmPlatformInvokeFFI
+
+extern "C" void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+    int argc, const u4* argv, const char* signature, void* func, JValue* pResult);
+#endif
+
+/*
+ * Call "func" with the specified arguments.
+ *
+ * The second argument to JNI native functions is either the object (the
+ * "this" pointer) or, for static functions, a pointer to the class object.
+ * The Dalvik instructions will push "this" into argv[0], but it's up to
+ * us to insert the class object.
+ *
+ * Because there is no such thing in as a null "this" pointer, we use
+ * the non-NULL state of "clazz" to determine whether or not it's static.
+ *
+ * For maximum efficiency we should compute the CIF once and save it with
+ * the method.  However, this requires storing the data with every native
+ * method.  Since the goal is to have custom assembly versions of this
+ * on the platforms where performance matters, I'm recomputing the CIF on
+ * every call.
+ */
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* shorty, void* func, JValue* pReturn)
+{
+    const int kMaxArgs = argc+2;    /* +1 for env, maybe +1 for clazz */
+    ffi_cif cif;
+    ffi_type* types[kMaxArgs];
+    void* values[kMaxArgs];
+    ffi_type* retType;
+    char sigByte;
+    int srcArg, dstArg;
+
+    types[0] = &ffi_type_pointer;
+    values[0] = &pEnv;
+
+    types[1] = &ffi_type_pointer;
+    if (clazz != NULL) {
+        values[1] = &clazz;
+        srcArg = 0;
+    } else {
+        values[1] = (void*) argv++;
+        srcArg = 1;
+    }
+    dstArg = 2;
+
+    /*
+     * Scan the types out of the short signature.  Use them to fill out the
+     * "types" array.  Store the start address of the argument in "values".
+     */
+    retType = getFfiType(*shorty);
+    while ((sigByte = *++shorty) != '\0') {
+        types[dstArg] = getFfiType(sigByte);
+        values[dstArg++] = (void*) argv++;
+        if (sigByte == 'D' || sigByte == 'J')
+            argv++;
+    }
+
+    /*
+     * Prep the CIF (Call InterFace object).
+     */
+    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, dstArg, retType, types) != FFI_OK) {
+        ALOGE("ffi_prep_cif failed");
+        dvmAbort();
+    }
+
+    ffi_call(&cif, FFI_FN(func), pReturn, values);
+}
diff --git a/vm/arch/generic/Hints.cpp b/vm/arch/generic/Hints.cpp
new file mode 100644
index 0000000..7b08aeb
--- /dev/null
+++ b/vm/arch/generic/Hints.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints
+ *
+ * This function is a placeholder/template and should be duplicated in the
+ * appropriate arch/<target>/ directory for new target ports.  The hints
+ * field should be defined and constructed in conjunction with
+ * dvmPlatformInvoke.
+
+ * If valid hints can't be constructed, this function should return a negative
+ * value.  In that case, the caller will set the S bit in the jniArgInfo word
+ * and convert the arguments the slow way.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+    /* No hints for generic target - force argument walk at run-time */
+    return DALVIK_JNI_NO_ARG_INFO;
+}
diff --git a/vm/arch/mips/CallO32.S b/vm/arch/mips/CallO32.S
new file mode 100644
index 0000000..e436d1e
--- /dev/null
+++ b/vm/arch/mips/CallO32.S
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the MIPS O32 ABI.
+ */
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+
+Please notice that argc in dvmPlatformInvoke does NOT include pEnv and clazz/this.
+*/
+
+    .text
+    .align  2
+    .globl dvmPlatformInvoke
+    .ent dvmPlatformInvoke
+/*
+ * On entry:
+ *   a0  JNIEnv (can be left alone)
+ *   a1  clazz (NULL for virtual method calls, non-NULL for static)
+ *   a2  argInfo
+ *   a3  argc (number of 32-bit values in argv)
+ *   MIPS reservers 16 bytes on stack even if the first 4 args are passed by
+ *   reg a0-a3. That's different from ARM.
+ *   [sp + 16]  argv
+ *   [sp + 20]  short signature
+ *   [sp + 24]  func
+ *   [sp + 28]  pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return type enumeration, really only important for hardware FP
+ *   L - number of double-words (64 bits!) of storage required on stack (0-30 words)
+ *   F - pad flag -- if set, write a pad word to the stack
+ *
+ * With this arrangement we can efficiently push up to 24 words of arguments
+ * onto the stack.  Anything requiring more than that -- which should happen
+ * rarely to never -- can do the slow signature scan.
+ *
+ * (We could pack the Fs more efficiently -- we know we never push two pads
+ * in a row, and the first word can never be a pad -- but there's really
+ * no need for it.)
+ *
+ * NOTE: if the called function has more than 4 words of arguments, gdb
+ * will not be able to unwind the stack past this method.  The only way
+ * around this is to convince gdb to respect an explicit frame pointer.
+ */
+
+ /* Stack:
+  *                     High
+  *                 ____________
+  *                 |__28______| pReturn
+  *                 |__24______| func
+  *                 |__20______| short signature
+  *                 |__16______| argv
+  *                 |__12______| reserved (a3: argc)
+  *                 |__8_______| reserved (a2: arg)
+  *                 |__4_______| reserved (a1: clazz)
+  *__sp on entry_->_|__0_______|_reserved (a0: JNIenv)
+  *                 |__________| saved ra
+  *                 |__________| saved fp
+  *                 |__________| saved s0
+  *                 |__________| spare
+  *                 |__________| saved s2
+  *"framepointer"->_|__________| pad for 8 bytes aligned
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| other argv or pad
+  *                 |__________| reserved for a3
+  *                 |__________| reserved for a2
+  *                 |__________| reserved for a1
+  *_____new sp___-> |__________| reserved for a0
+  * (new sp: sp when call native method)
+  */
+
+ /* Register usage:
+  *
+  *  s0: pReturn
+  *  s2: Return type
+  * These registers should be saved to and restored from stack.
+  *
+  *  t0: argv
+  *  t9: func
+  * These registers do not need to be saved.
+  *
+  * We put the stack size into register s1 because we can not know the size
+  * of stack at the beginning. This size can be calculated with the help
+  * of hints in jniarginfo.
+  *
+  */
+
+dvmPlatformInvoke:
+	.set noreorder
+	.cpload $t9
+	.set reorder
+
+	/*  Do we have arg padding flags in "argInfo"? Check bit 31 */
+	bltz	$a2,.Lno_arginfo
+
+	/* Fast path. We have hints. */
+	/* save fp and ra to stack */
+#define FSIZE 24
+	subu	$sp,FSIZE
+	sw	$ra,20($sp)
+	sw	$fp,16($sp)
+	sw	$s0,12($sp)
+	sw	$s2,4($sp)
+	move	$fp,$sp
+
+	lw	$t0,FSIZE+16($sp)	/* t0 <- argv */
+	lw	$t9,FSIZE+24($sp)	/* t9 <- func */
+	lw	$s0,FSIZE+28($sp)	/* s0 <- pReturn */
+
+	/* Is the method static? */
+	bnez	$a1,1f
+	/* Not static: a1 <- *argv++ ("this"), argc-- */
+	lw	$a1,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+1:
+	/* expand the stack for args */
+	srl	$s2,$a2,28	/* s2 <- returnType */
+	srl	$t1,$a2,21
+	andi	$t1,0x78	/* t1 <- stackSize in bytes */
+
+	addiu	$t1,16		/* include space for a0/a1/a2/a3 */
+	subu	$sp,$t1
+	addiu	$t1,$sp,8
+
+	/*
+	 * t0 :argv
+	 * t1 :sp+8(first arg position in stack except pEnv and clazz/this)
+	 * a2 :argInfo
+	 * a3 :argc
+	 * sp :new stack bottom
+	 */
+
+	/* first two args or one args and pad */
+	blez	$a3,.Largs_done
+	lw	$t2,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+	sw	$t2,($t1)
+	addiu	$t1,4
+	srl	$a2,1
+	blez	$a3,.Largs_done
+
+	andi	$t3,$a2,0x1	/* the second position is a pad? */
+	bnez	$t3,.Lpad0
+
+	lw	$t2,($t0)
+	addiu	$t0,4
+	addiu	$a3,-1
+	sw	$t2,($t1)
+.Lpad0:
+	addiu	$t1,4
+	srl	$a2,1
+	blez	$a3,.Largs_done
+
+.Lloop1:
+	/* copy other args
+	 * $fp: sp top for args
+	 * $t1: sp for next arg
+	 */
+	beq	$t1,$fp,.Largs_done
+	andi	$t3,$a2,0x1
+	srl	$a2,1
+	bnez	$t3,.Lpad
+	lw	$t2,($t0)
+	addiu	$t0,4
+	sw	$t2,($t1)
+.Lpad:
+	addiu	$t1,4
+	b	.Lloop1
+
+.Largs_done:
+
+	/*
+	 * We have copied args into stacks. Then copy argv[0]/argv[1] into
+	 * reg a2/a3. You may find that if argv[0] is 32 bits and argv[1]
+	 * is 64 bits, then we do not need to set reg a3 since it is a pad.
+	 * However, copy a3 from argv is harmless. We do not need to set
+	 * a0(pEnv)/a1(clazz/this) since they are already there.
+	 */
+
+	/*
+	 * sp: new stack
+	 * s0: pReturn
+	 * s2: Return type
+	 *
+	 */
+	lw	$a2,8($sp)
+	lw	$a3,12($sp)
+
+	/* Linux/PIC needs $t9 points to function address.
+	 * call the function
+	 */
+	jalr $t9
+
+	/* function call return */
+	/* 1. check the return type
+	 * 2. if the return type is not DALVIK_JNI_RETURN_VOID then copy v0/v1
+	 *    to pReturn
+	 */
+	beqz	$s2,.Lend	/* don't set result if return type is void */
+
+#ifdef __mips_hard_float
+	mfc1	$t0,$f0		/* Get float ($f0) or double ($f1$f0) result */
+	mfc1	$t1,$f1
+	sltiu	$t2,$s2,3	/* set t2 if return type is float or double */
+#ifdef HAVE_LITTLE_ENDIAN
+        /* Note: for little endian, the double result is in $v1:$v0 and float result is in $v0 */
+	movn	$v0,$t0,$t2	/* If the result type is float or double overwrite $v1/$v0 */
+	movn	$v1,$t1,$t2
+#else
+        /* Note: for big endian, the double result is in $v0:$v1 and float result is in $v0 */
+	movn	$v1,$t0,$t2	/* If the result type is float or double overwrite $v0/$v1 */
+	movn	$v0,$t1,$t2
+	sltiu	$t3,$s2,2	/* set t3 if return type is float */
+	movn	$v0,$t0,$t3	/* If the result type is float overwrite $v0 */
+#endif
+#endif
+
+	/* Store the result */
+	sw	$v0,0($s0)
+	sw	$v1,4($s0)
+
+.Lend:
+	/* restore saved registers */
+	move	$sp,$fp
+	lw	$ra,20($sp)
+	lw	$fp,16($sp)
+	lw	$s0,12($sp)
+	lw	$s2,4($sp)
+	addiu	$sp,FSIZE
+	jr	$ra
+
+/* Slow path - just tail call the generic routine */
+.Lno_arginfo:
+
+	la $t9,dvmPlatformInvokeFFI
+	j $t9
+
+.end dvmPlatformInvoke
diff --git a/vm/arch/mips/HintsO32.cpp b/vm/arch/mips/HintsO32.cpp
new file mode 100644
index 0000000..77fdfd4
--- /dev/null
+++ b/vm/arch/mips/HintsO32.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the MIPS O32 ABI.
+ */
+
+/* TODO: this is candidate for consolidation of similar code from ARM. */
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces mips-specific hints - specifically a description
+ * of padding required to keep all 64-bit parameters properly aligned.
+ *
+ * MIPS JNI hint format(Same as ARM)
+ *
+ *       LLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ *   L - number of double-words of storage required on the stack (0-30 words)
+ *   F - pad flag -- if set, the stack increases 8 bytes, else the stack increases 4 bytes
+ *                   after copying 32 bits args into stack. (little different from ARM)
+ *
+ * If there are too many arguments to construct valid hints, this function will
+ * return a result with the S bit set.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto)
+{
+
+    const char* sig = dexProtoGetShorty(proto);
+    int padFlags, jniHints;
+    char sigByte;
+    int stackOffset, padMask, hints;
+
+    stackOffset = padFlags = 0;
+    padMask = 0x00000001;
+
+    /* Skip past the return type */
+    sig++;
+
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            if ((stackOffset & 1) != 0) {
+                padFlags |= padMask;
+                stackOffset++;
+                padMask <<= 1;
+            }
+            stackOffset += 2;
+            padMask <<= 2;
+        } else {
+            stackOffset++;
+            padMask <<= 1;
+        }
+    }
+
+    jniHints = 0;
+
+    if (stackOffset > DALVIK_JNI_COUNT_SHIFT) {
+        /* too big for "fast" version */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0);
+        /*
+         * StackOffset includes the space for a2/a3. However we have reserved
+         * 16 bytes on stack in CallO32.S, so we should subtract 2 from stackOffset.
+         */
+        stackOffset -= 2;
+        if (stackOffset < 0)
+            stackOffset = 0;
+        jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT;
+        jniHints |= padFlags;
+    }
+
+    return jniHints;
+}
diff --git a/vm/arch/x86/Call386ABI.S b/vm/arch/x86/Call386ABI.S
new file mode 100644
index 0000000..7765e5d
--- /dev/null
+++ b/vm/arch/x86/Call386ABI.S
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * JNI method invocation.  This is used to call a C/C++ JNI method.  The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports 32-bit x86
+ */
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+    const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+    -or-
+  return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two).  It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+x86 notes:
+
+The native code expects arguments on the stack, pushed from right to left.
+This matches what Dalvik is passing here.
+
+EAX, EDX and ECX are scratch.
+
+4-byte alignment is required for long long and double, so we won't pad
+
+Non-FP return types <= 4 bytes come back in EAX
+Non-FP return types of 8 bytes come back in EAX:EDX, with lsw in EAX.
+Float and double returned on top of FP stack.
+
+*/
+
+    .text
+    .align  4
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, @function
+
+/*
+ * On entry:
+ *  [ 8]  arg0  JNIEnv (can be left alone)
+ *  [12]  arg1  clazz (NULL for virtual method calls, non-NULL for static)
+ *  [16]  arg2  arg info
+ *  [20]  arg3  argc
+ *  [24]  arg4  argv
+ *  [28]  arg5  short signature
+ *  [32]  arg6  func
+ *  [36]  arg7  pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *   SRRRZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ *   Z - reserved
+ *   S - if set, argInfo hints are invalid
+ *   R - return type enumeration (see jniInternal.h)
+ *       VOID   -> 0
+ *       FLOAT  -> 1
+ *       DOUBLE -> 2
+ *       S8     -> 3
+ *       S4     -> 4
+ *   A - size of the variable argument block in 32-bit words
+ *
+ */
+dvmPlatformInvoke:
+/* Establish the frame pointer and spill. */
+    pushl    %ebp
+    movl     %esp,%ebp
+    pushl    %edi
+    pushl    %esi
+    pushl    %ebx
+
+/* Align stack to 16b. */
+    subl     $15,%esp
+    andl     $0xfffffff0,%esp
+
+/* For 386 ABI, argInfo hints should always be valid.  Abort if not. */
+    movl     16(%ebp),%ebx
+    testl    %ebx,%ebx
+    js       dvmAbort
+/*
+ * Get the size of the variable region, add two more slots for the first
+ * two arguments and grow (preserving alignment)
+ */
+    movl     %ebx,%ecx
+    leal     20(,%ecx,4),%ecx
+    andl     $0x0003FFF0,%ecx
+    subl     %ecx,%esp
+/* Handle this/class */
+    movl     8(%ebp),%ecx
+    movl     12(%ebp),%eax
+    movl     24(%ebp),%esi
+    testl    %eax,%eax
+    jne      isClass
+    movl     (%esi),%eax
+    addl     $4,%esi
+isClass:
+    movl     %eax,4(%esp)
+    movl     %ecx,0(%esp)
+/* Now, copy the variable arguments region */
+    movl     %ebx,%ecx
+    andl     $0x0000FFFF,%ecx
+    leal     8(%esp),%edi
+    cld
+    rep
+    movsd
+/* Ready to go - call the native code */
+    call     *32(%ebp)
+/* Store the result. */
+    sarl      $28,%ebx
+    /* Is void? */
+    testl     %ebx,%ebx
+    je       cleanUpAndExit
+    movl     36(%ebp),%ecx
+    /* Is FP? */
+    cmpl     $2,%ebx
+    jle      isFP
+    cmpl     $4,%ebx  /* smaller than 32-bits? */
+    jg       isSmall
+storeRetval:
+    /* Blindly storing 64-bits won't hurt 32-bit case */
+    movl     %eax,(%ecx)
+    movl     %edx,4(%ecx)
+    jmp      cleanUpAndExit
+isSmall:
+    cmpl     $7,%ebx  /* S1? */
+    jne      checkShort
+    movsbl   %al,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+checkShort:
+    cmpl     $6,%ebx  /* U2? */
+    jne      isSignedShort
+    movzwl   %ax,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+isSignedShort:
+    /* Must be S2 */
+    movswl   %ax,%eax
+    movl     %eax,(%ecx)
+    jmp      cleanUpAndExit
+isFP:
+    /* Is Float? */
+    cmpl    $1,%ebx
+    je       saveFloat
+    fstpl    (%ecx)
+    jmp      cleanUpAndExit
+saveFloat:
+    fstps    (%ecx)
+cleanUpAndExit:
+    leal     -12(%ebp),%esp
+    pop      %ebx
+    pop      %esi
+    pop      %edi
+    pop      %ebp
+    ret
+    .size    dvmPlatformInvoke, .-dvmPlatformInvoke
+    .section .note.GNU-stack,"",@progbits
diff --git a/vm/arch/x86/Hints386ABI.cpp b/vm/arch/x86/Hints386ABI.cpp
new file mode 100644
index 0000000..2a0a1d8
--- /dev/null
+++ b/vm/arch/x86/Hints386ABI.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls.  The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ *    SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *    S - if set, ignore the hints and do things the hard way (scan signature)
+ *    R - return-type enumeration
+ *    H - target-specific hints (see below for details)
+ *
+ * This function produces x86-specific hints for the standard 32-bit 386 ABI.
+ * Note that the JNI requirements are very close to the 386 runtime model.  In
+ * particular, natural datatype alignments do not apply to passed arguments.
+ * All arguments have 32-bit alignment.  As a result, we don't have to worry
+ * about padding - just total size.  The only tricky bit is that floating point
+ * return values come back on the FP stack.
+ *
+ *
+ * 386 ABI JNI hint format
+ *
+ *       ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ *   Z - reserved, must be 0
+ *   A - size of variable argument block in 32-bit words (note - does not
+ *       include JNIEnv or clazz)
+ *
+ * For the 386 ABI, valid hints should always be generated.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    unsigned int jniHints, wordCount;
+    char sigByte;
+
+    wordCount = 0;
+    while (true) {
+        sigByte = *(sig++);
+
+        if (sigByte == '\0')
+            break;
+
+        wordCount++;
+
+        if (sigByte == 'D' || sigByte == 'J') {
+            wordCount++;
+        }
+    }
+
+    if (wordCount > 0xFFFF) {
+        /* Invalid - Dex file limitation */
+        jniHints = DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        jniHints = wordCount;
+    }
+
+    return jniHints;
+}
diff --git a/vm/compiler/Compiler.cpp b/vm/compiler/Compiler.cpp
new file mode 100644
index 0000000..781f014
--- /dev/null
+++ b/vm/compiler/Compiler.cpp
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <cutils/ashmem.h>
+
+#include "Dalvik.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#ifdef ARCH_IA32
+#include "codegen/x86/Translator.h"
+#include "codegen/x86/Lower.h"
+#endif
+
+extern "C" void dvmCompilerTemplateStart(void);
+extern "C" void dvmCompilerTemplateEnd(void);
+
+static inline bool workQueueLength(void)
+{
+    return gDvmJit.compilerQueueLength;
+}
+
+static CompilerWorkOrder workDequeue(void)
+{
+    assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
+           != kWorkOrderInvalid);
+    CompilerWorkOrder work =
+        gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
+    gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
+        kWorkOrderInvalid;
+    if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
+        gDvmJit.compilerWorkDequeueIndex = 0;
+    }
+    gDvmJit.compilerQueueLength--;
+    if (gDvmJit.compilerQueueLength == 0) {
+        dvmSignalCond(&gDvmJit.compilerQueueEmpty);
+    }
+
+    /* Remember the high water mark of the queue length */
+    if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
+        gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
+
+    return work;
+}
+
+/*
+ * Enqueue a work order - retrying until successful.  If attempt to enqueue
+ * is repeatedly unsuccessful, assume the JIT is in a bad state and force a
+ * code cache reset.
+ */
+#define ENQUEUE_MAX_RETRIES 20
+void dvmCompilerForceWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
+{
+    bool success;
+    int retries = 0;
+    do {
+        success = dvmCompilerWorkEnqueue(pc, kind, info);
+        if (!success) {
+            retries++;
+            if (retries > ENQUEUE_MAX_RETRIES) {
+                ALOGE("JIT: compiler queue wedged - forcing reset");
+                gDvmJit.codeCacheFull = true;  // Force reset
+                success = true;  // Because we'll drop the order now anyway
+            } else {
+                dvmLockMutex(&gDvmJit.compilerLock);
+                pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                                  &gDvmJit.compilerLock);
+                dvmUnlockMutex(&gDvmJit.compilerLock);
+
+            }
+        }
+    } while (!success);
+}
+
+/*
+ * Attempt to enqueue a work order, returning true if successful.
+ *
+ * NOTE: Make sure that the caller frees the info pointer if the return value
+ * is false.
+ */
+bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
+{
+    int cc;
+    int i;
+    int numWork;
+    bool result = true;
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /*
+     * Return if queue or code cache is full.
+     */
+    if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
+        gDvmJit.codeCacheFull == true) {
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return false;
+    }
+
+    for (numWork = gDvmJit.compilerQueueLength,
+           i = gDvmJit.compilerWorkDequeueIndex;
+         numWork > 0;
+         numWork--) {
+        /* Already enqueued */
+        if (gDvmJit.compilerWorkQueue[i++].pc == pc) {
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+            return true;
+        }
+        /* Wrap around */
+        if (i == COMPILER_WORK_QUEUE_SIZE)
+            i = 0;
+    }
+
+    CompilerWorkOrder *newOrder =
+        &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
+    newOrder->pc = pc;
+    newOrder->kind = kind;
+    newOrder->info = info;
+    newOrder->result.methodCompilationAborted = false;
+    newOrder->result.codeAddress = NULL;
+    newOrder->result.discardResult =
+        (kind == kWorkOrderTraceDebug) ? true : false;
+    newOrder->result.cacheVersion = gDvmJit.cacheVersion;
+    newOrder->result.requestingThread = dvmThreadSelf();
+
+    gDvmJit.compilerWorkEnqueueIndex++;
+    if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
+        gDvmJit.compilerWorkEnqueueIndex = 0;
+    gDvmJit.compilerQueueLength++;
+    cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+    assert(cc == 0);
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+    return result;
+}
+
+/* Block until the queue length is 0, or there is a pending suspend request */
+void dvmCompilerDrainQueue(void)
+{
+    Thread *self = dvmThreadSelf();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+    while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread &&
+           self->suspendCount == 0) {
+        /*
+         * Use timed wait here - more than one mutator threads may be blocked
+         * but the compiler thread will only signal once when the queue is
+         * emptied. Furthermore, the compiler thread may have been shutdown
+         * so the blocked thread may never get the wakeup signal.
+         */
+        dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock,                             1000, 0);
+    }
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+}
+
+bool dvmCompilerSetupCodeCache(void)
+{
+    int fd;
+
+    /* Allocate the code cache */
+    fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
+    if (fd < 0) {
+        ALOGE("Could not create %u-byte ashmem region for the JIT code cache",
+             gDvmJit.codeCacheSize);
+        return false;
+    }
+    gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize,
+                             PROT_READ | PROT_WRITE | PROT_EXEC,
+                             MAP_PRIVATE , fd, 0);
+    close(fd);
+    if (gDvmJit.codeCache == MAP_FAILED) {
+        ALOGE("Failed to mmap the JIT code cache of size %d: %s", gDvmJit.codeCacheSize, strerror(errno));
+        return false;
+    }
+
+    gDvmJit.pageSizeMask = getpagesize() - 1;
+
+    /* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */
+    // ALOGD("Code cache starts at %p", gDvmJit.codeCache);
+
+#ifndef ARCH_IA32
+    /* Copy the template code into the beginning of the code cache */
+    int templateSize = (intptr_t) dvmCompilerTemplateEnd -
+                       (intptr_t) dvmCompilerTemplateStart;
+    memcpy((void *) gDvmJit.codeCache,
+           (void *) dvmCompilerTemplateStart,
+           templateSize);
+
+    /*
+     * Work around a CPU bug by keeping the 32-bit ARM handler code in its own
+     * page.
+     */
+    if (dvmCompilerInstructionSet() == DALVIK_JIT_THUMB2) {
+        templateSize = (templateSize + 4095) & ~4095;
+    }
+
+    gDvmJit.templateSize = templateSize;
+    gDvmJit.codeCacheByteUsed = templateSize;
+
+    /* Only flush the part in the code cache that is being used now */
+    dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+                          (intptr_t) gDvmJit.codeCache + templateSize);
+#else
+    gDvmJit.codeCacheByteUsed = 0;
+    stream = (char*)gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    ALOGV("codeCache = %p stream = %p before initJIT", gDvmJit.codeCache, stream);
+    streamStart = stream;
+    initJIT(NULL, NULL);
+    gDvmJit.templateSize = (stream - streamStart);
+    gDvmJit.codeCacheByteUsed = (stream - streamStart);
+    ALOGV("stream = %p after initJIT", stream);
+#endif
+
+    int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
+                          PROTECT_CODE_CACHE_ATTRS);
+
+    if (result == -1) {
+        ALOGE("Failed to remove the write permission for the code cache");
+        dvmAbort();
+    }
+
+    return true;
+}
+
+static void crawlDalvikStack(Thread *thread, bool print)
+{
+    void *fp = thread->interpSave.curFrame;
+    StackSaveArea* saveArea = NULL;
+    int stackLevel = 0;
+
+    if (print) {
+        ALOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
+             dvmGetThreadStatusStr(thread->status),
+             thread->inJitCodeCache,
+             thread->inJitCodeCache ? "jit" : "interp");
+    }
+    /* Crawl the Dalvik stack frames to clear the returnAddr field */
+    while (fp != NULL) {
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (print) {
+            if (dvmIsBreakFrame((u4*)fp)) {
+                ALOGD("  #%d: break frame (%p)",
+                     stackLevel, saveArea->returnAddr);
+            }
+            else {
+                ALOGD("  #%d: %s.%s%s (%p)",
+                     stackLevel,
+                     saveArea->method->clazz->descriptor,
+                     saveArea->method->name,
+                     dvmIsNativeMethod(saveArea->method) ?
+                         " (native)" : "",
+                     saveArea->returnAddr);
+            }
+        }
+        stackLevel++;
+        saveArea->returnAddr = NULL;
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    /* Make sure the stack is fully unwound to the bottom */
+    assert(saveArea == NULL ||
+           (u1 *) (saveArea+1) == thread->interpStackStart);
+}
+
+static void resetCodeCache(void)
+{
+    Thread* thread;
+    u8 startTime = dvmGetRelativeTimeUsec();
+    int inJit = 0;
+    int byteUsed = gDvmJit.codeCacheByteUsed;
+
+    /* If any thread is found stuck in the JIT state, don't reset the cache  */
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        /*
+         * Crawl the stack to wipe out the returnAddr field so that
+         * 1) the soon-to-be-deleted code in the JIT cache won't be used
+         * 2) or the thread stuck in the JIT land will soon return
+         *    to the interpreter land
+         */
+        crawlDalvikStack(thread, false);
+        if (thread->inJitCodeCache) {
+            inJit++;
+        }
+        /* Cancel any ongoing trace selection */
+        dvmDisableSubMode(thread, kSubModeJitTraceBuild);
+    }
+    dvmUnlockThreadList();
+
+    if (inJit) {
+        ALOGD("JIT code cache reset delayed (%d bytes %d/%d)",
+             gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset,
+             ++gDvmJit.numCodeCacheResetDelayed);
+        return;
+    }
+
+    /* Lock the mutex to clean up the work queue */
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /* Update the translation cache version */
+    gDvmJit.cacheVersion++;
+
+    /* Drain the work queue to free the work orders */
+    while (workQueueLength()) {
+        CompilerWorkOrder work = workDequeue();
+        free(work.info);
+    }
+
+    /* Reset the JitEntry table contents to the initial unpopulated state */
+    dvmJitResetTable();
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+    /*
+     * Wipe out the code cache content to force immediate crashes if
+     * stale JIT'ed code is invoked.
+     */
+    dvmCompilerCacheClear((char *) gDvmJit.codeCache + gDvmJit.templateSize,
+                          gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+
+    dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+                          (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    /* Reset the current mark of used bytes to the end of template code */
+    gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
+    gDvmJit.numCompilations = 0;
+
+    /* Reset the work queue */
+    memset(gDvmJit.compilerWorkQueue, 0,
+           sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
+    gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+    gDvmJit.compilerQueueLength = 0;
+
+    /* Reset the IC patch work queue */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+    gDvmJit.compilerICPatchIndex = 0;
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+
+    /*
+     * Reset the inflight compilation address (can only be done in safe points
+     * or by the compiler thread when its thread state is RUNNING).
+     */
+    gDvmJit.inflightBaseAddr = NULL;
+
+    /* All clear now */
+    gDvmJit.codeCacheFull = false;
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    ALOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)",
+         (dvmGetRelativeTimeUsec() - startTime) / 1000,
+         byteUsed, ++gDvmJit.numCodeCacheReset,
+         gDvmJit.numCodeCacheResetDelayed);
+}
+
+/*
+ * Perform actions that are only safe when all threads are suspended. Currently
+ * we do:
+ * 1) Check if the code cache is full. If so reset it and restart populating it
+ *    from scratch.
+ * 2) Patch predicted chaining cells by consuming recorded work orders.
+ */
+void dvmCompilerPerformSafePointChecks(void)
+{
+    if (gDvmJit.codeCacheFull) {
+        resetCodeCache();
+    }
+    dvmCompilerPatchInlineCache();
+}
+
+static bool compilerThreadStartup(void)
+{
+    JitEntry *pJitTable = NULL;
+    unsigned char *pJitProfTable = NULL;
+    JitTraceProfCounters *pJitTraceProfCounters = NULL;
+    unsigned int i;
+
+    if (!dvmCompilerArchInit())
+        goto fail;
+
+    /*
+     * Setup the code cache if we have not inherited a valid code cache
+     * from the zygote.
+     */
+    if (gDvmJit.codeCache == NULL) {
+        if (!dvmCompilerSetupCodeCache())
+            goto fail;
+    }
+
+    /* Allocate the initial arena block */
+    if (dvmCompilerHeapInit() == false) {
+        goto fail;
+    }
+
+    /* Cache the thread pointer */
+    gDvmJit.compilerThread = dvmThreadSelf();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+
+    /* Track method-level compilation statistics */
+    gDvmJit.methodStatsTable =  dvmHashTableCreate(32, NULL);
+
+#if defined(WITH_JIT_TUNING)
+    gDvm.verboseShutdown = true;
+#endif
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Set up the JitTable */
+
+    /* Power of 2? */
+    assert(gDvmJit.jitTableSize &&
+           !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));
+
+    dvmInitMutex(&gDvmJit.tableLock);
+    dvmLockMutex(&gDvmJit.tableLock);
+    pJitTable = (JitEntry*)
+                calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
+    if (!pJitTable) {
+        ALOGE("jit table allocation failed");
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+    /*
+     * NOTE: the profile table must only be allocated once, globally.
+     * Profiling is turned on and off by nulling out gDvm.pJitProfTable
+     * and then restoring its original value.  However, this action
+     * is not synchronized for speed so threads may continue to hold
+     * and update the profile table after profiling has been turned
+     * off by null'ng the global pointer.  Be aware.
+     */
+    pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
+    if (!pJitProfTable) {
+        ALOGE("jit prof table allocation failed");
+        free(pJitTable);
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+    memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+       pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
+    }
+    /* Is chain field wide enough for termination pattern? */
+    assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
+
+    /* Allocate the trace profiling structure */
+    pJitTraceProfCounters = (JitTraceProfCounters*)
+                             calloc(1, sizeof(*pJitTraceProfCounters));
+    if (!pJitTraceProfCounters) {
+        ALOGE("jit trace prof counters allocation failed");
+        free(pJitTable);
+        free(pJitProfTable);
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        goto fail;
+    }
+
+    gDvmJit.pJitEntryTable = pJitTable;
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    gDvmJit.jitTableEntriesUsed = 0;
+    gDvmJit.compilerHighWater =
+        COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
+    /*
+     * If the VM is launched with wait-on-the-debugger, we will need to hide
+     * the profile table here
+     */
+    gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable;
+    gDvmJit.pProfTableCopy = pJitProfTable;
+    gDvmJit.pJitTraceProfCounters = pJitTraceProfCounters;
+    dvmJitUpdateThreadStateAll();
+    dvmUnlockMutex(&gDvmJit.tableLock);
+
+    /* Signal running threads to refresh their cached pJitTable pointers */
+    dvmSuspendAllThreads(SUSPEND_FOR_REFRESH);
+    dvmResumeAllThreads(SUSPEND_FOR_REFRESH);
+
+    /* Enable signature breakpoints by customizing the following code */
+#if defined(SIGNATURE_BREAKPOINT)
+    /*
+     * Suppose one sees the following native crash in the bugreport:
+     * I/DEBUG   ( 1638): Build fingerprint: 'unknown'
+     * I/DEBUG   ( 1638): pid: 2468, tid: 2507  >>> com.google.android.gallery3d
+     * I/DEBUG   ( 1638): signal 11 (SIGSEGV), fault addr 00001400
+     * I/DEBUG   ( 1638):  r0 44ea7190  r1 44e4f7b8  r2 44ebc710  r3 00000000
+     * I/DEBUG   ( 1638):  r4 00000a00  r5 41862dec  r6 4710dc10  r7 00000280
+     * I/DEBUG   ( 1638):  r8 ad010f40  r9 46a37a12  10 001116b0  fp 42a78208
+     * I/DEBUG   ( 1638):  ip 00000090  sp 4710dbc8  lr ad060e67  pc 46b90682
+     * cpsr 00000030
+     * I/DEBUG   ( 1638):  #00  pc 46b90682 /dev/ashmem/dalvik-jit-code-cache
+     * I/DEBUG   ( 1638):  #01  pc 00060e62  /system/lib/libdvm.so
+     *
+     * I/DEBUG   ( 1638): code around pc:
+     * I/DEBUG   ( 1638): 46b90660 6888d01c 34091dcc d2174287 4a186b68
+     * I/DEBUG   ( 1638): 46b90670 d0052800 68006809 28004790 6b68d00e
+     * I/DEBUG   ( 1638): 46b90680 512000bc 37016eaf 6ea866af 6f696028
+     * I/DEBUG   ( 1638): 46b90690 682a6069 429a686b e003da08 6df1480b
+     * I/DEBUG   ( 1638): 46b906a0 1c2d4788 47806d70 46a378fa 47806d70
+     *
+     * Clearly it is a JIT bug. To find out which translation contains the
+     * offending code, the content of the memory dump around the faulting PC
+     * can be pasted into the gDvmJit.signatureBreakpoint[] array and next time
+     * when a similar compilation is being created, the JIT compiler replay the
+     * trace in the verbose mode and one can investigate the instruction
+     * sequence in details.
+     *
+     * The length of the signature may need additional experiments to determine.
+     * The rule of thumb is don't include PC-relative instructions in the
+     * signature since it may be affected by the alignment of the compiled code.
+     * However, a signature that's too short might increase the chance of false
+     * positive matches. Using gdbjithelper to disassembly the memory content
+     * first might be a good companion approach.
+     *
+     * For example, if the next 4 words starting from 46b90680 is pasted into
+     * the data structure:
+     */
+
+    gDvmJit.signatureBreakpointSize = 4;
+    gDvmJit.signatureBreakpoint =
+        malloc(sizeof(u4) * gDvmJit.signatureBreakpointSize);
+    gDvmJit.signatureBreakpoint[0] = 0x512000bc;
+    gDvmJit.signatureBreakpoint[1] = 0x37016eaf;
+    gDvmJit.signatureBreakpoint[2] = 0x6ea866af;
+    gDvmJit.signatureBreakpoint[3] = 0x6f696028;
+
+    /*
+     * The following log will be printed when a match is found in subsequent
+     * testings:
+     *
+     * D/dalvikvm( 2468): Signature match starting from offset 0x34 (4 words)
+     * D/dalvikvm( 2468): --------
+     * D/dalvikvm( 2468): Compiler: Building trace for computeVisibleItems,
+     * offset 0x1f7
+     * D/dalvikvm( 2468): 0x46a37a12: 0x0090 add-int v42, v5, v26
+     * D/dalvikvm( 2468): 0x46a37a16: 0x004d aput-object v13, v14, v42
+     * D/dalvikvm( 2468): 0x46a37a1a: 0x0028 goto, (#0), (#0)
+     * D/dalvikvm( 2468): 0x46a3794e: 0x00d8 add-int/lit8 v26, v26, (#1)
+     * D/dalvikvm( 2468): 0x46a37952: 0x0028 goto, (#0), (#0)
+     * D/dalvikvm( 2468): 0x46a378ee: 0x0002 move/from16 v0, v26, (#0)
+     * D/dalvikvm( 2468): 0x46a378f2: 0x0002 move/from16 v1, v29, (#0)
+     * D/dalvikvm( 2468): 0x46a378f6: 0x0035 if-ge v0, v1, (#10)
+     * D/dalvikvm( 2468): TRACEINFO (554): 0x46a37624
+     * Lcom/cooliris/media/GridLayer;computeVisibleItems 0x1f7 14 of 934, 8
+     * blocks
+     *     :
+     *     :
+     * D/dalvikvm( 2468): 0x20 (0020): ldr     r0, [r5, #52]
+     * D/dalvikvm( 2468): 0x22 (0022): ldr     r2, [pc, #96]
+     * D/dalvikvm( 2468): 0x24 (0024): cmp     r0, #0
+     * D/dalvikvm( 2468): 0x26 (0026): beq     0x00000034
+     * D/dalvikvm( 2468): 0x28 (0028): ldr     r1, [r1, #0]
+     * D/dalvikvm( 2468): 0x2a (002a): ldr     r0, [r0, #0]
+     * D/dalvikvm( 2468): 0x2c (002c): blx     r2
+     * D/dalvikvm( 2468): 0x2e (002e): cmp     r0, #0
+     * D/dalvikvm( 2468): 0x30 (0030): beq     0x00000050
+     * D/dalvikvm( 2468): 0x32 (0032): ldr     r0, [r5, #52]
+     * D/dalvikvm( 2468): 0x34 (0034): lsls    r4, r7, #2
+     * D/dalvikvm( 2468): 0x36 (0036): str     r0, [r4, r4]
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x01fb @ goto, (#0), (#0)
+     * D/dalvikvm( 2468): L0x0195:
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0195 @ add-int/lit8 v26,
+     * v26, (#1)
+     * D/dalvikvm( 2468): 0x38 (0038): ldr     r7, [r5, #104]
+     * D/dalvikvm( 2468): 0x3a (003a): adds    r7, r7, #1
+     * D/dalvikvm( 2468): 0x3c (003c): str     r7, [r5, #104]
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0197 @ goto, (#0), (#0)
+     * D/dalvikvm( 2468): L0x0165:
+     * D/dalvikvm( 2468): -------- dalvik offset: 0x0165 @ move/from16 v0, v26,
+     * (#0)
+     * D/dalvikvm( 2468): 0x3e (003e): ldr     r0, [r5, #104]
+     * D/dalvikvm( 2468): 0x40 (0040): str     r0, [r5, #0]
+     *
+     * The "str r0, [r4, r4]" is indeed the culprit of the native crash.
+     */
+#endif
+
+    return true;
+
+fail:
+    return false;
+
+}
+
+static void *compilerThreadStart(void *arg)
+{
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+    /*
+     * If we're not running stand-alone, wait a little before
+     * recieving translation requests on the assumption that process start
+     * up code isn't worth compiling.  We'll resume when the framework
+     * signals us that the first screen draw has happened, or the timer
+     * below expires (to catch daemons).
+     *
+     * There is a theoretical race between the callback to
+     * VMRuntime.startJitCompiation and when the compiler thread reaches this
+     * point. In case the callback happens earlier, in order not to permanently
+     * hold the system_server (which is not using the timed wait) in
+     * interpreter-only mode we bypass the delay here.
+     */
+    if (gDvmJit.runningInAndroidFramework &&
+        !gDvmJit.alreadyEnabledViaFramework) {
+        /*
+         * If the current VM instance is the system server (detected by having
+         * 0 in gDvm.systemServerPid), we will use the indefinite wait on the
+         * conditional variable to determine whether to start the JIT or not.
+         * If the system server detects that the whole system is booted in
+         * safe mode, the conditional variable will never be signaled and the
+         * system server will remain in the interpreter-only mode. All
+         * subsequent apps will be started with the --enable-safemode flag
+         * explicitly appended.
+         */
+        if (gDvm.systemServerPid == 0) {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                              &gDvmJit.compilerLock);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+            ALOGD("JIT started for system_server");
+        } else {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            /*
+             * TUNING: experiment with the delay & perhaps make it
+             * target-specific
+             */
+            dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
+                                 &gDvmJit.compilerLock, 3000, 0);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+        }
+        if (gDvmJit.haltCompilerThread) {
+             return NULL;
+        }
+    }
+
+    compilerThreadStartup();
+
+    dvmLockMutex(&gDvmJit.compilerLock);
+    /*
+     * Since the compiler thread will not touch any objects on the heap once
+     * being created, we just fake its state as VMWAIT so that it can be a
+     * bit late when there is suspend request pending.
+     */
+    while (!gDvmJit.haltCompilerThread) {
+        if (workQueueLength() == 0) {
+            int cc;
+            cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+            assert(cc == 0);
+            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                              &gDvmJit.compilerLock);
+            continue;
+        } else {
+            do {
+                CompilerWorkOrder work = workDequeue();
+                dvmUnlockMutex(&gDvmJit.compilerLock);
+#if defined(WITH_JIT_TUNING)
+                /*
+                 * This is live across setjmp().  Mark it volatile to suppress
+                 * a gcc warning.  We should not need this since it is assigned
+                 * only once but gcc is not smart enough.
+                 */
+                volatile u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+                /*
+                 * Check whether there is a suspend request on me.  This
+                 * is necessary to allow a clean shutdown.
+                 *
+                 * However, in the blocking stress testing mode, let the
+                 * compiler thread continue doing compilations to unblock
+                 * other requesting threads. This may occasionally cause
+                 * shutdown from proceeding cleanly in the standalone invocation
+                 * of the vm but this should be acceptable.
+                 */
+                if (!gDvmJit.blockingMode)
+                    dvmCheckSuspendPending(dvmThreadSelf());
+                /* Is JitTable filling up? */
+                if (gDvmJit.jitTableEntriesUsed >
+                    (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
+                    bool resizeFail =
+                        dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
+                    /*
+                     * If the jit table is full, consider it's time to reset
+                     * the code cache too.
+                     */
+                    gDvmJit.codeCacheFull |= resizeFail;
+                }
+                if (gDvmJit.haltCompilerThread) {
+                    ALOGD("Compiler shutdown in progress - discarding request");
+                } else if (!gDvmJit.codeCacheFull) {
+                    jmp_buf jmpBuf;
+                    work.bailPtr = &jmpBuf;
+                    bool aborted = setjmp(jmpBuf);
+                    if (!aborted) {
+                        bool codeCompiled = dvmCompilerDoWork(&work);
+                        /*
+                         * Make sure we are still operating with the
+                         * same translation cache version.  See
+                         * Issue 4271784 for details.
+                         */
+                        dvmLockMutex(&gDvmJit.compilerLock);
+                        if ((work.result.cacheVersion ==
+                             gDvmJit.cacheVersion) &&
+                             codeCompiled &&
+                             !work.result.discardResult &&
+                             work.result.codeAddress) {
+                            dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
+                                              work.result.instructionSet,
+                                              false, /* not method entry */
+                                              work.result.profileCodeSize);
+                        }
+                        dvmUnlockMutex(&gDvmJit.compilerLock);
+                    }
+                    dvmCompilerArenaReset();
+                }
+                free(work.info);
+#if defined(WITH_JIT_TUNING)
+                gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime;
+#endif
+                dvmLockMutex(&gDvmJit.compilerLock);
+            } while (workQueueLength() != 0);
+        }
+    }
+    pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /*
+     * As part of detaching the thread we need to call into Java code to update
+     * the ThreadGroup, and we should not be in VMWAIT state while executing
+     * interpreted code.
+     */
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+
+    if (gDvm.verboseShutdown)
+        ALOGD("Compiler thread shutting down");
+    return NULL;
+}
+
+bool dvmCompilerStartup(void)
+{
+
+    dvmInitMutex(&gDvmJit.compilerLock);
+    dvmInitMutex(&gDvmJit.compilerICPatchLock);
+    dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
+    dvmLockMutex(&gDvmJit.compilerLock);
+    dvmInitCondForTimedWait(&gDvmJit.compilerQueueActivity);
+    dvmInitCondForTimedWait(&gDvmJit.compilerQueueEmpty);
+
+    /* Reset the work queue */
+    gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+    gDvmJit.compilerQueueLength = 0;
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /*
+     * Defer rest of initialization until we're sure JIT'ng makes sense. Launch
+     * the compiler thread, which will do the real initialization if and
+     * when it is signalled to do so.
+     */
+    return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
+                                   compilerThreadStart, NULL);
+}
+
+void dvmCompilerShutdown(void)
+{
+    void *threadReturn;
+
+    /* Disable new translation requests */
+    gDvmJit.pProfTable = NULL;
+    gDvmJit.pProfTableCopy = NULL;
+    dvmJitUpdateThreadStateAll();
+
+    if (gDvm.verboseShutdown ||
+            gDvmJit.profileMode == kTraceProfilingContinuous) {
+        dvmCompilerDumpStats();
+        while (gDvmJit.compilerQueueLength)
+          sleep(5);
+    }
+
+    if (gDvmJit.compilerHandle) {
+
+        gDvmJit.haltCompilerThread = true;
+
+        dvmLockMutex(&gDvmJit.compilerLock);
+        pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+
+        if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
+            ALOGW("Compiler thread join failed");
+        else if (gDvm.verboseShutdown)
+            ALOGD("Compiler thread has shut down");
+    }
+
+    /* Break loops within the translation cache */
+    dvmJitUnchainAll();
+
+    /*
+     * NOTE: our current implementatation doesn't allow for the compiler
+     * thread to be restarted after it exits here.  We aren't freeing
+     * the JitTable or the ProfTable because threads which still may be
+     * running or in the process of shutting down may hold references to
+     * them.
+     */
+}
+
+void dvmCompilerUpdateGlobalState()
+{
+    bool jitActive;
+    bool jitActivate;
+    bool needUnchain = false;
+
+    /*
+     * The tableLock might not be initialized yet by the compiler thread if
+     * debugger is attached from the very beginning of the VM launch. If
+     * pProfTableCopy is NULL, the lock is not initialized yet and we don't
+     * need to refresh anything either.
+     */
+    if (gDvmJit.pProfTableCopy == NULL) {
+        return;
+    }
+
+    /*
+     * On the first enabling of method tracing, switch the compiler
+     * into a mode that includes trace support for invokes and returns.
+     * If there are any existing translations, flush them.  NOTE:  we
+     * can't blindly flush the translation cache because this code
+     * may be executed before the compiler thread has finished
+     * initialization.
+     */
+    if ((gDvm.activeProfilers != 0) &&
+        !gDvmJit.methodTraceSupport) {
+        bool resetRequired;
+        /*
+         * compilerLock will prevent new compilations from being
+         * installed while we are working.
+         */
+        dvmLockMutex(&gDvmJit.compilerLock);
+        gDvmJit.cacheVersion++; // invalidate compilations in flight
+        gDvmJit.methodTraceSupport = true;
+        resetRequired = (gDvmJit.numCompilations != 0);
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        if (resetRequired) {
+            dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
+            resetCodeCache();
+            dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
+        }
+    }
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    jitActive = gDvmJit.pProfTable != NULL;
+    jitActivate = !dvmDebuggerOrProfilerActive();
+
+    if (jitActivate && !jitActive) {
+        gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
+    } else if (!jitActivate && jitActive) {
+        gDvmJit.pProfTable = NULL;
+        needUnchain = true;
+    }
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    if (needUnchain)
+        dvmJitUnchainAll();
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+}
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
new file mode 100644
index 0000000..7af2809
--- /dev/null
+++ b/vm/compiler/Compiler.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_H_
+#define DALVIK_VM_COMPILER_H_
+
+#include <setjmp.h>
+#include "Thread.h"
+
+/*
+ * Uncomment the following to enable JIT signature breakpoint
+ * #define SIGNATURE_BREAKPOINT
+ */
+
+#define COMPILER_WORK_QUEUE_SIZE        100
+#define COMPILER_IC_PATCH_QUEUE_SIZE    64
+#define COMPILER_PC_OFFSET_SIZE         100
+
+/* Architectural-independent parameters for predicted chains */
+#define PREDICTED_CHAIN_CLAZZ_INIT       0
+#define PREDICTED_CHAIN_METHOD_INIT      0
+#define PREDICTED_CHAIN_COUNTER_INIT     0
+/* A fake value which will avoid initialization and won't match any class */
+#define PREDICTED_CHAIN_FAKE_CLAZZ       0xdeadc001
+/* Has to be positive */
+#define PREDICTED_CHAIN_COUNTER_AVOID    0x7fffffff
+/* Rechain after this many misses - shared globally and has to be positive */
+#define PREDICTED_CHAIN_COUNTER_RECHAIN  8192
+
+#define COMPILER_TRACED(X)
+#define COMPILER_TRACEE(X)
+#define COMPILER_TRACE_CHAINING(X)
+
+/* Macro to change the permissions applied to a chunk of the code cache */
+#define PROTECT_CODE_CACHE_ATTRS       (PROT_READ | PROT_EXEC)
+#define UNPROTECT_CODE_CACHE_ATTRS     (PROT_READ | PROT_EXEC | PROT_WRITE)
+
+/* Acquire the lock before removing PROT_WRITE from the specified mem region */
+#define UNPROTECT_CODE_CACHE(addr, size)                                       \
+    {                                                                          \
+        dvmLockMutex(&gDvmJit.codeCacheProtectionLock);                        \
+        mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask),       \
+                 (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask),        \
+                 (UNPROTECT_CODE_CACHE_ATTRS));                                \
+    }
+
+/* Add the PROT_WRITE to the specified memory region then release the lock */
+#define PROTECT_CODE_CACHE(addr, size)                                         \
+    {                                                                          \
+        mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask),       \
+                 (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask),        \
+                 (PROTECT_CODE_CACHE_ATTRS));                                  \
+        dvmUnlockMutex(&gDvmJit.codeCacheProtectionLock);                      \
+    }
+
+#define SINGLE_STEP_OP(opcode)                                                 \
+    (gDvmJit.includeSelectedOp !=                                              \
+     ((gDvmJit.opList[opcode >> 3] & (1 << (opcode & 0x7))) != 0))
+
+typedef enum JitInstructionSetType {
+    DALVIK_JIT_NONE = 0,
+    DALVIK_JIT_ARM,
+    DALVIK_JIT_THUMB,
+    DALVIK_JIT_THUMB2,
+    DALVIK_JIT_IA32,
+    DALVIK_JIT_MIPS
+} JitInstructionSetType;
+
+/* Description of a compiled trace. */
+typedef struct JitTranslationInfo {
+    void *codeAddress;
+    JitInstructionSetType instructionSet;
+    int profileCodeSize;
+    bool discardResult;         // Used for debugging divergence and IC patching
+    bool methodCompilationAborted;  // Cannot compile the whole method
+    Thread *requestingThread;   // For debugging purpose
+    int cacheVersion;           // Used to identify stale trace requests
+} JitTranslationInfo;
+
+typedef enum WorkOrderKind {
+    kWorkOrderInvalid = 0,      // Should never see by the backend
+    kWorkOrderMethod = 1,       // Work is to compile a whole method
+    kWorkOrderTrace = 2,        // Work is to compile code fragment(s)
+    kWorkOrderTraceDebug = 3,   // Work is to compile/debug code fragment(s)
+    kWorkOrderProfileMode = 4,  // Change profiling mode
+} WorkOrderKind;
+
+typedef struct CompilerWorkOrder {
+    const u2* pc;
+    WorkOrderKind kind;
+    void* info;
+    JitTranslationInfo result;
+    jmp_buf *bailPtr;
+} CompilerWorkOrder;
+
+/* Chain cell for predicted method invocation */
+typedef struct PredictedChainingCell {
+    u4 branch;                  /* Branch to chained destination */
+#ifdef __mips__
+    u4 delay_slot;              /* nop goes here */
+#elif defined(ARCH_IA32)
+    u4 branch2;                 /* IA32 branch instr may be > 32 bits */
+#endif
+    const ClassObject *clazz;   /* key for prediction */
+    const Method *method;       /* to lookup native PC from dalvik PC */
+    const ClassObject *stagedClazz;   /* possible next key for prediction */
+} PredictedChainingCell;
+
+/* Work order for inline cache patching */
+typedef struct ICPatchWorkOrder {
+    PredictedChainingCell *cellAddr;    /* Address to be patched */
+    PredictedChainingCell cellContent;  /* content of the new cell */
+    const char *classDescriptor;        /* Descriptor of the class object */
+    Object *classLoader;                /* Class loader */
+    u4 serialNumber;                    /* Serial # (for verification only) */
+} ICPatchWorkOrder;
+
+/*
+ * Trace description as will appear in the translation cache.  Note
+ * flexible array at end, as these will be of variable size.  To
+ * conserve space in the translation cache, total length of JitTraceRun
+ * array must be recomputed via seqential scan if needed.
+ */
+typedef struct {
+    const Method* method;
+    JitTraceRun trace[0];       // Variable-length trace descriptors
+} JitTraceDescription;
+
+typedef enum JitMethodAttributes {
+    kIsCallee = 0,      /* Code is part of a callee (invoked by a hot trace) */
+    kIsHot,             /* Code is part of a hot trace */
+    kIsLeaf,            /* Method is leaf */
+    kIsEmpty,           /* Method is empty */
+    kIsThrowFree,       /* Method doesn't throw */
+    kIsGetter,          /* Method fits the getter pattern */
+    kIsSetter,          /* Method fits the setter pattern */
+    kCannotCompile,     /* Method cannot be compiled */
+} JitMethodAttributes;
+
+#define METHOD_IS_CALLEE        (1 << kIsCallee)
+#define METHOD_IS_HOT           (1 << kIsHot)
+#define METHOD_IS_LEAF          (1 << kIsLeaf)
+#define METHOD_IS_EMPTY         (1 << kIsEmpty)
+#define METHOD_IS_THROW_FREE    (1 << kIsThrowFree)
+#define METHOD_IS_GETTER        (1 << kIsGetter)
+#define METHOD_IS_SETTER        (1 << kIsSetter)
+#define METHOD_CANNOT_COMPILE   (1 << kCannotCompile)
+
+/* Vectors to provide optimization hints */
+typedef enum JitOptimizationHints {
+    kJitOptNoLoop = 0,          // Disable loop formation/optimization
+} JitOptimizationHints;
+
+#define JIT_OPT_NO_LOOP         (1 << kJitOptNoLoop)
+
+/* Customized node traversal orders for different needs */
+typedef enum DataFlowAnalysisMode {
+    kAllNodes = 0,              // All nodes
+    kReachableNodes,            // All reachable nodes
+    kPreOrderDFSTraversal,      // Depth-First-Search / Pre-Order
+    kPostOrderDFSTraversal,     // Depth-First-Search / Post-Order
+    kPostOrderDOMTraversal,     // Dominator tree / Post-Order
+} DataFlowAnalysisMode;
+
+typedef struct CompilerMethodStats {
+    const Method *method;       // Used as hash entry signature
+    int dalvikSize;             // # of bytes for dalvik bytecodes
+    int compiledDalvikSize;     // # of compiled dalvik bytecodes
+    int nativeSize;             // # of bytes for produced native code
+    int attributes;             // attribute vector
+} CompilerMethodStats;
+
+struct CompilationUnit;
+struct BasicBlock;
+struct SSARepresentation;
+struct GrowableList;
+struct JitEntry;
+struct MIR;
+
+bool dvmCompilerSetupCodeCache(void);
+bool dvmCompilerArchInit(void);
+void dvmCompilerArchDump(void);
+bool dvmCompilerStartup(void);
+void dvmCompilerShutdown(void);
+void dvmCompilerForceWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
+bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
+void *dvmCheckCodeCache(void *method);
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+                                                  bool isCallee);
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+                                          const DecodedInstruction *insn);
+bool dvmCompileMethod(const Method *method, JitTranslationInfo *info);
+bool dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts,
+                     JitTranslationInfo *info, jmp_buf *bailPtr, int optHints);
+void dvmCompilerDumpStats(void);
+void dvmCompilerDrainQueue(void);
+void dvmJitUnchainAll(void);
+void dvmJitScanAllClassPointers(void (*callback)(void *ptr));
+void dvmCompilerSortAndPrintTraceProfiles(void);
+void dvmCompilerPerformSafePointChecks(void);
+void dvmCompilerInlineMIR(struct CompilationUnit *cUnit,
+                          JitTranslationInfo *info);
+void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
+int dvmConvertSSARegToDalvik(const struct CompilationUnit *cUnit, int ssaReg);
+bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
+void dvmCompilerInsertBackwardChaining(struct CompilationUnit *cUnit);
+void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
+bool dvmCompilerFindLocalLiveIn(struct CompilationUnit *cUnit,
+                                struct BasicBlock *bb);
+bool dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
+                                struct BasicBlock *bb);
+bool dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
+                                      struct BasicBlock *bb);
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+                                       struct BasicBlock *bb);
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+                                 struct BasicBlock *bb);
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
+                                      const char *note);
+char *dvmCompilerFullDisassembler(const struct CompilationUnit *cUnit,
+                                  const struct MIR *mir);
+char *dvmCompilerGetSSAString(struct CompilationUnit *cUnit,
+                              struct SSARepresentation *ssaRep);
+void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
+                bool (*func)(struct CompilationUnit *, struct BasicBlock *),
+                DataFlowAnalysisMode dfaMode,
+                bool isIterative);
+void dvmCompilerMethodSSATransformation(struct CompilationUnit *cUnit);
+bool dvmCompilerBuildLoop(struct CompilationUnit *cUnit);
+void dvmCompilerUpdateGlobalState(void);
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const struct JitEntry *desc);
+extern "C" void *dvmCompilerGetInterpretTemplate();
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet();
+u8 dvmGetRegResourceMask(int reg);
+void dvmDumpCFG(struct CompilationUnit *cUnit, const char *dirPrefix);
+bool dvmIsOpcodeSupportedByJit(Opcode opcode);
+
+#endif  // DALVIK_VM_COMPILER_H_
diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h
new file mode 100644
index 0000000..73efad8
--- /dev/null
+++ b/vm/compiler/CompilerIR.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_IR_H_
+#define DALVIK_VM_COMPILER_IR_H_
+
+#include "codegen/Optimizer.h"
+#ifdef ARCH_IA32
+#include "CompilerUtility.h"
+#endif
+
+typedef enum RegisterClass {
+    kCoreReg,
+    kFPReg,
+    kAnyReg,
+} RegisterClass;
+
+typedef enum RegLocationType {
+    kLocDalvikFrame = 0,
+    kLocPhysReg,
+    kLocRetval,          // Return region in interpState
+    kLocSpill,
+} RegLocationType;
+
+typedef struct RegLocation {
+    RegLocationType location:2;
+    unsigned wide:1;
+    unsigned fp:1;      // Hint for float/double
+    u1 lowReg:6;        // First physical register
+    u1 highReg:6;       // 2nd physical register (if wide)
+    s2 sRegLow;         // SSA name for low Dalvik word
+} RegLocation;
+
+#define INVALID_SREG (-1)
+#define INVALID_REG (0x3F)
+
+typedef enum BBType {
+    /* For coding convenience reasons chaining cell types should appear first */
+    kChainingCellNormal = 0,
+    kChainingCellHot,
+    kChainingCellInvokeSingleton,
+    kChainingCellInvokePredicted,
+    kChainingCellBackwardBranch,
+    kChainingCellGap,
+    /* Don't insert new fields between Gap and Last */
+    kChainingCellLast = kChainingCellGap + 1,
+    kEntryBlock,
+    kDalvikByteCode,
+    kExitBlock,
+    kPCReconstruction,
+    kExceptionHandling,
+    kCatchEntry,
+} BBType;
+
+typedef enum JitMode {
+    kJitTrace = 0, // Acyclic - all instructions come from the trace descriptor
+    kJitLoop,      // Cycle - trace descriptor is used as a hint
+    kJitMethod,    // Whole method
+} JitMode;
+
+typedef struct ChainCellCounts {
+    union {
+        u1 count[kChainingCellLast]; /* include one more space for the gap # */
+        u4 dummyForAlignment;
+    } u;
+} ChainCellCounts;
+
+typedef struct LIR {
+    int offset;
+    struct LIR *next;
+    struct LIR *prev;
+    struct LIR *target;
+} LIR;
+
+enum ExtendedMIROpcode {
+    kMirOpFirst = kNumPackedOpcodes,
+    kMirOpPhi = kMirOpFirst,
+    kMirOpNullNRangeUpCheck,
+    kMirOpNullNRangeDownCheck,
+    kMirOpLowerBound,
+    kMirOpPunt,
+    kMirOpCheckInlinePrediction,        // Gen checks for predicted inlining
+    kMirOpLast,
+};
+
+struct SSARepresentation;
+
+typedef enum {
+    kMIRIgnoreNullCheck = 0,
+    kMIRNullCheckOnly,
+    kMIRIgnoreRangeCheck,
+    kMIRRangeCheckOnly,
+    kMIRInlined,                        // Invoke is inlined (ie dead)
+    kMIRInlinedPred,                    // Invoke is inlined via prediction
+    kMIRCallee,                         // Instruction is inlined from callee
+    kMIRInvokeMethodJIT,                // Callee is JIT'ed as a whole method
+} MIROptimizationFlagPositons;
+
+#define MIR_IGNORE_NULL_CHECK           (1 << kMIRIgnoreNullCheck)
+#define MIR_NULL_CHECK_ONLY             (1 << kMIRNullCheckOnly)
+#define MIR_IGNORE_RANGE_CHECK          (1 << kMIRIgnoreRangeCheck)
+#define MIR_RANGE_CHECK_ONLY            (1 << kMIRRangeCheckOnly)
+#define MIR_INLINED                     (1 << kMIRInlined)
+#define MIR_INLINED_PRED                (1 << kMIRInlinedPred)
+#define MIR_CALLEE                      (1 << kMIRCallee)
+#define MIR_INVOKE_METHOD_JIT           (1 << kMIRInvokeMethodJIT)
+
+typedef struct CallsiteInfo {
+    const char *classDescriptor;
+    Object *classLoader;
+    const Method *method;
+    LIR *misPredBranchOver;
+} CallsiteInfo;
+
+typedef struct MIR {
+    DecodedInstruction dalvikInsn;
+    unsigned int width;
+    unsigned int offset;
+    struct MIR *prev;
+    struct MIR *next;
+    struct SSARepresentation *ssaRep;
+    int OptimizationFlags;
+    int seqNum;
+    union {
+        // Used by the inlined insn from the callee to find the mother method
+        const Method *calleeMethod;
+        // Used by the inlined invoke to find the class and method pointers
+        CallsiteInfo *callsiteInfo;
+    } meta;
+} MIR;
+
+struct BasicBlockDataFlow;
+
+/* For successorBlockList */
+typedef enum BlockListType {
+    kNotUsed = 0,
+    kCatch,
+    kPackedSwitch,
+    kSparseSwitch,
+} BlockListType;
+
+typedef struct BasicBlock {
+    int id;
+    bool visited;
+    bool hidden;
+    unsigned int startOffset;
+    const Method *containingMethod;     // For blocks from the callee
+    BBType blockType;
+    bool needFallThroughBranch;         // For blocks ended due to length limit
+    bool isFallThroughFromInvoke;       // True means the block needs alignment
+    MIR *firstMIRInsn;
+    MIR *lastMIRInsn;
+    struct BasicBlock *fallThrough;
+    struct BasicBlock *taken;
+    struct BasicBlock *iDom;            // Immediate dominator
+    struct BasicBlockDataFlow *dataFlowInfo;
+    BitVector *predecessors;
+    BitVector *dominators;
+    BitVector *iDominated;              // Set nodes being immediately dominated
+    BitVector *domFrontier;             // Dominance frontier
+    struct {                            // For one-to-many successors like
+        BlockListType blockListType;    // switch and exception handling
+        GrowableList blocks;
+    } successorBlockList;
+} BasicBlock;
+
+/*
+ * The "blocks" field in "successorBlockList" points to an array of
+ * elements with the type "SuccessorBlockInfo".
+ * For catch blocks, key is type index for the exception.
+ * For swtich blocks, key is the case value.
+ */
+typedef struct SuccessorBlockInfo {
+    BasicBlock *block;
+    int key;
+} SuccessorBlockInfo;
+
+struct LoopAnalysis;
+struct RegisterPool;
+
+typedef enum AssemblerStatus {
+    kSuccess,
+    kRetryAll,
+    kRetryHalve
+} AssemblerStatus;
+
+typedef struct CompilationUnit {
+    int numInsts;
+    int numBlocks;
+    GrowableList blockList;
+    const Method *method;
+#ifdef ARCH_IA32
+    int exceptionBlockId;               // the block corresponding to exception handling
+#endif
+    const JitTraceDescription *traceDesc;
+    LIR *firstLIRInsn;
+    LIR *lastLIRInsn;
+    LIR *literalList;                   // Constants
+    LIR *classPointerList;              // Relocatable
+    int numClassPointers;
+    LIR *chainCellOffsetLIR;
+    GrowableList pcReconstructionList;
+    int headerSize;                     // bytes before the first code ptr
+    int dataOffset;                     // starting offset of literal pool
+    int totalSize;                      // header + code size
+    AssemblerStatus assemblerStatus;    // Success or fix and retry
+    int assemblerRetries;               // How many times tried to fix assembly
+    unsigned char *codeBuffer;
+    void *baseAddr;
+    bool printMe;
+    bool allSingleStep;
+    bool hasClassLiterals;              // Contains class ptrs used as literals
+    bool hasLoop;                       // Contains a loop
+    bool hasInvoke;                     // Contains an invoke instruction
+    bool heapMemOp;                     // Mark mem ops for self verification
+    bool usesLinkRegister;              // For self-verification only
+    int profileCodeSize;                // Size of the profile prefix in bytes
+    int numChainingCells[kChainingCellGap];
+    LIR *firstChainingLIR[kChainingCellGap];
+    LIR *chainingCellBottom;
+    struct RegisterPool *regPool;
+    int optRound;                       // round number to tell an LIR's age
+    jmp_buf *bailPtr;
+    JitInstructionSetType instructionSet;
+    /* Number of total regs used in the whole cUnit after SSA transformation */
+    int numSSARegs;
+    /* Map SSA reg i to the Dalvik[15..0]/Sub[31..16] pair. */
+    GrowableList *ssaToDalvikMap;
+
+    /* The following are new data structures to support SSA representations */
+    /* Map original Dalvik reg i to the SSA[15..0]/Sub[31..16] pair */
+    int *dalvikToSSAMap;                // length == method->registersSize
+    BitVector *isConstantV;             // length == numSSAReg
+    int *constantValues;                // length == numSSAReg
+
+    /* Data structure for loop analysis and optimizations */
+    struct LoopAnalysis *loopAnalysis;
+
+    /* Map SSA names to location */
+    RegLocation *regLocation;
+    int sequenceNumber;
+
+    /*
+     * Set to the Dalvik PC of the switch instruction if it has more than
+     * MAX_CHAINED_SWITCH_CASES cases.
+     */
+    const u2 *switchOverflowPad;
+
+    JitMode jitMode;
+    int numReachableBlocks;
+    int numDalvikRegisters;             // method->registersSize + inlined
+    BasicBlock *entryBlock;
+    BasicBlock *exitBlock;
+    BasicBlock *puntBlock;              // punting to interp for exceptions
+    BasicBlock *backChainBlock;         // for loop-trace
+    BasicBlock *curBlock;
+    BasicBlock *nextCodegenBlock;       // for extended trace codegen
+    GrowableList dfsOrder;
+    GrowableList domPostOrderTraversal;
+    BitVector *tryBlockAddr;
+    BitVector **defBlockMatrix;         // numDalvikRegister x numBlocks
+    BitVector *tempBlockV;
+    BitVector *tempDalvikRegisterV;
+    BitVector *tempSSARegisterV;        // numSSARegs
+    bool printSSANames;
+    void *blockLabelList;
+    bool quitLoopMode;                  // cold path/complex bytecode
+} CompilationUnit;
+
+#if defined(WITH_SELF_VERIFICATION)
+#define HEAP_ACCESS_SHADOW(_state) cUnit->heapMemOp = _state
+#else
+#define HEAP_ACCESS_SHADOW(_state)
+#endif
+
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId);
+
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR);
+
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir);
+
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerAbort(CompilationUnit *cUnit);
+
+/* Debug Utilities */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit);
+
+#endif  // DALVIK_VM_COMPILER_IR_H_
diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h
new file mode 100644
index 0000000..d635286
--- /dev/null
+++ b/vm/compiler/CompilerInternals.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_INTERNAL_H_
+#define DALVIK_VM_COMPILER_INTERNAL_H_
+
+#include "Dalvik.h"
+#include "CompilerUtility.h"
+#include "codegen/CompilerCodegen.h"
+#include "interp/Jit.h"
+
+#endif  // DALVIK_VM_COMPILER_INTERNAL_H_
diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h
new file mode 100644
index 0000000..a06192d
--- /dev/null
+++ b/vm/compiler/CompilerUtility.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_UTILITY_H_
+#define DALVIK_VM_COMPILER_UTILITY_H_
+
+#include "Dalvik.h"
+
+/* Each arena page has some overhead, so take a few bytes off 8k */
+#define ARENA_DEFAULT_SIZE 8100
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void);
+
+typedef struct ArenaMemBlock {
+    size_t blockSize;
+    size_t bytesAllocated;
+    struct ArenaMemBlock *next;
+    char ptr[0];
+} ArenaMemBlock;
+
+void *dvmCompilerNew(size_t size, bool zero);
+
+void dvmCompilerArenaReset(void);
+
+typedef struct GrowableList {
+    size_t numAllocated;
+    size_t numUsed;
+    intptr_t *elemList;
+} GrowableList;
+
+typedef struct GrowableListIterator {
+    GrowableList *list;
+    size_t idx;
+    size_t size;
+} GrowableListIterator;
+
+#define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N])
+
+#define BLOCK_NAME_LEN 80
+
+/* Forward declarations */
+struct LIR;
+struct BasicBlock;
+
+void dvmInitGrowableList(GrowableList *gList, size_t initLength);
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem);
+void dvmGrowableListIteratorInit(GrowableList *gList,
+                                 GrowableListIterator *iterator);
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator);
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx);
+
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable);
+bool dvmCompilerSetBit(BitVector* pBits, unsigned int num);
+bool dvmCompilerClearBit(BitVector* pBits, unsigned int num);
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set);
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length);
+void dvmDumpLIRInsn(struct LIR *lir, unsigned char *baseAddr);
+void dvmDumpResourceMask(struct LIR *lir, u8 mask, const char *prefix);
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+                           const BitVector *bv, int length);
+void dvmGetBlockName(struct BasicBlock *bb, char *name);
+void dvmCompilerCacheFlush(uintptr_t start, uintptr_t end);
+void dvmCompilerCacheClear(char *start, size_t size);
+
+
+#endif  // DALVIK_COMPILER_UTILITY_H_
diff --git a/vm/compiler/Dataflow.cpp b/vm/compiler/Dataflow.cpp
new file mode 100644
index 0000000..7bed839
--- /dev/null
+++ b/vm/compiler/Dataflow.cpp
@@ -0,0 +1,1746 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/DexOpcodes.h"
+
+/*
+ * Main table containing data flow attributes for each bytecode. The
+ * first kNumPackedOpcodes entries are for Dalvik bytecode
+ * instructions, where extended opcode at the MIR level are appended
+ * afterwards.
+ *
+ * TODO - many optimization flags are incomplete - they will only limit the
+ * scope of optimizations but will not cause mis-optimizations.
+ */
+int dvmCompilerDataFlowAttributes[kMirOpLast] = {
+    // 00 OP_NOP
+    DF_NOP,
+
+    // 01 OP_MOVE vA, vB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 02 OP_MOVE_FROM16 vAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 03 OP_MOVE_16 vAAAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 04 OP_MOVE_WIDE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 05 OP_MOVE_WIDE_FROM16 vAA, vBBBB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 06 OP_MOVE_WIDE_16 vAAAA, vBBBB
+    DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+    // 07 OP_MOVE_OBJECT vA, vB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 08 OP_MOVE_OBJECT_FROM16 vAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 09 OP_MOVE_OBJECT_16 vAAAA, vBBBB
+    DF_DA | DF_UB | DF_IS_MOVE,
+
+    // 0A OP_MOVE_RESULT vAA
+    DF_DA,
+
+    // 0B OP_MOVE_RESULT_WIDE vAA
+    DF_DA_WIDE,
+
+    // 0C OP_MOVE_RESULT_OBJECT vAA
+    DF_DA,
+
+    // 0D OP_MOVE_EXCEPTION vAA
+    DF_DA,
+
+    // 0E OP_RETURN_VOID
+    DF_NOP,
+
+    // 0F OP_RETURN vAA
+    DF_UA,
+
+    // 10 OP_RETURN_WIDE vAA
+    DF_UA_WIDE,
+
+    // 11 OP_RETURN_OBJECT vAA
+    DF_UA,
+
+    // 12 OP_CONST_4 vA, #+B
+    DF_DA | DF_SETS_CONST,
+
+    // 13 OP_CONST_16 vAA, #+BBBB
+    DF_DA | DF_SETS_CONST,
+
+    // 14 OP_CONST vAA, #+BBBBBBBB
+    DF_DA | DF_SETS_CONST,
+
+    // 15 OP_CONST_HIGH16 VAA, #+BBBB0000
+    DF_DA | DF_SETS_CONST,
+
+    // 16 OP_CONST_WIDE_16 vAA, #+BBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 17 OP_CONST_WIDE_32 vAA, #+BBBBBBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 18 OP_CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 19 OP_CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
+    DF_DA_WIDE | DF_SETS_CONST,
+
+    // 1A OP_CONST_STRING vAA, string@BBBB
+    DF_DA,
+
+    // 1B OP_CONST_STRING_JUMBO vAA, string@BBBBBBBB
+    DF_DA,
+
+    // 1C OP_CONST_CLASS vAA, type@BBBB
+    DF_DA,
+
+    // 1D OP_MONITOR_ENTER vAA
+    DF_UA,
+
+    // 1E OP_MONITOR_EXIT vAA
+    DF_UA,
+
+    // 1F OP_CHECK_CAST vAA, type@BBBB
+    DF_UA,
+
+    // 20 OP_INSTANCE_OF vA, vB, type@CCCC
+    DF_DA | DF_UB,
+
+    // 21 OP_ARRAY_LENGTH vA, vB
+    DF_DA | DF_UB,
+
+    // 22 OP_NEW_INSTANCE vAA, type@BBBB
+    DF_DA,
+
+    // 23 OP_NEW_ARRAY vA, vB, type@CCCC
+    DF_DA | DF_UB,
+
+    // 24 OP_FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 25 OP_FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
+    DF_FORMAT_3RC,
+
+    // 26 OP_FILL_ARRAY_DATA vAA, +BBBBBBBB
+    DF_UA,
+
+    // 27 OP_THROW vAA
+    DF_UA,
+
+    // 28 OP_GOTO
+    DF_NOP,
+
+    // 29 OP_GOTO_16
+    DF_NOP,
+
+    // 2A OP_GOTO_32
+    DF_NOP,
+
+    // 2B OP_PACKED_SWITCH vAA, +BBBBBBBB
+    DF_UA,
+
+    // 2C OP_SPARSE_SWITCH vAA, +BBBBBBBB
+    DF_UA,
+
+    // 2D OP_CMPL_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+    // 2E OP_CMPG_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+    // 2F OP_CMPL_DOUBLE vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+    // 30 OP_CMPG_DOUBLE vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+    // 31 OP_CMP_LONG vAA, vBB, vCC
+    DF_DA | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 32 OP_IF_EQ vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 33 OP_IF_NE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 34 OP_IF_LT vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 35 OP_IF_GE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 36 OP_IF_GT vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+    // 37 OP_IF_LE vA, vB, +CCCC
+    DF_UA | DF_UB,
+
+
+    // 38 OP_IF_EQZ vAA, +BBBB
+    DF_UA,
+
+    // 39 OP_IF_NEZ vAA, +BBBB
+    DF_UA,
+
+    // 3A OP_IF_LTZ vAA, +BBBB
+    DF_UA,
+
+    // 3B OP_IF_GEZ vAA, +BBBB
+    DF_UA,
+
+    // 3C OP_IF_GTZ vAA, +BBBB
+    DF_UA,
+
+    // 3D OP_IF_LEZ vAA, +BBBB
+    DF_UA,
+
+    // 3E OP_UNUSED_3E
+    DF_NOP,
+
+    // 3F OP_UNUSED_3F
+    DF_NOP,
+
+    // 40 OP_UNUSED_40
+    DF_NOP,
+
+    // 41 OP_UNUSED_41
+    DF_NOP,
+
+    // 42 OP_UNUSED_42
+    DF_NOP,
+
+    // 43 OP_UNUSED_43
+    DF_NOP,
+
+    // 44 OP_AGET vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 45 OP_AGET_WIDE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 46 OP_AGET_OBJECT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 47 OP_AGET_BOOLEAN vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 48 OP_AGET_BYTE vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 49 OP_AGET_CHAR vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 4A OP_AGET_SHORT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+    // 4B OP_APUT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4C OP_APUT_WIDE vAA, vBB, vCC
+    DF_UA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_2 | DF_IS_SETTER,
+
+    // 4D OP_APUT_OBJECT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4E OP_APUT_BOOLEAN vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 4F OP_APUT_BYTE vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 50 OP_APUT_CHAR vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 51 OP_APUT_SHORT vAA, vBB, vCC
+    DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+    // 52 OP_IGET vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 53 OP_IGET_WIDE vA, vB, field@CCCC
+    DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+    // 54 OP_IGET_OBJECT vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 55 OP_IGET_BOOLEAN vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 56 OP_IGET_BYTE vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 57 OP_IGET_CHAR vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 58 OP_IGET_SHORT vA, vB, field@CCCC
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // 59 OP_IPUT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5A OP_IPUT_WIDE vA, vB, field@CCCC
+    DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+    // 5B OP_IPUT_OBJECT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5C OP_IPUT_BOOLEAN vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5D OP_IPUT_BYTE vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5E OP_IPUT_CHAR vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 5F OP_IPUT_SHORT vA, vB, field@CCCC
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // 60 OP_SGET vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 61 OP_SGET_WIDE vAA, field@BBBB
+    DF_DA_WIDE | DF_IS_GETTER,
+
+    // 62 OP_SGET_OBJECT vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 63 OP_SGET_BOOLEAN vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 64 OP_SGET_BYTE vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 65 OP_SGET_CHAR vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 66 OP_SGET_SHORT vAA, field@BBBB
+    DF_DA | DF_IS_GETTER,
+
+    // 67 OP_SPUT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 68 OP_SPUT_WIDE vAA, field@BBBB
+    DF_UA_WIDE | DF_IS_SETTER,
+
+    // 69 OP_SPUT_OBJECT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6A OP_SPUT_BOOLEAN vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6B OP_SPUT_BYTE vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6C OP_SPUT_CHAR vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6D OP_SPUT_SHORT vAA, field@BBBB
+    DF_UA | DF_IS_SETTER,
+
+    // 6E OP_INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 6F OP_INVOKE_SUPER {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 70 OP_INVOKE_DIRECT {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 71 OP_INVOKE_STATIC {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 72 OP_INVOKE_INTERFACE {vD, vE, vF, vG, vA}
+    DF_FORMAT_35C,
+
+    // 73 OP_UNUSED_73
+    DF_NOP,
+
+    // 74 OP_INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 75 OP_INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 76 OP_INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 77 OP_INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 78 OP_INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
+    DF_FORMAT_3RC,
+
+    // 79 OP_UNUSED_79
+    DF_NOP,
+
+    // 7A OP_UNUSED_7A
+    DF_NOP,
+
+    // 7B OP_NEG_INT vA, vB
+    DF_DA | DF_UB,
+
+    // 7C OP_NOT_INT vA, vB
+    DF_DA | DF_UB,
+
+    // 7D OP_NEG_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE,
+
+    // 7E OP_NOT_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE,
+
+    // 7F OP_NEG_FLOAT vA, vB
+    DF_DA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // 80 OP_NEG_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // 81 OP_INT_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB,
+
+    // 82 OP_INT_TO_FLOAT vA, vB
+    DF_DA | DF_UB | DF_FP_A,
+
+    // 83 OP_INT_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_A,
+
+    // 84 OP_LONG_TO_INT vA, vB
+    DF_DA | DF_UB_WIDE,
+
+    // 85 OP_LONG_TO_FLOAT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_A,
+
+    // 86 OP_LONG_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_A,
+
+    // 87 OP_FLOAT_TO_INT vA, vB
+    DF_DA | DF_UB | DF_FP_B,
+
+    // 88 OP_FLOAT_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_B,
+
+    // 89 OP_FLOAT_TO_DOUBLE vA, vB
+    DF_DA_WIDE | DF_UB | DF_FP_A | DF_FP_B,
+
+    // 8A OP_DOUBLE_TO_INT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_B,
+
+    // 8B OP_DOUBLE_TO_LONG vA, vB
+    DF_DA_WIDE | DF_UB_WIDE | DF_FP_B,
+
+    // 8C OP_DOUBLE_TO_FLOAT vA, vB
+    DF_DA | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // 8D OP_INT_TO_BYTE vA, vB
+    DF_DA | DF_UB,
+
+    // 8E OP_INT_TO_CHAR vA, vB
+    DF_DA | DF_UB,
+
+    // 8F OP_INT_TO_SHORT vA, vB
+    DF_DA | DF_UB,
+
+    // 90 OP_ADD_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+    // 91 OP_SUB_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+    // 92 OP_MUL_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 93 OP_DIV_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 94 OP_REM_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 95 OP_AND_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 96 OP_OR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 97 OP_XOR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 98 OP_SHL_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 99 OP_SHR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 9A OP_USHR_INT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC,
+
+    // 9B OP_ADD_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9C OP_SUB_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9D OP_MUL_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9E OP_DIV_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // 9F OP_REM_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A0 OP_AND_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A1 OP_OR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A2 OP_XOR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+    // A3 OP_SHL_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A4 OP_SHR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A5 OP_USHR_LONG vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+    // A6 OP_ADD_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A7 OP_SUB_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A8 OP_MUL_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // A9 OP_DIV_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AA OP_REM_FLOAT vAA, vBB, vCC
+    DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AB OP_ADD_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AC OP_SUB_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AD OP_MUL_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AE OP_DIV_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // AF OP_REM_DOUBLE vAA, vBB, vCC
+    DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+    // B0 OP_ADD_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B1 OP_SUB_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B2 OP_MUL_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B3 OP_DIV_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B4 OP_REM_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B5 OP_AND_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B6 OP_OR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B7 OP_XOR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B8 OP_SHL_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // B9 OP_SHR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // BA OP_USHR_INT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB,
+
+    // BB OP_ADD_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BC OP_SUB_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BD OP_MUL_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BE OP_DIV_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // BF OP_REM_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C0 OP_AND_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C1 OP_OR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C2 OP_XOR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+    // C3 OP_SHL_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C4 OP_SHR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C5 OP_USHR_LONG_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+    // C6 OP_ADD_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C7 OP_SUB_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C8 OP_MUL_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // C9 OP_DIV_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // CA OP_REM_FLOAT_2ADDR vA, vB
+    DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+    // CB OP_ADD_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CC OP_SUB_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CD OP_MUL_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CE OP_DIV_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // CF OP_REM_DOUBLE_2ADDR vA, vB
+    DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+    // D0 OP_ADD_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D1 OP_RSUB_INT vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D2 OP_MUL_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D3 OP_DIV_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D4 OP_REM_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D5 OP_AND_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D6 OP_OR_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D7 OP_XOR_INT_LIT16 vA, vB, #+CCCC
+    DF_DA | DF_UB,
+
+    // D8 OP_ADD_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB | DF_IS_LINEAR,
+
+    // D9 OP_RSUB_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DA OP_MUL_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DB OP_DIV_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DC OP_REM_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DD OP_AND_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DE OP_OR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // DF OP_XOR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E0 OP_SHL_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E1 OP_SHR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E2 OP_USHR_INT_LIT8 vAA, vBB, #+CC
+    DF_DA | DF_UB,
+
+    // E3 OP_IGET_VOLATILE
+    DF_DA | DF_UB,
+
+    // E4 OP_IPUT_VOLATILE
+    DF_UA | DF_UB,
+
+    // E5 OP_SGET_VOLATILE
+    DF_DA,
+
+    // E6 OP_SPUT_VOLATILE
+    DF_UA,
+
+    // E7 OP_IGET_OBJECT_VOLATILE
+    DF_DA | DF_UB,
+
+    // E8 OP_IGET_WIDE_VOLATILE
+    DF_DA_WIDE | DF_UB,
+
+    // E9 OP_IPUT_WIDE_VOLATILE
+    DF_UA_WIDE | DF_UB,
+
+    // EA OP_SGET_WIDE_VOLATILE
+    DF_DA_WIDE,
+
+    // EB OP_SPUT_WIDE_VOLATILE
+    DF_UA_WIDE,
+
+    // EC OP_BREAKPOINT
+    DF_NOP,
+
+    // ED OP_THROW_VERIFICATION_ERROR
+    DF_NOP,
+
+    // EE OP_EXECUTE_INLINE
+    DF_FORMAT_35C,
+
+    // EF OP_EXECUTE_INLINE_RANGE
+    DF_FORMAT_3RC,
+
+    // F0 OP_INVOKE_OBJECT_INIT_RANGE
+    DF_NOP,
+
+    // F1 OP_RETURN_VOID_BARRIER
+    DF_NOP,
+
+    // F2 OP_IGET_QUICK
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // F3 OP_IGET_WIDE_QUICK
+    DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+    // F4 OP_IGET_OBJECT_QUICK
+    DF_DA | DF_UB | DF_IS_GETTER,
+
+    // F5 OP_IPUT_QUICK
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // F6 OP_IPUT_WIDE_QUICK
+    DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+    // F7 OP_IPUT_OBJECT_QUICK
+    DF_UA | DF_UB | DF_IS_SETTER,
+
+    // F8 OP_INVOKE_VIRTUAL_QUICK
+    DF_FORMAT_35C,
+
+    // F9 OP_INVOKE_VIRTUAL_QUICK_RANGE
+    DF_FORMAT_3RC,
+
+    // FA OP_INVOKE_SUPER_QUICK
+    DF_FORMAT_35C,
+
+    // FB OP_INVOKE_SUPER_QUICK_RANGE
+    DF_FORMAT_3RC,
+
+    // FC OP_IPUT_OBJECT_VOLATILE
+    DF_UA | DF_UB,
+
+    // FD OP_SGET_OBJECT_VOLATILE
+    DF_DA,
+
+    // FE OP_SPUT_OBJECT_VOLATILE
+    DF_UA,
+
+    // FF OP_UNUSED_FF
+    DF_NOP,
+
+    // Beginning of extended MIR opcodes
+    // 100 OP_MIR_PHI
+    DF_PHI | DF_DA,
+    /*
+     * For extended MIR inserted at the MIR2LIR stage, it is okay to have
+     * undefined values here.
+     */
+};
+
+/* Return the Dalvik register/subscript pair of a given SSA register */
+int dvmConvertSSARegToDalvik(const CompilationUnit *cUnit, int ssaReg)
+{
+      return GET_ELEM_N(cUnit->ssaToDalvikMap, int, ssaReg);
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
+                                      const char *note)
+{
+    char buffer[256];
+    Opcode opcode = insn->opcode;
+    int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+    int flags;
+    char *ret;
+
+    buffer[0] = 0;
+    if ((int)opcode >= (int)kMirOpFirst) {
+        if ((int)opcode == (int)kMirOpPhi) {
+            strcpy(buffer, "PHI");
+        }
+        else {
+            sprintf(buffer, "Opcode %#x", opcode);
+        }
+        flags = 0;
+    } else {
+        strcpy(buffer, dexGetOpcodeName(opcode));
+        flags = dexGetFlagsFromOpcode(insn->opcode);
+    }
+
+    if (note)
+        strcat(buffer, note);
+
+    /* For branches, decode the instructions to print out the branch targets */
+    if (flags & kInstrCanBranch) {
+        InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+        int offset = 0;
+        switch (dalvikFormat) {
+            case kFmt21t:
+                snprintf(buffer + strlen(buffer), 256, " v%d,", insn->vA);
+                offset = (int) insn->vB;
+                break;
+            case kFmt22t:
+                snprintf(buffer + strlen(buffer), 256, " v%d, v%d,",
+                         insn->vA, insn->vB);
+                offset = (int) insn->vC;
+                break;
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                offset = (int) insn->vA;
+                break;
+            default:
+                ALOGE("Unexpected branch format %d / opcode %#x", dalvikFormat,
+                     opcode);
+                dvmAbort();
+                break;
+        }
+        snprintf(buffer + strlen(buffer), 256, " (%c%x)",
+                 offset > 0 ? '+' : '-',
+                 offset > 0 ? offset : -offset);
+    } else if (dfAttributes & DF_FORMAT_35C) {
+        unsigned int i;
+        for (i = 0; i < insn->vA; i++) {
+            if (i != 0) strcat(buffer, ",");
+            snprintf(buffer + strlen(buffer), 256, " v%d", insn->arg[i]);
+        }
+    }
+    else if (dfAttributes & DF_FORMAT_3RC) {
+        snprintf(buffer + strlen(buffer), 256,
+                 " v%d..v%d", insn->vC, insn->vC + insn->vA - 1);
+    }
+    else {
+        if (dfAttributes & DF_A_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, " v%d", insn->vA);
+        }
+        if (dfAttributes & DF_B_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vB);
+        }
+        else if ((int)opcode < (int)kMirOpFirst) {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vB);
+        }
+        if (dfAttributes & DF_C_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vC);
+        }
+        else if ((int)opcode < (int)kMirOpFirst) {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vC);
+        }
+    }
+    int length = strlen(buffer) + 1;
+    ret = (char *)dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+char *getSSAName(const CompilationUnit *cUnit, int ssaReg, char *name)
+{
+    int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaReg);
+
+    sprintf(name, "v%d_%d",
+            DECODE_REG(ssa2DalvikValue), DECODE_SUB(ssa2DalvikValue));
+    return name;
+}
+
+/*
+ * Dalvik instruction disassembler with optional SSA printing.
+ */
+char *dvmCompilerFullDisassembler(const CompilationUnit *cUnit,
+                                  const MIR *mir)
+{
+    char buffer[256];
+    char operand0[256], operand1[256];
+    const DecodedInstruction *insn = &mir->dalvikInsn;
+    int opcode = insn->opcode;
+    int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+    char *ret;
+    int length;
+    OpcodeFlags flags;
+
+    buffer[0] = 0;
+    if (opcode >= kMirOpFirst) {
+        if (opcode == kMirOpPhi) {
+            snprintf(buffer, 256, "PHI %s = (%s",
+                     getSSAName(cUnit, mir->ssaRep->defs[0], operand0),
+                     getSSAName(cUnit, mir->ssaRep->uses[0], operand1));
+            int i;
+            for (i = 1; i < mir->ssaRep->numUses; i++) {
+                snprintf(buffer + strlen(buffer), 256, ", %s",
+                         getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+            }
+            snprintf(buffer + strlen(buffer), 256, ")");
+        }
+        else {
+            sprintf(buffer, "Opcode %#x", opcode);
+        }
+        goto done;
+    } else {
+        strcpy(buffer, dexGetOpcodeName((Opcode)opcode));
+    }
+
+    flags = dexGetFlagsFromOpcode((Opcode)opcode);
+    /* For branches, decode the instructions to print out the branch targets */
+    if (flags & kInstrCanBranch) {
+        InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+        int delta = 0;
+        switch (dalvikFormat) {
+            case kFmt21t:
+                snprintf(buffer + strlen(buffer), 256, " %s, ",
+                         getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+                delta = (int) insn->vB;
+                break;
+            case kFmt22t:
+                snprintf(buffer + strlen(buffer), 256, " %s, %s, ",
+                         getSSAName(cUnit, mir->ssaRep->uses[0], operand0),
+                         getSSAName(cUnit, mir->ssaRep->uses[1], operand1));
+                delta = (int) insn->vC;
+                break;
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                delta = (int) insn->vA;
+                break;
+            default:
+                ALOGE("Unexpected branch format: %d", dalvikFormat);
+                dvmAbort();
+                break;
+        }
+        snprintf(buffer + strlen(buffer), 256, " %04x",
+                 mir->offset + delta);
+    } else if (dfAttributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) {
+        unsigned int i;
+        for (i = 0; i < insn->vA; i++) {
+            if (i != 0) strcat(buffer, ",");
+            snprintf(buffer + strlen(buffer), 256, " %s",
+                     getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+        }
+    } else {
+        int udIdx;
+        if (mir->ssaRep->numDefs) {
+
+            for (udIdx = 0; udIdx < mir->ssaRep->numDefs; udIdx++) {
+                snprintf(buffer + strlen(buffer), 256, " %s",
+                         getSSAName(cUnit, mir->ssaRep->defs[udIdx], operand0));
+            }
+            strcat(buffer, ",");
+        }
+        if (mir->ssaRep->numUses) {
+            /* No leading ',' for the first use */
+            snprintf(buffer + strlen(buffer), 256, " %s",
+                     getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+            for (udIdx = 1; udIdx < mir->ssaRep->numUses; udIdx++) {
+                snprintf(buffer + strlen(buffer), 256, ", %s",
+                         getSSAName(cUnit, mir->ssaRep->uses[udIdx], operand0));
+            }
+        }
+        if (opcode < kMirOpFirst) {
+            InstructionFormat dalvikFormat =
+                dexGetFormatFromOpcode((Opcode)opcode);
+            switch (dalvikFormat) {
+                case kFmt11n:        // op vA, #+B
+                case kFmt21s:        // op vAA, #+BBBB
+                case kFmt21h:        // op vAA, #+BBBB00000[00000000]
+                case kFmt31i:        // op vAA, #+BBBBBBBB
+                case kFmt51l:        // op vAA, #+BBBBBBBBBBBBBBBB
+                    snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vB);
+                    break;
+                case kFmt21c:        // op vAA, thing@BBBB
+                case kFmt31c:        // op vAA, thing@BBBBBBBB
+                    snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vB);
+                    break;
+                case kFmt22b:        // op vAA, vBB, #+CC
+                case kFmt22s:        // op vA, vB, #+CCCC
+                    snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vC);
+                    break;
+                case kFmt22c:        // op vA, vB, thing@CCCC
+                case kFmt22cs:       // [opt] op vA, vB, field offset CCCC
+                    snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vC);
+                    break;
+                    /* No need for special printing */
+                default:
+                    break;
+            }
+        }
+    }
+
+done:
+    length = strlen(buffer) + 1;
+    ret = (char *) dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetSSAString(CompilationUnit *cUnit, SSARepresentation *ssaRep)
+{
+    char buffer[256];
+    char *ret;
+    int i;
+
+    buffer[0] = 0;
+    for (i = 0; i < ssaRep->numDefs; i++) {
+        int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->defs[i]);
+
+        sprintf(buffer + strlen(buffer), "s%d(v%d_%d) ",
+                ssaRep->defs[i], DECODE_REG(ssa2DalvikValue),
+                DECODE_SUB(ssa2DalvikValue));
+    }
+
+    if (ssaRep->numDefs) {
+        strcat(buffer, "<- ");
+    }
+
+    for (i = 0; i < ssaRep->numUses; i++) {
+        int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->uses[i]);
+        int len = strlen(buffer);
+
+        if (snprintf(buffer + len, 250 - len, "s%d(v%d_%d) ",
+                     ssaRep->uses[i], DECODE_REG(ssa2DalvikValue),
+                     DECODE_SUB(ssa2DalvikValue)) >= (250 - len)) {
+            strcat(buffer, "...");
+            break;
+        }
+    }
+
+    int length = strlen(buffer) + 1;
+    ret = (char *)dvmCompilerNew(length, false);
+    memcpy(ret, buffer, length);
+    return ret;
+}
+
+/* Any register that is used before being defined is considered live-in */
+static inline void handleLiveInUse(BitVector *useV, BitVector *defV,
+                                   BitVector *liveInV, int dalvikRegId)
+{
+    dvmCompilerSetBit(useV, dalvikRegId);
+    if (!dvmIsBitSet(defV, dalvikRegId)) {
+        dvmCompilerSetBit(liveInV, dalvikRegId);
+    }
+}
+
+/* Mark a reg as being defined */
+static inline void handleDef(BitVector *defV, int dalvikRegId)
+{
+    dvmCompilerSetBit(defV, dalvikRegId);
+}
+
+/*
+ * Find out live-in variables for natural loops. Variables that are live-in in
+ * the main loop body are considered to be defined in the entry block.
+ */
+bool dvmCompilerFindLocalLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    BitVector *useV, *defV, *liveInV;
+
+    if (bb->dataFlowInfo == NULL) return false;
+
+    useV = bb->dataFlowInfo->useV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+    defV = bb->dataFlowInfo->defV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+    liveInV = bb->dataFlowInfo->liveInV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (dfAttributes & DF_HAS_USES) {
+            if (dfAttributes & DF_UA) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+            } else if (dfAttributes & DF_UA_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vA+1);
+            }
+            if (dfAttributes & DF_UB) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+            } else if (dfAttributes & DF_UB_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vB+1);
+            }
+            if (dfAttributes & DF_UC) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+            } else if (dfAttributes & DF_UC_WIDE) {
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+                handleLiveInUse(useV, defV, liveInV, dInsn->vC+1);
+            }
+        }
+        if (dfAttributes & DF_HAS_DEFS) {
+            handleDef(defV, dInsn->vA);
+            if (dfAttributes & DF_DA_WIDE) {
+                handleDef(defV, dInsn->vA+1);
+            }
+        }
+    }
+    return true;
+}
+
+/* Find out the latest SSA register for a given Dalvik register */
+static void handleSSAUse(CompilationUnit *cUnit, int *uses, int dalvikReg,
+                         int regIndex)
+{
+    int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+    int ssaReg = DECODE_REG(encodedValue);
+    uses[regIndex] = ssaReg;
+}
+
+/* Setup a new SSA register for a given Dalvik register */
+static void handleSSADef(CompilationUnit *cUnit, int *defs, int dalvikReg,
+                         int regIndex)
+{
+    int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+    int ssaReg = cUnit->numSSARegs++;
+    /* Bump up the subscript */
+    int dalvikSub = DECODE_SUB(encodedValue) + 1;
+    int newD2SMapping = ENCODE_REG_SUB(ssaReg, dalvikSub);
+
+    cUnit->dalvikToSSAMap[dalvikReg] = newD2SMapping;
+
+    int newS2DMapping = ENCODE_REG_SUB(dalvikReg, dalvikSub);
+    dvmInsertGrowableList(cUnit->ssaToDalvikMap, newS2DMapping);
+
+    defs[regIndex] = ssaReg;
+}
+
+/* Loop up new SSA names for format_35c instructions */
+static void dataFlowSSAFormat35C(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int numUses = dInsn->vA;
+    int i;
+
+    mir->ssaRep->numUses = numUses;
+    mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
+
+    for (i = 0; i < numUses; i++) {
+        handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->arg[i], i);
+    }
+}
+
+/* Loop up new SSA names for format_3rc instructions */
+static void dataFlowSSAFormat3RC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int numUses = dInsn->vA;
+    int i;
+
+    mir->ssaRep->numUses = numUses;
+    mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
+
+    for (i = 0; i < numUses; i++) {
+        handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+i, i);
+    }
+}
+
+/* Entry function to convert a block into SSA representation */
+bool dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+
+    if (bb->dataFlowInfo == NULL) return false;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        mir->ssaRep = (struct SSARepresentation *)
+            dvmCompilerNew(sizeof(SSARepresentation), true);
+
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        int numUses = 0;
+
+        if (dfAttributes & DF_FORMAT_35C) {
+            dataFlowSSAFormat35C(cUnit, mir);
+            continue;
+        }
+
+        if (dfAttributes & DF_FORMAT_3RC) {
+            dataFlowSSAFormat3RC(cUnit, mir);
+            continue;
+        }
+
+        if (dfAttributes & DF_HAS_USES) {
+            if (dfAttributes & DF_UA) {
+                numUses++;
+            } else if (dfAttributes & DF_UA_WIDE) {
+                numUses += 2;
+            }
+            if (dfAttributes & DF_UB) {
+                numUses++;
+            } else if (dfAttributes & DF_UB_WIDE) {
+                numUses += 2;
+            }
+            if (dfAttributes & DF_UC) {
+                numUses++;
+            } else if (dfAttributes & DF_UC_WIDE) {
+                numUses += 2;
+            }
+        }
+
+        if (numUses) {
+            mir->ssaRep->numUses = numUses;
+            mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses,
+                                                      false);
+            mir->ssaRep->fpUse = (bool *)dvmCompilerNew(sizeof(bool) * numUses,
+                                                false);
+        }
+
+        int numDefs = 0;
+
+        if (dfAttributes & DF_HAS_DEFS) {
+            numDefs++;
+            if (dfAttributes & DF_DA_WIDE) {
+                numDefs++;
+            }
+        }
+
+        if (numDefs) {
+            mir->ssaRep->numDefs = numDefs;
+            mir->ssaRep->defs = (int *)dvmCompilerNew(sizeof(int) * numDefs,
+                                                      false);
+            mir->ssaRep->fpDef = (bool *)dvmCompilerNew(sizeof(bool) * numDefs,
+                                                        false);
+        }
+
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (dfAttributes & DF_HAS_USES) {
+            numUses = 0;
+            if (dfAttributes & DF_UA) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+            } else if (dfAttributes & DF_UA_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA+1, numUses++);
+            }
+            if (dfAttributes & DF_UB) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+            } else if (dfAttributes & DF_UB_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB+1, numUses++);
+            }
+            if (dfAttributes & DF_UC) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+            } else if (dfAttributes & DF_UC_WIDE) {
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+                mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+                handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+1, numUses++);
+            }
+        }
+        if (dfAttributes & DF_HAS_DEFS) {
+            mir->ssaRep->fpDef[0] = dfAttributes & DF_FP_A;
+            handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA, 0);
+            if (dfAttributes & DF_DA_WIDE) {
+                mir->ssaRep->fpDef[1] = dfAttributes & DF_FP_A;
+                handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA+1, 1);
+            }
+        }
+    }
+
+    /*
+     * Take a snapshot of Dalvik->SSA mapping at the end of each block. The
+     * input to PHI nodes can be derived from the snapshot of all predecessor
+     * blocks.
+     */
+    bb->dataFlowInfo->dalvikToSSAMap =
+        (int *)dvmCompilerNew(sizeof(int) * cUnit->method->registersSize,
+                              false);
+
+    memcpy(bb->dataFlowInfo->dalvikToSSAMap, cUnit->dalvikToSSAMap,
+           sizeof(int) * cUnit->method->registersSize);
+    return true;
+}
+
+/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
+static void setConstant(CompilationUnit *cUnit, int ssaReg, int value)
+{
+    dvmSetBit(cUnit->isConstantV, ssaReg);
+    cUnit->constantValues[ssaReg] = value;
+}
+
+bool dvmCompilerDoConstantPropagation(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    BitVector *isConstantV = cUnit->isConstantV;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+        if (!(dfAttributes & DF_HAS_DEFS)) continue;
+
+        /* Handle instructions that set up constants directly */
+        if (dfAttributes & DF_SETS_CONST) {
+            if (dfAttributes & DF_DA) {
+                switch (dInsn->opcode) {
+                    case OP_CONST_4:
+                    case OP_CONST_16:
+                    case OP_CONST:
+                        setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+                        break;
+                    case OP_CONST_HIGH16:
+                        setConstant(cUnit, mir->ssaRep->defs[0],
+                                    dInsn->vB << 16);
+                        break;
+                    default:
+                        break;
+                }
+            } else if (dfAttributes & DF_DA_WIDE) {
+                switch (dInsn->opcode) {
+                    case OP_CONST_WIDE_16:
+                    case OP_CONST_WIDE_32:
+                        setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+                        setConstant(cUnit, mir->ssaRep->defs[1], 0);
+                        break;
+                    case OP_CONST_WIDE:
+                        setConstant(cUnit, mir->ssaRep->defs[0],
+                                    (int) dInsn->vB_wide);
+                        setConstant(cUnit, mir->ssaRep->defs[1],
+                                    (int) (dInsn->vB_wide >> 32));
+                        break;
+                    case OP_CONST_WIDE_HIGH16:
+                        setConstant(cUnit, mir->ssaRep->defs[0], 0);
+                        setConstant(cUnit, mir->ssaRep->defs[1],
+                                    dInsn->vB << 16);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        /* Handle instructions that set up constants directly */
+        } else if (dfAttributes & DF_IS_MOVE) {
+            int i;
+
+            for (i = 0; i < mir->ssaRep->numUses; i++) {
+                if (!dvmIsBitSet(isConstantV, mir->ssaRep->uses[i])) break;
+            }
+            /* Move a register holding a constant to another register */
+            if (i == mir->ssaRep->numUses) {
+                setConstant(cUnit, mir->ssaRep->defs[0],
+                            cUnit->constantValues[mir->ssaRep->uses[0]]);
+                if (dfAttributes & DF_DA_WIDE) {
+                    setConstant(cUnit, mir->ssaRep->defs[1],
+                                cUnit->constantValues[mir->ssaRep->uses[1]]);
+                }
+            }
+        }
+    }
+    /* TODO: implement code to handle arithmetic operations */
+    return true;
+}
+
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+                                       struct BasicBlock *bb)
+{
+    BitVector *isIndVarV = cUnit->loopAnalysis->isIndVarV;
+    BitVector *isConstantV = cUnit->isConstantV;
+    GrowableList *ivList = cUnit->loopAnalysis->ivList;
+    MIR *mir;
+
+    if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock) {
+        return false;
+    }
+
+    /* If the bb doesn't have a phi it cannot contain an induction variable */
+    if (bb->firstMIRInsn == NULL ||
+        (int)bb->firstMIRInsn->dalvikInsn.opcode != (int)kMirOpPhi) {
+        return false;
+    }
+
+    /* Find basic induction variable first */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+        /*
+         * For a basic induction variable:
+         *   1) use[0] should belong to the output of a phi node
+         *   2) def[0] should belong to the input of the same phi node
+         *   3) the value added/subtracted is a constant
+         */
+        MIR *phi;
+        for (phi = bb->firstMIRInsn; phi; phi = phi->next) {
+            if ((int)phi->dalvikInsn.opcode != (int)kMirOpPhi) break;
+
+            if (phi->ssaRep->defs[0] == mir->ssaRep->uses[0] &&
+                phi->ssaRep->uses[1] == mir->ssaRep->defs[0]) {
+                bool deltaIsConstant = false;
+                int deltaValue;
+
+                switch (mir->dalvikInsn.opcode) {
+                    case OP_ADD_INT:
+                        if (dvmIsBitSet(isConstantV,
+                                        mir->ssaRep->uses[1])) {
+                            deltaValue =
+                                cUnit->constantValues[mir->ssaRep->uses[1]];
+                            deltaIsConstant = true;
+                        }
+                        break;
+                    case OP_SUB_INT:
+                        if (dvmIsBitSet(isConstantV,
+                                        mir->ssaRep->uses[1])) {
+                            deltaValue =
+                                -cUnit->constantValues[mir->ssaRep->uses[1]];
+                            deltaIsConstant = true;
+                        }
+                        break;
+                    case OP_ADD_INT_LIT8:
+                        deltaValue = mir->dalvikInsn.vC;
+                        deltaIsConstant = true;
+                        break;
+                    default:
+                        break;
+                }
+                if (deltaIsConstant) {
+                    dvmSetBit(isIndVarV, mir->ssaRep->uses[0]);
+                    InductionVariableInfo *ivInfo = (InductionVariableInfo *)
+                        dvmCompilerNew(sizeof(InductionVariableInfo),
+                                       false);
+
+                    ivInfo->ssaReg = mir->ssaRep->uses[0];
+                    ivInfo->basicSSAReg = mir->ssaRep->uses[0];
+                    ivInfo->m = 1;         // always 1 to basic iv
+                    ivInfo->c = 0;         // N/A to basic iv
+                    ivInfo->inc = deltaValue;
+                    dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
+                    cUnit->loopAnalysis->numBasicIV++;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* Find dependent induction variable now */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+        /* Skip already identified induction variables */
+        if (dvmIsBitSet(isIndVarV, mir->ssaRep->defs[0])) continue;
+
+        /*
+         * For a dependent induction variable:
+         *  1) use[0] should be an induction variable (basic/dependent)
+         *  2) operand2 should be a constant
+         */
+        if (dvmIsBitSet(isIndVarV, mir->ssaRep->uses[0])) {
+            int srcDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+                                                        mir->ssaRep->uses[0]);
+            int dstDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+                                                        mir->ssaRep->defs[0]);
+
+            bool cIsConstant = false;
+            int c = 0;
+
+            switch (mir->dalvikInsn.opcode) {
+                case OP_ADD_INT:
+                    if (dvmIsBitSet(isConstantV,
+                                    mir->ssaRep->uses[1])) {
+                        c = cUnit->constantValues[mir->ssaRep->uses[1]];
+                        cIsConstant = true;
+                    }
+                    break;
+                case OP_SUB_INT:
+                    if (dvmIsBitSet(isConstantV,
+                                    mir->ssaRep->uses[1])) {
+                        c = -cUnit->constantValues[mir->ssaRep->uses[1]];
+                        cIsConstant = true;
+                    }
+                    break;
+                case OP_ADD_INT_LIT8:
+                    c = mir->dalvikInsn.vC;
+                    cIsConstant = true;
+                    break;
+                default:
+                    break;
+            }
+
+            /* Ignore the update to the basic induction variable itself */
+            if (DECODE_REG(srcDalvikReg) == DECODE_REG(dstDalvikReg))  {
+                cUnit->loopAnalysis->ssaBIV = mir->ssaRep->defs[0];
+                cIsConstant = false;
+            }
+
+            if (cIsConstant) {
+                unsigned int i;
+                dvmSetBit(isIndVarV, mir->ssaRep->defs[0]);
+                InductionVariableInfo *ivInfo = (InductionVariableInfo *)
+                    dvmCompilerNew(sizeof(InductionVariableInfo),
+                                   false);
+                InductionVariableInfo *ivInfoOld = NULL ;
+
+                for (i = 0; i < ivList->numUsed; i++) {
+                    ivInfoOld = (InductionVariableInfo *) ivList->elemList[i];
+                    if (ivInfoOld->ssaReg == mir->ssaRep->uses[0]) break;
+                }
+
+                /* Guaranteed to find an element */
+                assert(i < ivList->numUsed);
+
+                ivInfo->ssaReg = mir->ssaRep->defs[0];
+                ivInfo->basicSSAReg = ivInfoOld->basicSSAReg;
+                ivInfo->m = ivInfoOld->m;
+                ivInfo->c = c + ivInfoOld->c;
+                ivInfo->inc = ivInfoOld->inc;
+                dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
+            }
+        }
+    }
+    return true;
+}
+
+/* Setup the basic data structures for SSA conversion */
+void dvmInitializeSSAConversion(CompilationUnit *cUnit)
+{
+    int i;
+    int numDalvikReg = cUnit->method->registersSize;
+
+    cUnit->ssaToDalvikMap = (GrowableList *)dvmCompilerNew(sizeof(GrowableList),
+                                                           false);
+    dvmInitGrowableList(cUnit->ssaToDalvikMap, numDalvikReg);
+
+    /*
+     * Initial number of SSA registers is equal to the number of Dalvik
+     * registers.
+     */
+    cUnit->numSSARegs = numDalvikReg;
+
+    /*
+     * Initialize the SSA2Dalvik map list. For the first numDalvikReg elements,
+     * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+     * into "(0 << 16) | i"
+     */
+    for (i = 0; i < numDalvikReg; i++) {
+        dvmInsertGrowableList(cUnit->ssaToDalvikMap, ENCODE_REG_SUB(i, 0));
+    }
+
+    /*
+     * Initialize the DalvikToSSAMap map. The low 16 bit is the SSA register id,
+     * while the high 16 bit is the current subscript. The original Dalvik
+     * register N is mapped to SSA register N with subscript 0.
+     */
+    cUnit->dalvikToSSAMap = (int *)dvmCompilerNew(sizeof(int) * numDalvikReg,
+                                                  false);
+    for (i = 0; i < numDalvikReg; i++) {
+        cUnit->dalvikToSSAMap[i] = i;
+    }
+
+    /*
+     * Allocate the BasicBlockDataFlow structure for the entry and code blocks
+     */
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->hidden == true) continue;
+        if (bb->blockType == kDalvikByteCode ||
+            bb->blockType == kEntryBlock ||
+            bb->blockType == kExitBlock) {
+            bb->dataFlowInfo = (BasicBlockDataFlow *)
+                dvmCompilerNew(sizeof(BasicBlockDataFlow),
+                               true);
+        }
+    }
+}
+
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+                                 struct BasicBlock *bb)
+{
+    bb->visited = false;
+    return true;
+}
+
+void dvmCompilerDataFlowAnalysisDispatcher(CompilationUnit *cUnit,
+                bool (*func)(CompilationUnit *, BasicBlock *),
+                DataFlowAnalysisMode dfaMode,
+                bool isIterative)
+{
+    bool change = true;
+
+    while (change) {
+        change = false;
+
+        /* Scan all blocks and perform the operations specified in func */
+        if (dfaMode == kAllNodes) {
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+            while (true) {
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+                if (bb == NULL) break;
+                if (bb->hidden == true) continue;
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks and perform the operations specified in
+         * func.
+         */
+        else if (dfaMode == kReachableNodes) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int blockIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                             blockIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the pre-order in the depth-first-search
+         * CFG and perform the operations specified in func.
+         */
+        else if (dfaMode == kPreOrderDFSTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int dfsIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the post-order in the depth-first-search
+         * CFG and perform the operations specified in func.
+         */
+        else if (dfaMode == kPostOrderDFSTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = numReachableBlocks - 1; idx >= 0; idx--) {
+                int dfsIdx = cUnit->dfsOrder.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /*
+         * Scan all reachable blocks by the post-order in the dominator tree
+         * and perform the operations specified in func.
+         */
+        else if (dfaMode == kPostOrderDOMTraversal) {
+            int numReachableBlocks = cUnit->numReachableBlocks;
+            int idx;
+            const GrowableList *blockList = &cUnit->blockList;
+
+            for (idx = 0; idx < numReachableBlocks; idx++) {
+                int domIdx = cUnit->domPostOrderTraversal.elemList[idx];
+                BasicBlock *bb =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, domIdx);
+                change |= (*func)(cUnit, bb);
+            }
+        }
+        /* If isIterative is false, exit the loop after the first iteration */
+        change &= isIterative;
+    }
+}
+
+/* Main entry point to do SSA conversion for non-loop traces */
+void dvmCompilerNonLoopAnalysis(CompilationUnit *cUnit)
+{
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kAllNodes,
+                                          false /* isIterative */);
+}
diff --git a/vm/compiler/Dataflow.h b/vm/compiler/Dataflow.h
new file mode 100644
index 0000000..f04c91c
--- /dev/null
+++ b/vm/compiler/Dataflow.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_DATAFLOW_H_
+#define DALVIK_VM_DATAFLOW_H_
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef enum DataFlowAttributePos {
+    kUA = 0,
+    kUB,
+    kUC,
+    kUAWide,
+    kUBWide,
+    kUCWide,
+    kDA,
+    kDAWide,
+    kIsMove,
+    kIsLinear,
+    kSetsConst,
+    kFormat35c,
+    kFormat3rc,
+    kPhi,
+    kNullNRangeCheck0,
+    kNullNRangeCheck1,
+    kNullNRangeCheck2,
+    kFPA,
+    kFPB,
+    kFPC,
+    kGetter,
+    kSetter,
+} DataFlowAttributes;
+
+#define DF_NOP                  0
+#define DF_UA                   (1 << kUA)
+#define DF_UB                   (1 << kUB)
+#define DF_UC                   (1 << kUC)
+#define DF_UA_WIDE              (1 << kUAWide)
+#define DF_UB_WIDE              (1 << kUBWide)
+#define DF_UC_WIDE              (1 << kUCWide)
+#define DF_DA                   (1 << kDA)
+#define DF_DA_WIDE              (1 << kDAWide)
+#define DF_IS_MOVE              (1 << kIsMove)
+#define DF_IS_LINEAR            (1 << kIsLinear)
+#define DF_SETS_CONST           (1 << kSetsConst)
+#define DF_FORMAT_35C           (1 << kFormat35c)
+#define DF_FORMAT_3RC           (1 << kFormat3rc)
+#define DF_PHI                  (1 << kPhi)
+#define DF_NULL_N_RANGE_CHECK_0 (1 << kNullNRangeCheck0)
+#define DF_NULL_N_RANGE_CHECK_1 (1 << kNullNRangeCheck1)
+#define DF_NULL_N_RANGE_CHECK_2 (1 << kNullNRangeCheck2)
+#define DF_FP_A                 (1 << kFPA)
+#define DF_FP_B                 (1 << kFPB)
+#define DF_FP_C                 (1 << kFPC)
+#define DF_IS_GETTER            (1 << kGetter)
+#define DF_IS_SETTER            (1 << kSetter)
+
+#define DF_HAS_USES             (DF_UA | DF_UB | DF_UC | DF_UA_WIDE | \
+                                 DF_UB_WIDE | DF_UC_WIDE)
+
+#define DF_HAS_DEFS             (DF_DA | DF_DA_WIDE)
+
+#define DF_HAS_NR_CHECKS        (DF_NULL_N_RANGE_CHECK_0 | \
+                                 DF_NULL_N_RANGE_CHECK_1 | \
+                                 DF_NULL_N_RANGE_CHECK_2)
+
+#define DF_A_IS_REG             (DF_UA | DF_UA_WIDE | DF_DA | DF_DA_WIDE)
+#define DF_B_IS_REG             (DF_UB | DF_UB_WIDE)
+#define DF_C_IS_REG             (DF_UC | DF_UC_WIDE)
+#define DF_IS_GETTER_OR_SETTER  (DF_IS_GETTER | DF_IS_SETTER)
+
+extern int dvmCompilerDataFlowAttributes[kMirOpLast];
+
+typedef struct BasicBlockDataFlow {
+    BitVector *useV;
+    BitVector *defV;
+    BitVector *liveInV;
+    BitVector *phiV;
+    int *dalvikToSSAMap;
+} BasicBlockDataFlow;
+
+typedef struct SSARepresentation {
+    int numUses;
+    int *uses;
+    bool *fpUse;
+    int numDefs;
+    int *defs;
+    bool *fpDef;
+} SSARepresentation;
+
+/*
+ * An induction variable is represented by "m*i + c", where i is a basic
+ * induction variable.
+ */
+typedef struct InductionVariableInfo {
+    int ssaReg;
+    int basicSSAReg;
+    int m;      // multiplier
+    int c;      // constant
+    int inc;    // loop incriment
+} InductionVariableInfo;
+
+typedef struct ArrayAccessInfo {
+    int arrayReg;
+    int ivReg;
+    int maxC;                   // For DIV - will affect upper bound checking
+    int minC;                   // For DIV - will affect lower bound checking
+} ArrayAccessInfo;
+
+#define ENCODE_REG_SUB(r,s)             ((s<<16) | r)
+#define DECODE_REG(v)                   (v & 0xffff)
+#define DECODE_SUB(v)                   (((unsigned int) v) >> 16)
+
+#endif  // DALVIK_VM_DATAFLOW_H_
diff --git a/vm/compiler/Frontend.cpp b/vm/compiler/Frontend.cpp
new file mode 100644
index 0000000..47c1898
--- /dev/null
+++ b/vm/compiler/Frontend.cpp
@@ -0,0 +1,2177 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexCatch.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+static inline bool contentIsInsn(const u2 *codePtr) {
+    u2 instr = *codePtr;
+    Opcode opcode = (Opcode)(instr & 0xff);
+
+    /*
+     * Since the low 8-bit in metadata may look like OP_NOP, we need to check
+     * both the low and whole sub-word to determine whether it is code or data.
+     */
+    return (opcode != OP_NOP || instr == 0);
+}
+
+/*
+ * Parse an instruction, return the length of the instruction
+ */
+static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
+                            bool printMe)
+{
+    // Don't parse instruction data
+    if (!contentIsInsn(codePtr)) {
+        return 0;
+    }
+
+    u2 instr = *codePtr;
+    Opcode opcode = dexOpcodeFromCodeUnit(instr);
+
+    dexDecodeInstruction(codePtr, decInsn);
+    if (printMe) {
+        char *decodedString = dvmCompilerGetDalvikDisassembly(decInsn, NULL);
+        ALOGD("%p: %#06x %s", codePtr, opcode, decodedString);
+    }
+    return dexGetWidthFromOpcode(opcode);
+}
+
+#define UNKNOWN_TARGET 0xffffffff
+
+/*
+ * Identify block-ending instructions and collect supplemental information
+ * regarding the following instructions.
+ */
+static inline bool findBlockBoundary(const Method *caller, MIR *insn,
+                                     unsigned int curOffset,
+                                     unsigned int *target, bool *isInvoke,
+                                     const Method **callee)
+{
+    switch (insn->dalvikInsn.opcode) {
+        /* Target is not compile-time constant */
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+        case OP_THROW:
+          *target = UNKNOWN_TARGET;
+          break;
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE:
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+            *isInvoke = true;
+            break;
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            int mIndex = caller->clazz->pDvmDex->
+                pResMethods[insn->dalvikInsn.vB]->methodIndex;
+            const Method *calleeMethod =
+                caller->clazz->super->vtable[mIndex];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->super->vtable[insn->dalvikInsn.vB];
+
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            const Method *calleeMethod =
+                caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+            if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+                *target = (unsigned int) calleeMethod->insns;
+            }
+            *isInvoke = true;
+            *callee = calleeMethod;
+            break;
+        }
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            *target = curOffset + (int) insn->dalvikInsn.vA;
+            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:
+            *target = curOffset + (int) insn->dalvikInsn.vC;
+            break;
+
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            *target = curOffset + (int) insn->dalvikInsn.vB;
+            break;
+
+        default:
+            return false;
+    }
+    return true;
+}
+
+static inline bool isGoto(MIR *insn)
+{
+    switch (insn->dalvikInsn.opcode) {
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            return true;
+        default:
+            return false;
+    }
+}
+
+/*
+ * Identify unconditional branch instructions
+ */
+static inline bool isUnconditionalBranch(MIR *insn)
+{
+    switch (insn->dalvikInsn.opcode) {
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+            return true;
+        default:
+            return isGoto(insn);
+    }
+}
+
+/*
+ * dvmHashTableLookup() callback
+ */
+static int compareMethod(const CompilerMethodStats *m1,
+                         const CompilerMethodStats *m2)
+{
+    return (int) m1->method - (int) m2->method;
+}
+
+/*
+ * Analyze the body of the method to collect high-level information regarding
+ * inlining:
+ * - is empty method?
+ * - is getter/setter?
+ * - can throw exception?
+ *
+ * Currently the inliner only handles getters and setters. When its capability
+ * becomes more sophisticated more information will be retrieved here.
+ */
+static int analyzeInlineTarget(DecodedInstruction *dalvikInsn, int attributes,
+                               int offset)
+{
+    int flags = dexGetFlagsFromOpcode(dalvikInsn->opcode);
+    int dalvikOpcode = dalvikInsn->opcode;
+
+    if (flags & kInstrInvoke) {
+        attributes &= ~METHOD_IS_LEAF;
+    }
+
+    if (!(flags & kInstrCanReturn)) {
+        if (!(dvmCompilerDataFlowAttributes[dalvikOpcode] &
+              DF_IS_GETTER)) {
+            attributes &= ~METHOD_IS_GETTER;
+        }
+        if (!(dvmCompilerDataFlowAttributes[dalvikOpcode] &
+              DF_IS_SETTER)) {
+            attributes &= ~METHOD_IS_SETTER;
+        }
+    }
+
+    /*
+     * The expected instruction sequence is setter will never return value and
+     * getter will also do. Clear the bits if the behavior is discovered
+     * otherwise.
+     */
+    if (flags & kInstrCanReturn) {
+        if (dalvikOpcode == OP_RETURN_VOID) {
+            attributes &= ~METHOD_IS_GETTER;
+        }
+        else {
+            attributes &= ~METHOD_IS_SETTER;
+        }
+    }
+
+    if (flags & kInstrCanThrow) {
+        attributes &= ~METHOD_IS_THROW_FREE;
+    }
+
+    if (offset == 0 && dalvikOpcode == OP_RETURN_VOID) {
+        attributes |= METHOD_IS_EMPTY;
+    }
+
+    /*
+     * Check if this opcode is selected for single stepping.
+     * If so, don't inline the callee as there is no stack frame for the
+     * interpreter to single-step through the instruction.
+     */
+    if (SINGLE_STEP_OP(dalvikOpcode)) {
+        attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+    }
+
+    return attributes;
+}
+
+/*
+ * Analyze each method whose traces are ever compiled. Collect a variety of
+ * statistics like the ratio of exercised vs overall code and code bloat
+ * ratios. If isCallee is true, also analyze each instruction in more details
+ * to see if it is suitable for inlining.
+ */
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+                                                  bool isCallee)
+{
+    const DexCode *dexCode = dvmGetMethodCode(method);
+    const u2 *codePtr = dexCode->insns;
+    const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+    int insnSize = 0;
+    int hashValue = dvmComputeUtf8Hash(method->name);
+
+    CompilerMethodStats dummyMethodEntry; // For hash table lookup
+    CompilerMethodStats *realMethodEntry; // For hash table storage
+
+    /* For lookup only */
+    dummyMethodEntry.method = method;
+    realMethodEntry = (CompilerMethodStats *)
+        dvmHashTableLookup(gDvmJit.methodStatsTable,
+                           hashValue,
+                           &dummyMethodEntry,
+                           (HashCompareFunc) compareMethod,
+                           false);
+
+    /* This method has never been analyzed before - create an entry */
+    if (realMethodEntry == NULL) {
+        realMethodEntry =
+            (CompilerMethodStats *) calloc(1, sizeof(CompilerMethodStats));
+        realMethodEntry->method = method;
+
+        dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+                           realMethodEntry,
+                           (HashCompareFunc) compareMethod,
+                           true);
+    }
+
+    /* This method is invoked as a callee and has been analyzed - just return */
+    if ((isCallee == true) && (realMethodEntry->attributes & METHOD_IS_CALLEE))
+        return realMethodEntry;
+
+    /*
+     * Similarly, return if this method has been compiled before as a hot
+     * method already.
+     */
+    if ((isCallee == false) &&
+        (realMethodEntry->attributes & METHOD_IS_HOT))
+        return realMethodEntry;
+
+    int attributes;
+
+    /* Method hasn't been analyzed for the desired purpose yet */
+    if (isCallee) {
+        /* Aggressively set the attributes until proven otherwise */
+        attributes = METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE |
+                     METHOD_IS_GETTER | METHOD_IS_SETTER;
+    } else {
+        attributes = METHOD_IS_HOT;
+    }
+
+    /* Count the number of instructions */
+    while (codePtr < codeEnd) {
+        DecodedInstruction dalvikInsn;
+        int width = parseInsn(codePtr, &dalvikInsn, false);
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        if (isCallee) {
+            attributes = analyzeInlineTarget(&dalvikInsn, attributes, insnSize);
+        }
+
+        insnSize += width;
+        codePtr += width;
+    }
+
+    /*
+     * Only handle simple getters/setters with one instruction followed by
+     * return
+     */
+    if ((attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) &&
+        (insnSize != 3)) {
+        attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+    }
+
+    realMethodEntry->dalvikSize = insnSize * 2;
+    realMethodEntry->attributes |= attributes;
+
+#if 0
+    /* Uncomment the following to explore various callee patterns */
+    if (attributes & METHOD_IS_THROW_FREE) {
+        ALOGE("%s%s is inlinable%s", method->clazz->descriptor, method->name,
+             (attributes & METHOD_IS_EMPTY) ? " empty" : "");
+    }
+
+    if (attributes & METHOD_IS_LEAF) {
+        ALOGE("%s%s is leaf %d%s", method->clazz->descriptor, method->name,
+             insnSize, insnSize < 5 ? " (small)" : "");
+    }
+
+    if (attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) {
+        ALOGE("%s%s is %s", method->clazz->descriptor, method->name,
+             attributes & METHOD_IS_GETTER ? "getter": "setter");
+    }
+    if (attributes ==
+        (METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE)) {
+        ALOGE("%s%s is inlinable non setter/getter", method->clazz->descriptor,
+             method->name);
+    }
+#endif
+
+    return realMethodEntry;
+}
+
+/*
+ * Crawl the stack of the thread that requesed compilation to see if any of the
+ * ancestors are on the blacklist.
+ */
+static bool filterMethodByCallGraph(Thread *thread, const char *curMethodName)
+{
+    /* Crawl the Dalvik stack frames and compare the method name*/
+    StackSaveArea *ssaPtr = ((StackSaveArea *) thread->interpSave.curFrame) - 1;
+    while (ssaPtr != ((StackSaveArea *) NULL) - 1) {
+        const Method *method = ssaPtr->method;
+        if (method) {
+            int hashValue = dvmComputeUtf8Hash(method->name);
+            bool found =
+                dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               (char *) method->name,
+                               (HashCompareFunc) strcmp, false) !=
+                NULL;
+            if (found) {
+                ALOGD("Method %s (--> %s) found on the JIT %s list",
+                     method->name, curMethodName,
+                     gDvmJit.includeSelectedMethod ? "white" : "black");
+                return true;
+            }
+
+        }
+        ssaPtr = ((StackSaveArea *) ssaPtr->prevFrame) - 1;
+    };
+    return false;
+}
+
+/*
+ * Since we are including instructions from possibly a cold method into the
+ * current trace, we need to make sure that all the associated information
+ * with the callee is properly initialized. If not, we punt on this inline
+ * target.
+ *
+ * TODO: volatile instructions will be handled later.
+ */
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+                                          const DecodedInstruction *insn)
+{
+    switch (insn->opcode) {
+        case OP_NEW_INSTANCE:
+        case OP_CHECK_CAST: {
+            ClassObject *classPtr = (ClassObject *)(void*)
+              (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+            /* Class hasn't been initialized yet */
+            if (classPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_SGET:
+        case OP_SGET_WIDE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_WIDE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT: {
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[insn->vB]);
+
+            if (fieldPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            int mIndex = method->clazz->pDvmDex->
+                pResMethods[insn->vB]->methodIndex;
+            const Method *calleeMethod = method->clazz->super->vtable[mIndex];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            const Method *calleeMethod = method->clazz->super->vtable[insn->vB];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE:
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            const Method *calleeMethod =
+                method->clazz->pDvmDex->pResMethods[insn->vB];
+            if (calleeMethod == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+                (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+            if (classPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+                (method->clazz->pDvmDex->pResStrings[insn->vB]);
+
+            if (strPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        default:
+            return true;
+    }
+}
+
+/* Split an existing block from the specified code offset into two */
+static BasicBlock *splitBlock(CompilationUnit *cUnit,
+                              unsigned int codeOffset,
+                              BasicBlock *origBlock,
+                              BasicBlock **immedPredBlockP)
+{
+    MIR *insn = origBlock->firstMIRInsn;
+    while (insn) {
+        if (insn->offset == codeOffset) break;
+        insn = insn->next;
+    }
+    if (insn == NULL) {
+        ALOGE("Break split failed");
+        dvmAbort();
+    }
+    BasicBlock *bottomBlock = dvmCompilerNewBB(kDalvikByteCode,
+                                               cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bottomBlock);
+
+    bottomBlock->startOffset = codeOffset;
+    bottomBlock->firstMIRInsn = insn;
+    bottomBlock->lastMIRInsn = origBlock->lastMIRInsn;
+
+    /* Handle the taken path */
+    bottomBlock->taken = origBlock->taken;
+    if (bottomBlock->taken) {
+        origBlock->taken = NULL;
+        dvmCompilerClearBit(bottomBlock->taken->predecessors, origBlock->id);
+        dvmCompilerSetBit(bottomBlock->taken->predecessors, bottomBlock->id);
+    }
+
+    /* Handle the fallthrough path */
+    bottomBlock->needFallThroughBranch = origBlock->needFallThroughBranch;
+    bottomBlock->fallThrough = origBlock->fallThrough;
+    origBlock->fallThrough = bottomBlock;
+    origBlock->needFallThroughBranch = true;
+    dvmCompilerSetBit(bottomBlock->predecessors, origBlock->id);
+    if (bottomBlock->fallThrough) {
+        dvmCompilerClearBit(bottomBlock->fallThrough->predecessors,
+                            origBlock->id);
+        dvmCompilerSetBit(bottomBlock->fallThrough->predecessors,
+                          bottomBlock->id);
+    }
+
+    /* Handle the successor list */
+    if (origBlock->successorBlockList.blockListType != kNotUsed) {
+        bottomBlock->successorBlockList = origBlock->successorBlockList;
+        origBlock->successorBlockList.blockListType = kNotUsed;
+        GrowableListIterator iterator;
+
+        dvmGrowableListIteratorInit(&bottomBlock->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *bb = successorBlockInfo->block;
+            dvmCompilerClearBit(bb->predecessors, origBlock->id);
+            dvmCompilerSetBit(bb->predecessors, bottomBlock->id);
+        }
+    }
+
+    origBlock->lastMIRInsn = insn->prev;
+
+    insn->prev->next = NULL;
+    insn->prev = NULL;
+
+    /*
+     * Update the immediate predecessor block pointer so that outgoing edges
+     * can be applied to the proper block.
+     */
+    if (immedPredBlockP) {
+        assert(*immedPredBlockP == origBlock);
+        *immedPredBlockP = bottomBlock;
+    }
+    return bottomBlock;
+}
+
+/*
+ * Given a code offset, find out the block that starts with it. If the offset
+ * is in the middle of an existing block, split it into two. If immedPredBlockP
+ * is non-null and is the block being split, update *immedPredBlockP to point
+ * to the bottom block so that outgoing edges can be setup properly (by the
+ * caller).
+ */
+static BasicBlock *findBlock(CompilationUnit *cUnit,
+                             unsigned int codeOffset,
+                             bool split, bool create,
+                             BasicBlock **immedPredBlockP)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    BasicBlock *bb;
+    unsigned int i;
+
+    for (i = 0; i < blockList->numUsed; i++) {
+        bb = (BasicBlock *) blockList->elemList[i];
+        if (bb->blockType != kDalvikByteCode) continue;
+        if (bb->startOffset == codeOffset) return bb;
+        /* Check if a branch jumps into the middle of an existing block */
+        if ((split == true) && (codeOffset > bb->startOffset) &&
+            (bb->lastMIRInsn != NULL) &&
+            (codeOffset <= bb->lastMIRInsn->offset)) {
+            BasicBlock *newBB = splitBlock(cUnit, codeOffset, bb,
+                                           bb == *immedPredBlockP ?
+                                               immedPredBlockP : NULL);
+            return newBB;
+        }
+    }
+    if (create) {
+          bb = dvmCompilerNewBB(kDalvikByteCode, cUnit->numBlocks++);
+          dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+          bb->startOffset = codeOffset;
+          return bb;
+    }
+    return NULL;
+}
+
+/* Dump the CFG into a DOT graph */
+void dvmDumpCFG(CompilationUnit *cUnit, const char *dirPrefix)
+{
+    const Method *method = cUnit->method;
+    FILE *file;
+    char *signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    char startOffset[80];
+    sprintf(startOffset, "_%x", cUnit->entryBlock->fallThrough->startOffset);
+    char *fileName = (char *) dvmCompilerNew(
+                                  strlen(dirPrefix) +
+                                  strlen(method->clazz->descriptor) +
+                                  strlen(method->name) +
+                                  strlen(signature) +
+                                  strlen(startOffset) +
+                                  strlen(".dot") + 1, true);
+    sprintf(fileName, "%s%s%s%s%s.dot", dirPrefix,
+            method->clazz->descriptor, method->name, signature, startOffset);
+    free(signature);
+
+    /*
+     * Convert the special characters into a filesystem- and shell-friendly
+     * format.
+     */
+    int i;
+    for (i = strlen(dirPrefix); fileName[i]; i++) {
+        if (fileName[i] == '/') {
+            fileName[i] = '_';
+        } else if (fileName[i] == ';') {
+            fileName[i] = '#';
+        } else if (fileName[i] == '$') {
+            fileName[i] = '+';
+        } else if (fileName[i] == '(' || fileName[i] == ')') {
+            fileName[i] = '@';
+        } else if (fileName[i] == '<' || fileName[i] == '>') {
+            fileName[i] = '=';
+        }
+    }
+    file = fopen(fileName, "w");
+    if (file == NULL) {
+        return;
+    }
+    fprintf(file, "digraph G {\n");
+
+    fprintf(file, "  rankdir=TB\n");
+
+    int numReachableBlocks = cUnit->numReachableBlocks;
+    int idx;
+    const GrowableList *blockList = &cUnit->blockList;
+
+    for (idx = 0; idx < numReachableBlocks; idx++) {
+        int blockIdx = cUnit->dfsOrder.elemList[idx];
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                                  blockIdx);
+        if (bb == NULL) break;
+        if (bb->blockType == kEntryBlock) {
+            fprintf(file, "  entry [shape=Mdiamond];\n");
+        } else if (bb->blockType == kExitBlock) {
+            fprintf(file, "  exit [shape=Mdiamond];\n");
+        } else if (bb->blockType == kDalvikByteCode) {
+            fprintf(file, "  block%04x [shape=record,label = \"{ \\\n",
+                    bb->startOffset);
+            const MIR *mir;
+            fprintf(file, "    {block id %d\\l}%s\\\n", bb->id,
+                    bb->firstMIRInsn ? " | " : " ");
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+                fprintf(file, "    {%04x %s\\l}%s\\\n", mir->offset,
+                        mir->ssaRep ?
+                            dvmCompilerFullDisassembler(cUnit, mir) :
+                            dexGetOpcodeName(mir->dalvikInsn.opcode),
+                        mir->next ? " | " : " ");
+            }
+            fprintf(file, "  }\"];\n\n");
+        } else if (bb->blockType == kExceptionHandling) {
+            char blockName[BLOCK_NAME_LEN];
+
+            dvmGetBlockName(bb, blockName);
+            fprintf(file, "  %s [shape=invhouse];\n", blockName);
+        }
+
+        char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+
+        if (bb->taken) {
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(bb->taken, blockName2);
+            fprintf(file, "  %s:s -> %s:n [style=dotted]\n",
+                    blockName1, blockName2);
+        }
+        if (bb->fallThrough) {
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(bb->fallThrough, blockName2);
+            fprintf(file, "  %s:s -> %s:n\n", blockName1, blockName2);
+        }
+
+        if (bb->successorBlockList.blockListType != kNotUsed) {
+            fprintf(file, "  succ%04x [shape=%s,label = \"{ \\\n",
+                    bb->startOffset,
+                    (bb->successorBlockList.blockListType == kCatch) ?
+                        "Mrecord" : "record");
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                        &iterator);
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+            int succId = 0;
+            while (true) {
+                if (successorBlockInfo == NULL) break;
+
+                BasicBlock *destBlock = successorBlockInfo->block;
+                SuccessorBlockInfo *nextSuccessorBlockInfo =
+                  (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+                fprintf(file, "    {<f%d> %04x: %04x\\l}%s\\\n",
+                        succId++,
+                        successorBlockInfo->key,
+                        destBlock->startOffset,
+                        (nextSuccessorBlockInfo != NULL) ? " | " : " ");
+
+                successorBlockInfo = nextSuccessorBlockInfo;
+            }
+            fprintf(file, "  }\"];\n\n");
+
+            dvmGetBlockName(bb, blockName1);
+            fprintf(file, "  %s:s -> succ%04x:n [style=dashed]\n",
+                    blockName1, bb->startOffset);
+
+            if (bb->successorBlockList.blockListType == kPackedSwitch ||
+                bb->successorBlockList.blockListType == kSparseSwitch) {
+
+                dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                            &iterator);
+
+                succId = 0;
+                while (true) {
+                    SuccessorBlockInfo *successorBlockInfo =
+                        (SuccessorBlockInfo *)
+                            dvmGrowableListIteratorNext(&iterator);
+                    if (successorBlockInfo == NULL) break;
+
+                    BasicBlock *destBlock = successorBlockInfo->block;
+
+                    dvmGetBlockName(destBlock, blockName2);
+                    fprintf(file, "  succ%04x:f%d:e -> %s:n\n",
+                            bb->startOffset, succId++,
+                            blockName2);
+                }
+            }
+        }
+        fprintf(file, "\n");
+
+        /*
+         * If we need to debug the dominator tree, uncomment the following code
+         */
+#if 1
+        dvmGetBlockName(bb, blockName1);
+        fprintf(file, "  cfg%s [label=\"%s\", shape=none];\n",
+                blockName1, blockName1);
+        if (bb->iDom) {
+            dvmGetBlockName(bb->iDom, blockName2);
+            fprintf(file, "  cfg%s:s -> cfg%s:n\n\n",
+                    blockName2, blockName1);
+        }
+#endif
+    }
+    fprintf(file, "}\n");
+    fclose(file);
+}
+
+/* Verify if all the successor is connected with all the claimed predecessors */
+static bool verifyPredInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVectorIterator bvIterator;
+
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+    while (true) {
+        int blockIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (blockIdx == -1) break;
+        BasicBlock *predBB = (BasicBlock *)
+            dvmGrowableListGetElement(&cUnit->blockList, blockIdx);
+        bool found = false;
+        if (predBB->taken == bb) {
+            found = true;
+        } else if (predBB->fallThrough == bb) {
+            found = true;
+        } else if (predBB->successorBlockList.blockListType != kNotUsed) {
+            GrowableListIterator iterator;
+            dvmGrowableListIteratorInit(&predBB->successorBlockList.blocks,
+                                        &iterator);
+            while (true) {
+                SuccessorBlockInfo *successorBlockInfo =
+                    (SuccessorBlockInfo *)
+                        dvmGrowableListIteratorNext(&iterator);
+                if (successorBlockInfo == NULL) break;
+                BasicBlock *succBB = successorBlockInfo->block;
+                if (succBB == bb) {
+                    found = true;
+                    break;
+                }
+            }
+        }
+        if (found == false) {
+            char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+            dvmGetBlockName(bb, blockName1);
+            dvmGetBlockName(predBB, blockName2);
+            dvmDumpCFG(cUnit, "/sdcard/cfg/");
+            ALOGE("Successor %s not found from %s",
+                 blockName1, blockName2);
+            dvmAbort();
+        }
+    }
+    return true;
+}
+
+/* Identify code range in try blocks and set up the empty catch blocks */
+static void processTryCatchBlocks(CompilationUnit *cUnit)
+{
+    const Method *meth = cUnit->method;
+    const DexCode *pCode = dvmGetMethodCode(meth);
+    int triesSize = pCode->triesSize;
+    int i;
+    int offset;
+
+    if (triesSize == 0) {
+        return;
+    }
+
+    const DexTry *pTries = dexGetTries(pCode);
+    BitVector *tryBlockAddr = cUnit->tryBlockAddr;
+
+    /* Mark all the insn offsets in Try blocks */
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        /* all in 16-bit units */
+        int startOffset = pTry->startAddr;
+        int endOffset = startOffset + pTry->insnCount;
+
+        for (offset = startOffset; offset < endOffset; offset++) {
+            dvmCompilerSetBit(tryBlockAddr, offset);
+        }
+    }
+
+    /* Iterate over each of the handlers to enqueue the empty Catch blocks */
+    offset = dexGetFirstHandlerOffset(pCode);
+    int handlersSize = dexGetHandlersSize(pCode);
+
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            /*
+             * Create dummy catch blocks first. Since these are created before
+             * other blocks are processed, "split" is specified as false.
+             */
+            findBlock(cUnit, handler->address,
+                      /* split */
+                      false,
+                      /* create */
+                      true,
+                      /* immedPredBlockP */
+                      NULL);
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+}
+
+/* Process instructions with the kInstrCanBranch flag */
+static void processCanBranch(CompilationUnit *cUnit, BasicBlock *curBlock,
+                             MIR *insn, int curOffset, int width, int flags,
+                             const u2* codePtr, const u2* codeEnd)
+{
+    int target = curOffset;
+    switch (insn->dalvikInsn.opcode) {
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            target += (int) insn->dalvikInsn.vA;
+            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:
+            target += (int) insn->dalvikInsn.vC;
+            break;
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            target += (int) insn->dalvikInsn.vB;
+            break;
+        default:
+            ALOGE("Unexpected opcode(%d) with kInstrCanBranch set",
+                 insn->dalvikInsn.opcode);
+            dvmAbort();
+    }
+    BasicBlock *takenBlock = findBlock(cUnit, target,
+                                       /* split */
+                                       true,
+                                       /* create */
+                                       true,
+                                       /* immedPredBlockP */
+                                       &curBlock);
+    curBlock->taken = takenBlock;
+    dvmCompilerSetBit(takenBlock->predecessors, curBlock->id);
+
+    /* Always terminate the current block for conditional branches */
+    if (flags & kInstrCanContinue) {
+        BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                                 curOffset +  width,
+                                                 /*
+                                                  * If the method is processed
+                                                  * in sequential order from the
+                                                  * beginning, we don't need to
+                                                  * specify split for continue
+                                                  * blocks. However, this
+                                                  * routine can be called by
+                                                  * compileLoop, which starts
+                                                  * parsing the method from an
+                                                  * arbitrary address in the
+                                                  * method body.
+                                                  */
+                                                 true,
+                                                 /* create */
+                                                 true,
+                                                 /* immedPredBlockP */
+                                                 &curBlock);
+        curBlock->fallThrough = fallthroughBlock;
+        dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+    } else if (codePtr < codeEnd) {
+        /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+        if (contentIsInsn(codePtr)) {
+            findBlock(cUnit, curOffset + width,
+                      /* split */
+                      false,
+                      /* create */
+                      true,
+                      /* immedPredBlockP */
+                      NULL);
+        }
+    }
+}
+
+/* Process instructions with the kInstrCanSwitch flag */
+static void processCanSwitch(CompilationUnit *cUnit, BasicBlock *curBlock,
+                             MIR *insn, int curOffset, int width, int flags)
+{
+    u2 *switchData= (u2 *) (cUnit->method->insns + curOffset +
+                            insn->dalvikInsn.vB);
+    int size;
+    int *keyTable;
+    int *targetTable;
+    int i;
+    int firstKey;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    if (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) {
+        assert(switchData[0] == kPackedSwitchSignature);
+        size = switchData[1];
+        firstKey = switchData[2] | (switchData[3] << 16);
+        targetTable = (int *) &switchData[4];
+        keyTable = NULL;        // Make the compiler happy
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+    } else {
+        assert(switchData[0] == kSparseSwitchSignature);
+        size = switchData[1];
+        keyTable = (int *) &switchData[2];
+        targetTable = (int *) &switchData[2 + size*2];
+        firstKey = 0;   // To make the compiler happy
+    }
+
+    if (curBlock->successorBlockList.blockListType != kNotUsed) {
+        ALOGE("Successor block list already in use: %d",
+             curBlock->successorBlockList.blockListType);
+        dvmAbort();
+    }
+    curBlock->successorBlockList.blockListType =
+        (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) ?
+        kPackedSwitch : kSparseSwitch;
+    dvmInitGrowableList(&curBlock->successorBlockList.blocks, size);
+
+    for (i = 0; i < size; i++) {
+        BasicBlock *caseBlock = findBlock(cUnit, curOffset + targetTable[i],
+                                          /* split */
+                                          true,
+                                          /* create */
+                                          true,
+                                          /* immedPredBlockP */
+                                          &curBlock);
+        SuccessorBlockInfo *successorBlockInfo =
+            (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+                                                  false);
+        successorBlockInfo->block = caseBlock;
+        successorBlockInfo->key = (insn->dalvikInsn.opcode == OP_PACKED_SWITCH)?
+                                  firstKey + i : keyTable[i];
+        dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+                              (intptr_t) successorBlockInfo);
+        dvmCompilerSetBit(caseBlock->predecessors, curBlock->id);
+    }
+
+    /* Fall-through case */
+    BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                             curOffset +  width,
+                                             /* split */
+                                             false,
+                                             /* create */
+                                             true,
+                                             /* immedPredBlockP */
+                                             NULL);
+    curBlock->fallThrough = fallthroughBlock;
+    dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+}
+
+/* Process instructions with the kInstrCanThrow flag */
+static void processCanThrow(CompilationUnit *cUnit, BasicBlock *curBlock,
+                            MIR *insn, int curOffset, int width, int flags,
+                            BitVector *tryBlockAddr, const u2 *codePtr,
+                            const u2* codeEnd)
+{
+    const Method *method = cUnit->method;
+    const DexCode *dexCode = dvmGetMethodCode(method);
+
+    /* In try block */
+    if (dvmIsBitSet(tryBlockAddr, curOffset)) {
+        DexCatchIterator iterator;
+
+        if (!dexFindCatchHandler(&iterator, dexCode, curOffset)) {
+            ALOGE("Catch block not found in dexfile for insn %x in %s",
+                 curOffset, method->name);
+            dvmAbort();
+
+        }
+        if (curBlock->successorBlockList.blockListType != kNotUsed) {
+            ALOGE("Successor block list already in use: %d",
+                 curBlock->successorBlockList.blockListType);
+            dvmAbort();
+        }
+        curBlock->successorBlockList.blockListType = kCatch;
+        dvmInitGrowableList(&curBlock->successorBlockList.blocks, 2);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+            if (handler == NULL) {
+                break;
+            }
+
+            BasicBlock *catchBlock = findBlock(cUnit, handler->address,
+                                               /* split */
+                                               false,
+                                               /* create */
+                                               false,
+                                               /* immedPredBlockP */
+                                               NULL);
+
+            SuccessorBlockInfo *successorBlockInfo =
+              (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+                                                    false);
+            successorBlockInfo->block = catchBlock;
+            successorBlockInfo->key = handler->typeIdx;
+            dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+                                  (intptr_t) successorBlockInfo);
+            dvmCompilerSetBit(catchBlock->predecessors, curBlock->id);
+        }
+    } else {
+        BasicBlock *ehBlock = dvmCompilerNewBB(kExceptionHandling,
+                                               cUnit->numBlocks++);
+        curBlock->taken = ehBlock;
+        dvmInsertGrowableList(&cUnit->blockList, (intptr_t) ehBlock);
+        ehBlock->startOffset = curOffset;
+        dvmCompilerSetBit(ehBlock->predecessors, curBlock->id);
+    }
+
+    /*
+     * Force the current block to terminate.
+     *
+     * Data may be present before codeEnd, so we need to parse it to know
+     * whether it is code or data.
+     */
+    if (codePtr < codeEnd) {
+        /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+        if (contentIsInsn(codePtr)) {
+            BasicBlock *fallthroughBlock = findBlock(cUnit,
+                                                     curOffset + width,
+                                                     /* split */
+                                                     false,
+                                                     /* create */
+                                                     true,
+                                                     /* immedPredBlockP */
+                                                     NULL);
+            /*
+             * OP_THROW and OP_THROW_VERIFICATION_ERROR are unconditional
+             * branches.
+             */
+            if (insn->dalvikInsn.opcode != OP_THROW_VERIFICATION_ERROR &&
+                insn->dalvikInsn.opcode != OP_THROW) {
+                curBlock->fallThrough = fallthroughBlock;
+                dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+            }
+        }
+    }
+}
+
+/*
+ * Similar to dvmCompileTrace, but the entity processed here is the whole
+ * method.
+ *
+ * TODO: implementation will be revisited when the trace builder can provide
+ * whole-method traces.
+ */
+bool dvmCompileMethod(const Method *method, JitTranslationInfo *info)
+{
+    CompilationUnit cUnit;
+    const DexCode *dexCode = dvmGetMethodCode(method);
+    const u2 *codePtr = dexCode->insns;
+    const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+    int numBlocks = 0;
+    unsigned int curOffset = 0;
+
+    /* Method already compiled */
+    if (dvmJitGetMethodAddr(codePtr)) {
+        info->codeAddress = NULL;
+        return false;
+    }
+
+    memset(&cUnit, 0, sizeof(cUnit));
+    cUnit.method = method;
+
+    cUnit.jitMode = kJitMethod;
+
+    /* Initialize the block list */
+    dvmInitGrowableList(&cUnit.blockList, 4);
+
+    /*
+     * FIXME - PC reconstruction list won't be needed after the codegen routines
+     * are enhanced to true method mode.
+     */
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+    /* Allocate the bit-vector to track the beginning of basic blocks */
+    BitVector *tryBlockAddr = dvmCompilerAllocBitVector(dexCode->insnsSize,
+                                                        true /* expandable */);
+    cUnit.tryBlockAddr = tryBlockAddr;
+
+    /* Create the default entry and exit blocks and enter them to the list */
+    BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
+
+    cUnit.entryBlock = entryBlock;
+    cUnit.exitBlock = exitBlock;
+
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) entryBlock);
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) exitBlock);
+
+    /* Current block to record parsed instructions */
+    BasicBlock *curBlock = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    curBlock->startOffset = 0;
+    dvmInsertGrowableList(&cUnit.blockList, (intptr_t) curBlock);
+    entryBlock->fallThrough = curBlock;
+    dvmCompilerSetBit(curBlock->predecessors, entryBlock->id);
+
+    /*
+     * Store back the number of blocks since new blocks may be created of
+     * accessing cUnit.
+     */
+    cUnit.numBlocks = numBlocks;
+
+    /* Identify code range in try blocks and set up the empty catch blocks */
+    processTryCatchBlocks(&cUnit);
+
+    /* Parse all instructions and put them into containing basic blocks */
+    while (codePtr < codeEnd) {
+        MIR *insn = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+        insn->width = width;
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        dvmCompilerAppendMIR(curBlock, insn);
+
+        codePtr += width;
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        if (flags & kInstrCanBranch) {
+            processCanBranch(&cUnit, curBlock, insn, curOffset, width, flags,
+                             codePtr, codeEnd);
+        } else if (flags & kInstrCanReturn) {
+            curBlock->fallThrough = exitBlock;
+            dvmCompilerSetBit(exitBlock->predecessors, curBlock->id);
+            /*
+             * Terminate the current block if there are instructions
+             * afterwards.
+             */
+            if (codePtr < codeEnd) {
+                /*
+                 * Create a fallthrough block for real instructions
+                 * (incl. OP_NOP).
+                 */
+                if (contentIsInsn(codePtr)) {
+                    findBlock(&cUnit, curOffset + width,
+                              /* split */
+                              false,
+                              /* create */
+                              true,
+                              /* immedPredBlockP */
+                              NULL);
+                }
+            }
+        } else if (flags & kInstrCanThrow) {
+            processCanThrow(&cUnit, curBlock, insn, curOffset, width, flags,
+                            tryBlockAddr, codePtr, codeEnd);
+        } else if (flags & kInstrCanSwitch) {
+            processCanSwitch(&cUnit, curBlock, insn, curOffset, width, flags);
+        }
+        curOffset += width;
+        BasicBlock *nextBlock = findBlock(&cUnit, curOffset,
+                                          /* split */
+                                          false,
+                                          /* create */
+                                          false,
+                                          /* immedPredBlockP */
+                                          NULL);
+        if (nextBlock) {
+            /*
+             * The next instruction could be the target of a previously parsed
+             * forward branch so a block is already created. If the current
+             * instruction is not an unconditional branch, connect them through
+             * the fall-through link.
+             */
+            assert(curBlock->fallThrough == NULL ||
+                   curBlock->fallThrough == nextBlock ||
+                   curBlock->fallThrough == exitBlock);
+
+            if ((curBlock->fallThrough == NULL) &&
+                (flags & kInstrCanContinue)) {
+                curBlock->fallThrough = nextBlock;
+                dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+            }
+            curBlock = nextBlock;
+        }
+    }
+
+    if (cUnit.printMe) {
+        dvmCompilerDumpCompilationUnit(&cUnit);
+    }
+
+    /* Adjust this value accordingly once inlining is performed */
+    cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
+    /* Verify if all blocks are connected as claimed */
+    /* FIXME - to be disabled in the future */
+    dvmCompilerDataFlowAnalysisDispatcher(&cUnit, verifyPredInfo,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+
+    /* Perform SSA transformation for the whole method */
+    dvmCompilerMethodSSATransformation(&cUnit);
+
+#ifndef ARCH_IA32
+    dvmCompilerInitializeRegAlloc(&cUnit);  // Needs to happen after SSA naming
+
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(&cUnit);
+#endif
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMethodMIR2LIR(&cUnit);
+
+    // Debugging only
+    //dvmDumpCFG(&cUnit, "/sdcard/cfg/");
+
+    /* Method is not empty */
+    if (cUnit.firstLIRInsn) {
+        /* Convert LIR into machine code. Loop for recoverable retries */
+        do {
+            dvmCompilerAssembleLIR(&cUnit, info);
+            cUnit.assemblerRetries++;
+            if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+                ALOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+                      cUnit.assemblerStatus);
+        } while (cUnit.assemblerStatus == kRetryAll);
+
+        if (cUnit.printMe) {
+            dvmCompilerCodegenDump(&cUnit);
+        }
+
+        if (info->codeAddress) {
+            dvmJitSetCodeAddr(dexCode->insns, info->codeAddress,
+                              info->instructionSet, true, 0);
+            /*
+             * Clear the codeAddress for the enclosing trace to reuse the info
+             */
+            info->codeAddress = NULL;
+        }
+    }
+
+    return false;
+}
+
+/* Extending the trace by crawling the code from curBlock */
+static bool exhaustTrace(CompilationUnit *cUnit, BasicBlock *curBlock)
+{
+    unsigned int curOffset = curBlock->startOffset;
+    const u2 *codePtr = cUnit->method->insns + curOffset;
+
+    if (curBlock->visited == true) return false;
+
+    curBlock->visited = true;
+
+    if (curBlock->blockType == kEntryBlock ||
+        curBlock->blockType == kExitBlock) {
+        return false;
+    }
+
+    /*
+     * Block has been parsed - check the taken/fallThrough in case it is a split
+     * block.
+     */
+    if (curBlock->firstMIRInsn != NULL) {
+          bool changed = false;
+          if (curBlock->taken)
+              changed |= exhaustTrace(cUnit, curBlock->taken);
+          if (curBlock->fallThrough)
+              changed |= exhaustTrace(cUnit, curBlock->fallThrough);
+          return changed;
+    }
+    while (true) {
+        MIR *insn = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+        insn->width = width;
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        dvmCompilerAppendMIR(curBlock, insn);
+
+        codePtr += width;
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        /* Stop extending the trace after seeing these instructions */
+        if (flags & (kInstrCanReturn | kInstrCanSwitch | kInstrInvoke)) {
+            curBlock->fallThrough = cUnit->exitBlock;
+            dvmCompilerSetBit(cUnit->exitBlock->predecessors, curBlock->id);
+            break;
+        } else if (flags & kInstrCanBranch) {
+            processCanBranch(cUnit, curBlock, insn, curOffset, width, flags,
+                             codePtr, NULL);
+            if (curBlock->taken) {
+                exhaustTrace(cUnit, curBlock->taken);
+            }
+            if (curBlock->fallThrough) {
+                exhaustTrace(cUnit, curBlock->fallThrough);
+            }
+            break;
+        }
+        curOffset += width;
+        BasicBlock *nextBlock = findBlock(cUnit, curOffset,
+                                          /* split */
+                                          false,
+                                          /* create */
+                                          false,
+                                          /* immedPredBlockP */
+                                          NULL);
+        if (nextBlock) {
+            /*
+             * The next instruction could be the target of a previously parsed
+             * forward branch so a block is already created. If the current
+             * instruction is not an unconditional branch, connect them through
+             * the fall-through link.
+             */
+            assert(curBlock->fallThrough == NULL ||
+                   curBlock->fallThrough == nextBlock ||
+                   curBlock->fallThrough == cUnit->exitBlock);
+
+            if ((curBlock->fallThrough == NULL) &&
+                (flags & kInstrCanContinue)) {
+                curBlock->needFallThroughBranch = true;
+                curBlock->fallThrough = nextBlock;
+                dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+            }
+            /* Block has been visited - no more parsing needed */
+            if (nextBlock->visited == true) {
+                return true;
+            }
+            curBlock = nextBlock;
+        }
+    }
+    return true;
+}
+
+/* Compile a loop */
+static bool compileLoop(CompilationUnit *cUnit, unsigned int startOffset,
+                        JitTraceDescription *desc, int numMaxInsts,
+                        JitTranslationInfo *info, jmp_buf *bailPtr,
+                        int optHints)
+{
+    int numBlocks = 0;
+    unsigned int curOffset = startOffset;
+    bool changed;
+    BasicBlock *bb;
+#if defined(WITH_JIT_TUNING)
+    CompilerMethodStats *methodStats;
+#endif
+
+    cUnit->jitMode = kJitLoop;
+
+    /* Initialize the block list */
+    dvmInitGrowableList(&cUnit->blockList, 4);
+
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit->pcReconstructionList, 8);
+
+    /* Create the default entry and exit blocks and enter them to the list */
+    BasicBlock *entryBlock = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    entryBlock->startOffset = curOffset;
+    BasicBlock *exitBlock = dvmCompilerNewBB(kExitBlock, numBlocks++);
+
+    cUnit->entryBlock = entryBlock;
+    cUnit->exitBlock = exitBlock;
+
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) entryBlock);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) exitBlock);
+
+    /* Current block to record parsed instructions */
+    BasicBlock *curBlock = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    curBlock->startOffset = curOffset;
+
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) curBlock);
+    entryBlock->fallThrough = curBlock;
+    dvmCompilerSetBit(curBlock->predecessors, entryBlock->id);
+
+    /*
+     * Store back the number of blocks since new blocks may be created of
+     * accessing cUnit.
+     */
+    cUnit->numBlocks = numBlocks;
+
+    do {
+        dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                              dvmCompilerClearVisitedFlag,
+                                              kAllNodes,
+                                              false /* isIterative */);
+        changed = exhaustTrace(cUnit, curBlock);
+    } while (changed);
+
+    /* Backward chaining block */
+    bb = dvmCompilerNewBB(kChainingCellBackwardBranch, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+    cUnit->backChainBlock = bb;
+
+    /* A special block to host PC reconstruction code */
+    bb = dvmCompilerNewBB(kPCReconstruction, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+
+    /* And one final block that publishes the PC and raises the exception */
+    bb = dvmCompilerNewBB(kExceptionHandling, cUnit->numBlocks++);
+    dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+    cUnit->puntBlock = bb;
+
+    cUnit->numDalvikRegisters = cUnit->method->registersSize;
+
+    /* Verify if all blocks are connected as claimed */
+    /* FIXME - to be disabled in the future */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, verifyPredInfo,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+
+    /* Try to identify a loop */
+    if (!dvmCompilerBuildLoop(cUnit))
+        goto bail;
+
+    dvmCompilerLoopOpt(cUnit);
+
+    /*
+     * Change the backward branch to the backward chaining cell after dataflow
+     * analsys/optimizations are done.
+     */
+    dvmCompilerInsertBackwardChaining(cUnit);
+
+#if defined(ARCH_IA32)
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(cUnit, info);
+#else
+    dvmCompilerInitializeRegAlloc(cUnit);
+
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(cUnit);
+#endif
+
+    /* Loop contains never executed blocks / heavy instructions */
+    if (cUnit->quitLoopMode) {
+        if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+            ALOGD("Loop trace @ offset %04x aborted due to unresolved code info",
+                 cUnit->entryBlock->startOffset);
+        }
+        goto bail;
+    }
+
+    /* Convert LIR into machine code. Loop for recoverable retries */
+    do {
+        dvmCompilerAssembleLIR(cUnit, info);
+        cUnit->assemblerRetries++;
+        if (cUnit->printMe && cUnit->assemblerStatus != kSuccess)
+            ALOGD("Assembler abort #%d on %d", cUnit->assemblerRetries,
+                  cUnit->assemblerStatus);
+    } while (cUnit->assemblerStatus == kRetryAll);
+
+    /* Loop is too big - bail out */
+    if (cUnit->assemblerStatus == kRetryHalve) {
+        goto bail;
+    }
+
+    if (cUnit->printMe || gDvmJit.receivedSIGUSR2) {
+        ALOGD("Loop trace @ offset %04x", cUnit->entryBlock->startOffset);
+        dvmCompilerCodegenDump(cUnit);
+    }
+
+    /*
+     * If this trace uses class objects as constants,
+     * dvmJitInstallClassObjectPointers will switch the thread state
+     * to running and look up the class pointers using the descriptor/loader
+     * tuple stored in the callsite info structure. We need to make this window
+     * as short as possible since it is blocking GC.
+     */
+    if (cUnit->hasClassLiterals && info->codeAddress) {
+        dvmJitInstallClassObjectPointers(cUnit, (char *) info->codeAddress);
+    }
+
+    /*
+     * Since callsiteinfo is allocated from the arena, delay the reset until
+     * class pointers are resolved.
+     */
+    dvmCompilerArenaReset();
+
+    assert(cUnit->assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+    /* Locate the entry to store compilation statistics for this method */
+    methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+    methodStats->nativeSize += cUnit->totalSize;
+#endif
+    return info->codeAddress != NULL;
+
+bail:
+    /* Retry the original trace with JIT_OPT_NO_LOOP disabled */
+    dvmCompilerArenaReset();
+    return dvmCompileTrace(desc, numMaxInsts, info, bailPtr,
+                           optHints | JIT_OPT_NO_LOOP);
+}
+
+static bool searchClassTablePrefix(const Method* method) {
+    if (gDvmJit.classTable == NULL) {
+        return false;
+    }
+    HashIter iter;
+    HashTable* pTab = gDvmJit.classTable;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        const char* str = (const char*) dvmHashIterData(&iter);
+        if (strncmp(method->clazz->descriptor, str, strlen(str)) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Main entry point to start trace compilation. Basic blocks are constructed
+ * first and they will be passed to the codegen routines to convert Dalvik
+ * bytecode into machine code.
+ */
+bool dvmCompileTrace(JitTraceDescription *desc, int numMaxInsts,
+                     JitTranslationInfo *info, jmp_buf *bailPtr,
+                     int optHints)
+{
+    const DexCode *dexCode = dvmGetMethodCode(desc->method);
+    const JitTraceRun* currRun = &desc->trace[0];
+    unsigned int curOffset = currRun->info.frag.startOffset;
+    unsigned int startOffset = curOffset;
+    unsigned int numInsts = currRun->info.frag.numInsts;
+    const u2 *codePtr = dexCode->insns + curOffset;
+    int traceSize = 0;  // # of half-words
+    const u2 *startCodePtr = codePtr;
+    BasicBlock *curBB, *entryCodeBB;
+    int numBlocks = 0;
+    static int compilationId;
+    CompilationUnit cUnit;
+    GrowableList *blockList;
+#if defined(WITH_JIT_TUNING)
+    CompilerMethodStats *methodStats;
+#endif
+
+    /* If we've already compiled this trace, just return success */
+    if (dvmJitGetTraceAddr(startCodePtr) && !info->discardResult) {
+        /*
+         * Make sure the codeAddress is NULL so that it won't clobber the
+         * existing entry.
+         */
+        info->codeAddress = NULL;
+        return true;
+    }
+
+    /* If the work order is stale, discard it */
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        return false;
+    }
+
+    compilationId++;
+    memset(&cUnit, 0, sizeof(CompilationUnit));
+
+#if defined(WITH_JIT_TUNING)
+    /* Locate the entry to store compilation statistics for this method */
+    methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+#endif
+
+    /* Set the recover buffer pointer */
+    cUnit.bailPtr = bailPtr;
+
+    /* Initialize the printMe flag */
+    cUnit.printMe = gDvmJit.printMe;
+
+    /* Setup the method */
+    cUnit.method = desc->method;
+
+    /* Store the trace descriptor and set the initial mode */
+    cUnit.traceDesc = desc;
+    cUnit.jitMode = kJitTrace;
+
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+    /* Initialize the basic block list */
+    blockList = &cUnit.blockList;
+    dvmInitGrowableList(blockList, 8);
+
+    /* Identify traces that we don't want to compile */
+    if (gDvmJit.classTable) {
+        bool classFound = searchClassTablePrefix(desc->method);
+        if (gDvmJit.classTable && gDvmJit.includeSelectedMethod != classFound) {
+            return false;
+        }
+    }
+    if (gDvmJit.methodTable) {
+        int len = strlen(desc->method->clazz->descriptor) +
+                  strlen(desc->method->name) + 1;
+        char *fullSignature = (char *)dvmCompilerNew(len, true);
+        strcpy(fullSignature, desc->method->clazz->descriptor);
+        strcat(fullSignature, desc->method->name);
+
+        int hashValue = dvmComputeUtf8Hash(fullSignature);
+
+        /*
+         * Doing three levels of screening to see whether we want to skip
+         * compiling this method
+         */
+
+        /* First, check the full "class;method" signature */
+        bool methodFound =
+            dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               fullSignature, (HashCompareFunc) strcmp,
+                               false) !=
+            NULL;
+
+        /* Full signature not found - check the enclosing class */
+        if (methodFound == false) {
+            int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor);
+            methodFound =
+                dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                               (char *) desc->method->clazz->descriptor,
+                               (HashCompareFunc) strcmp, false) !=
+                NULL;
+            /* Enclosing class not found - check the method name */
+            if (methodFound == false) {
+                int hashValue = dvmComputeUtf8Hash(desc->method->name);
+                methodFound =
+                    dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+                                   (char *) desc->method->name,
+                                   (HashCompareFunc) strcmp, false) !=
+                    NULL;
+
+                /*
+                 * Debug by call-graph is enabled. Check if the debug list
+                 * covers any methods on the VM stack.
+                 */
+                if (methodFound == false && gDvmJit.checkCallGraph == true) {
+                    methodFound =
+                        filterMethodByCallGraph(info->requestingThread,
+                                                desc->method->name);
+                }
+            }
+        }
+
+        /*
+         * Under the following conditions, the trace will be *conservatively*
+         * compiled by only containing single-step instructions to and from the
+         * interpreter.
+         * 1) If includeSelectedMethod == false, the method matches the full or
+         *    partial signature stored in the hash table.
+         *
+         * 2) If includeSelectedMethod == true, the method does not match the
+         *    full and partial signature stored in the hash table.
+         */
+        if (gDvmJit.methodTable && gDvmJit.includeSelectedMethod != methodFound) {
+#ifdef ARCH_IA32
+            return false;
+#else
+            cUnit.allSingleStep = true;
+#endif
+        } else {
+            /* Compile the trace as normal */
+
+            /* Print the method we cherry picked */
+            if (gDvmJit.includeSelectedMethod == true) {
+                cUnit.printMe = true;
+            }
+        }
+    }
+
+    // Each pair is a range, check whether curOffset falls into a range.
+    bool includeOffset = (gDvmJit.num_entries_pcTable < 2);
+    for (int pcOff = 0; pcOff < gDvmJit.num_entries_pcTable; ) {
+        if (pcOff+1 >= gDvmJit.num_entries_pcTable) {
+          break;
+        }
+        if (curOffset >= gDvmJit.pcTable[pcOff] && curOffset <= gDvmJit.pcTable[pcOff+1]) {
+            includeOffset = true;
+            break;
+        }
+        pcOff += 2;
+    }
+    if (!includeOffset) {
+        return false;
+    }
+
+    /* Allocate the entry block */
+    curBB = dvmCompilerNewBB(kEntryBlock, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+    curBB->startOffset = curOffset;
+
+    entryCodeBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) entryCodeBB);
+    entryCodeBB->startOffset = curOffset;
+    curBB->fallThrough = entryCodeBB;
+    curBB = entryCodeBB;
+
+    if (cUnit.printMe) {
+        ALOGD("--------\nCompiler: Building trace for %s, offset %#x",
+             desc->method->name, curOffset);
+    }
+
+    /*
+     * Analyze the trace descriptor and include up to the maximal number
+     * of Dalvik instructions into the IR.
+     */
+    while (1) {
+        MIR *insn;
+        int width;
+        insn = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
+
+        /* The trace should never incude instruction data */
+        assert(width);
+        insn->width = width;
+        traceSize += width;
+        dvmCompilerAppendMIR(curBB, insn);
+        cUnit.numInsts++;
+
+        int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+        if (flags & kInstrInvoke) {
+            const Method *calleeMethod = (const Method *)
+                currRun[JIT_TRACE_CUR_METHOD].info.meta;
+            assert(numInsts == 1);
+            CallsiteInfo *callsiteInfo =
+                (CallsiteInfo *)dvmCompilerNew(sizeof(CallsiteInfo), true);
+            callsiteInfo->classDescriptor = (const char *)
+                currRun[JIT_TRACE_CLASS_DESC].info.meta;
+            callsiteInfo->classLoader = (Object *)
+                currRun[JIT_TRACE_CLASS_LOADER].info.meta;
+            callsiteInfo->method = calleeMethod;
+            insn->meta.callsiteInfo = callsiteInfo;
+        }
+
+        /* Instruction limit reached - terminate the trace here */
+        if (cUnit.numInsts >= numMaxInsts) {
+            break;
+        }
+        if (--numInsts == 0) {
+            if (currRun->info.frag.runEnd) {
+                break;
+            } else {
+                /* Advance to the next trace description (ie non-meta info) */
+                do {
+                    currRun++;
+                } while (!currRun->isCode);
+
+                /* Dummy end-of-run marker seen */
+                if (currRun->info.frag.numInsts == 0) {
+                    break;
+                }
+
+                curBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+                dvmInsertGrowableList(blockList, (intptr_t) curBB);
+                curOffset = currRun->info.frag.startOffset;
+                numInsts = currRun->info.frag.numInsts;
+                curBB->startOffset = curOffset;
+                codePtr = dexCode->insns + curOffset;
+            }
+        } else {
+            curOffset += width;
+            codePtr += width;
+        }
+    }
+
+#if defined(WITH_JIT_TUNING)
+    /* Convert # of half-word to bytes */
+    methodStats->compiledDalvikSize += traceSize * 2;
+#endif
+
+    /*
+     * Now scan basic blocks containing real code to connect the
+     * taken/fallthrough links. Also create chaining cells for code not included
+     * in the trace.
+     */
+    size_t blockId;
+    for (blockId = 0; blockId < blockList->numUsed; blockId++) {
+        curBB = (BasicBlock *) dvmGrowableListGetElement(blockList, blockId);
+        MIR *lastInsn = curBB->lastMIRInsn;
+        /* Skip empty blocks */
+        if (lastInsn == NULL) {
+            continue;
+        }
+        curOffset = lastInsn->offset;
+        unsigned int targetOffset = curOffset;
+        unsigned int fallThroughOffset = curOffset + lastInsn->width;
+        bool isInvoke = false;
+        const Method *callee = NULL;
+
+        findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset,
+                          &targetOffset, &isInvoke, &callee);
+
+        /* Link the taken and fallthrough blocks */
+        BasicBlock *searchBB;
+
+        int flags = dexGetFlagsFromOpcode(lastInsn->dalvikInsn.opcode);
+
+        if (flags & kInstrInvoke) {
+            cUnit.hasInvoke = true;
+        }
+
+        /* Backward branch seen */
+        if (isInvoke == false &&
+            (flags & kInstrCanBranch) != 0 &&
+            targetOffset < curOffset &&
+            (optHints & JIT_OPT_NO_LOOP) == 0) {
+            dvmCompilerArenaReset();
+            return compileLoop(&cUnit, startOffset, desc, numMaxInsts,
+                               info, bailPtr, optHints);
+        }
+
+        /* No backward branch in the trace - start searching the next BB */
+        size_t searchBlockId;
+        for (searchBlockId = blockId+1; searchBlockId < blockList->numUsed;
+             searchBlockId++) {
+            searchBB = (BasicBlock *) dvmGrowableListGetElement(blockList,
+                                                                searchBlockId);
+            if (targetOffset == searchBB->startOffset) {
+                curBB->taken = searchBB;
+                dvmCompilerSetBit(searchBB->predecessors, curBB->id);
+            }
+            if (fallThroughOffset == searchBB->startOffset) {
+                curBB->fallThrough = searchBB;
+                dvmCompilerSetBit(searchBB->predecessors, curBB->id);
+
+                /*
+                 * Fallthrough block of an invoke instruction needs to be
+                 * aligned to 4-byte boundary (alignment instruction to be
+                 * inserted later.
+                 */
+                if (flags & kInstrInvoke) {
+                    searchBB->isFallThroughFromInvoke = true;
+                }
+            }
+        }
+
+        /*
+         * Some blocks are ended by non-control-flow-change instructions,
+         * currently only due to trace length constraint. In this case we need
+         * to generate an explicit branch at the end of the block to jump to
+         * the chaining cell.
+         */
+        curBB->needFallThroughBranch =
+            ((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+                       kInstrInvoke)) == 0);
+        if (lastInsn->dalvikInsn.opcode == OP_PACKED_SWITCH ||
+            lastInsn->dalvikInsn.opcode == OP_SPARSE_SWITCH) {
+            int i;
+            const u2 *switchData = desc->method->insns + lastInsn->offset +
+                             lastInsn->dalvikInsn.vB;
+            int size = switchData[1];
+            int maxChains = MIN(size, MAX_CHAINED_SWITCH_CASES);
+
+            /*
+             * Generate the landing pad for cases whose ranks are higher than
+             * MAX_CHAINED_SWITCH_CASES. The code will re-enter the interpreter
+             * through the NoChain point.
+             */
+            if (maxChains != size) {
+                cUnit.switchOverflowPad =
+                    desc->method->insns + lastInsn->offset;
+            }
+
+            s4 *targets = (s4 *) (switchData + 2 +
+                    (lastInsn->dalvikInsn.opcode == OP_PACKED_SWITCH ?
+                     2 : size * 2));
+
+            /* One chaining cell for the first MAX_CHAINED_SWITCH_CASES cases */
+            for (i = 0; i < maxChains; i++) {
+                BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+                                                         numBlocks++);
+                dvmInsertGrowableList(blockList, (intptr_t) caseChain);
+                caseChain->startOffset = lastInsn->offset + targets[i];
+            }
+
+            /* One more chaining cell for the default case */
+            BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+                                                     numBlocks++);
+            dvmInsertGrowableList(blockList, (intptr_t) caseChain);
+            caseChain->startOffset = lastInsn->offset + lastInsn->width;
+        /* Fallthrough block not included in the trace */
+        } else if (!isUnconditionalBranch(lastInsn) &&
+                   curBB->fallThrough == NULL) {
+            BasicBlock *fallThroughBB;
+            /*
+             * If the chaining cell is after an invoke or
+             * instruction that cannot change the control flow, request a hot
+             * chaining cell.
+             */
+            if (isInvoke || curBB->needFallThroughBranch) {
+                fallThroughBB = dvmCompilerNewBB(kChainingCellHot, numBlocks++);
+            } else {
+                fallThroughBB = dvmCompilerNewBB(kChainingCellNormal,
+                                                 numBlocks++);
+            }
+            dvmInsertGrowableList(blockList, (intptr_t) fallThroughBB);
+            fallThroughBB->startOffset = fallThroughOffset;
+            curBB->fallThrough = fallThroughBB;
+            dvmCompilerSetBit(fallThroughBB->predecessors, curBB->id);
+        }
+        /* Target block not included in the trace */
+        if (curBB->taken == NULL &&
+            (isGoto(lastInsn) || isInvoke ||
+            (targetOffset != UNKNOWN_TARGET && targetOffset != curOffset))) {
+            BasicBlock *newBB = NULL;
+            if (isInvoke) {
+                /* Monomorphic callee */
+                if (callee) {
+                    /* JNI call doesn't need a chaining cell */
+                    if (!dvmIsNativeMethod(callee)) {
+                        newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton,
+                                                 numBlocks++);
+                        newBB->startOffset = 0;
+                        newBB->containingMethod = callee;
+                    }
+                /* Will resolve at runtime */
+                } else {
+                    newBB = dvmCompilerNewBB(kChainingCellInvokePredicted,
+                                             numBlocks++);
+                    newBB->startOffset = 0;
+                }
+            /* For unconditional branches, request a hot chaining cell */
+            } else {
+#if !defined(WITH_SELF_VERIFICATION)
+                newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
+                                                  kChainingCellHot :
+                                                  kChainingCellNormal,
+                                         numBlocks++);
+                newBB->startOffset = targetOffset;
+#else
+                /* Handle branches that branch back into the block */
+                if (targetOffset >= curBB->firstMIRInsn->offset &&
+                    targetOffset <= curBB->lastMIRInsn->offset) {
+                    newBB = dvmCompilerNewBB(kChainingCellBackwardBranch,
+                                             numBlocks++);
+                } else {
+                    newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
+                                                      kChainingCellHot :
+                                                      kChainingCellNormal,
+                                             numBlocks++);
+                }
+                newBB->startOffset = targetOffset;
+#endif
+            }
+            if (newBB) {
+                curBB->taken = newBB;
+                dvmCompilerSetBit(newBB->predecessors, curBB->id);
+                dvmInsertGrowableList(blockList, (intptr_t) newBB);
+            }
+        }
+    }
+
+    /* Now create a special block to host PC reconstruction code */
+    curBB = dvmCompilerNewBB(kPCReconstruction, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+
+    /* And one final block that publishes the PC and raise the exception */
+    curBB = dvmCompilerNewBB(kExceptionHandling, numBlocks++);
+    dvmInsertGrowableList(blockList, (intptr_t) curBB);
+    cUnit.puntBlock = curBB;
+
+    if (cUnit.printMe) {
+        char* signature =
+            dexProtoCopyMethodDescriptor(&desc->method->prototype);
+        ALOGD("TRACEINFO (%d): 0x%08x %s%s.%s %#x %d of %d, %d blocks",
+            compilationId,
+            (intptr_t) desc->method->insns,
+            desc->method->clazz->descriptor,
+            desc->method->name,
+            signature,
+            desc->trace[0].info.frag.startOffset,
+            traceSize,
+            dexCode->insnsSize,
+            numBlocks);
+        free(signature);
+    }
+
+    cUnit.numBlocks = numBlocks;
+
+    /* Set the instruction set to use (NOTE: later components may change it) */
+    cUnit.instructionSet = dvmCompilerInstructionSet();
+
+    /* Inline transformation @ the MIR level */
+    if (cUnit.hasInvoke && !(gDvmJit.disableOpt & (1 << kMethodInlining))) {
+        dvmCompilerInlineMIR(&cUnit, info);
+    }
+
+    cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
+    /* Preparation for SSA conversion */
+    dvmInitializeSSAConversion(&cUnit);
+
+    dvmCompilerNonLoopAnalysis(&cUnit);
+
+#ifndef ARCH_IA32
+    dvmCompilerInitializeRegAlloc(&cUnit);  // Needs to happen after SSA naming
+#endif
+
+    if (cUnit.printMe) {
+        dvmCompilerDumpCompilationUnit(&cUnit);
+    }
+
+#ifndef ARCH_IA32
+    /* Allocate Registers using simple local allocation scheme */
+    dvmCompilerLocalRegAlloc(&cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(&cUnit);
+#else /* ARCH_IA32 */
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(&cUnit, info);
+#endif
+
+    /* Convert LIR into machine code. Loop for recoverable retries */
+    do {
+        dvmCompilerAssembleLIR(&cUnit, info);
+        cUnit.assemblerRetries++;
+        if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+            ALOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+                  cUnit.assemblerStatus);
+    } while (cUnit.assemblerStatus == kRetryAll);
+
+    if (cUnit.printMe) {
+        ALOGD("Trace Dalvik PC: %p", startCodePtr);
+        dvmCompilerCodegenDump(&cUnit);
+        ALOGD("End %s%s, %d Dalvik instructions",
+             desc->method->clazz->descriptor, desc->method->name,
+             cUnit.numInsts);
+    }
+
+    if (cUnit.assemblerStatus == kRetryHalve) {
+        /* Reset the compiler resource pool before retry */
+        dvmCompilerArenaReset();
+
+        /* Halve the instruction count and start from the top */
+        return dvmCompileTrace(desc, cUnit.numInsts / 2, info, bailPtr,
+                               optHints);
+    }
+
+    /*
+     * If this trace uses class objects as constants,
+     * dvmJitInstallClassObjectPointers will switch the thread state
+     * to running and look up the class pointers using the descriptor/loader
+     * tuple stored in the callsite info structure. We need to make this window
+     * as short as possible since it is blocking GC.
+     */
+    if (cUnit.hasClassLiterals && info->codeAddress) {
+        dvmJitInstallClassObjectPointers(&cUnit, (char *) info->codeAddress);
+    }
+
+    /*
+     * Since callsiteinfo is allocated from the arena, delay the reset until
+     * class pointers are resolved.
+     */
+    dvmCompilerArenaReset();
+
+    assert(cUnit.assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+    methodStats->nativeSize += cUnit.totalSize;
+#endif
+
+    return info->codeAddress != NULL;
+}
diff --git a/vm/compiler/InlineTransformation.cpp b/vm/compiler/InlineTransformation.cpp
new file mode 100644
index 0000000..650340c
--- /dev/null
+++ b/vm/compiler/InlineTransformation.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "libdex/DexOpcodes.h"
+
+/* Convert the reg id from the callee to the original id passed by the caller */
+static inline u4 convertRegId(const DecodedInstruction *invoke,
+                              const Method *calleeMethod,
+                              int calleeRegId, bool isRange)
+{
+    /* The order in the original arg passing list */
+    int rank = calleeRegId -
+               (calleeMethod->registersSize - calleeMethod->insSize);
+    assert(rank >= 0);
+    if (!isRange) {
+        return invoke->arg[rank];
+    } else {
+        return invoke->vC + rank;
+    }
+}
+
+static bool inlineGetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    BasicBlock *moveResultBB = invokeBB->fallThrough;
+    MIR *moveResultMIR = moveResultBB->firstMIRInsn;
+    MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction getterInsn;
+
+    /*
+     * Not all getter instructions have vC but vC will be read by
+     * dvmCompilerGetDalvikDisassembly unconditionally.
+     * Initialize it here to get Valgrind happy.
+     */
+    getterInsn.vC = 0;
+
+    dexDecodeInstruction(calleeMethod->insns, &getterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
+        return false;
+
+    /*
+     * Some getters (especially invoked through interface) are not followed
+     * by a move result.
+     */
+    if ((moveResultMIR == NULL) ||
+        (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT &&
+         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT &&
+         moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) {
+        return false;
+    }
+
+    int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode];
+
+    /* Expecting vA to be the destination register */
+    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+        ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode);
+        dvmAbort();
+    }
+
+    if (dfFlags & DF_UB) {
+        getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     getterInsn.vB, isRange);
+    }
+
+    if (dfFlags & DF_UC) {
+        getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     getterInsn.vC, isRange);
+    }
+
+    getterInsn.vA = moveResultMIR->dalvikInsn.vA;
+
+    /* Now setup the Dalvik instruction with converted src/dst registers */
+    newGetterMIR->dalvikInsn = getterInsn;
+
+    newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode);
+
+    newGetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+    /*
+     * If the getter instruction is about to raise any exception, punt to the
+     * interpreter and re-execute the invoke.
+     */
+    newGetterMIR->offset = invokeMIR->offset;
+
+    newGetterMIR->meta.calleeMethod = calleeMethod;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
+
+    if (isPredicted) {
+        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+        /* Use vC to denote the first argument (ie this) */
+        if (!isRange) {
+            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+        }
+
+        moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
+
+        dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
+        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokePolyGetterInlined++;
+#endif
+    } else {
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+        moveResultMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonoGetterInlined++;
+#endif
+    }
+
+    return true;
+}
+
+static bool inlineSetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction setterInsn;
+
+    /*
+     * Not all setter instructions have vC but vC will be read by
+     * dvmCompilerGetDalvikDisassembly unconditionally.
+     * Initialize it here to get Valgrind happy.
+     */
+    setterInsn.vC = 0;
+
+    dexDecodeInstruction(calleeMethod->insns, &setterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
+        return false;
+
+    int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode];
+
+    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+        setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vA, isRange);
+
+    }
+
+    if (dfFlags & DF_UB) {
+        setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vB, isRange);
+
+    }
+
+    if (dfFlags & DF_UC) {
+        setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+                                     setterInsn.vC, isRange);
+    }
+
+    /* Now setup the Dalvik instruction with converted src/dst registers */
+    newSetterMIR->dalvikInsn = setterInsn;
+
+    newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode);
+
+    newSetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+    /*
+     * If the setter instruction is about to raise any exception, punt to the
+     * interpreter and re-execute the invoke.
+     */
+    newSetterMIR->offset = invokeMIR->offset;
+
+    newSetterMIR->meta.calleeMethod = calleeMethod;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
+
+    if (isPredicted) {
+        MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+        /* Use vC to denote the first argument (ie this) */
+        if (!isRange) {
+            invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+        }
+
+        dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
+        invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokePolySetterInlined++;
+#endif
+    } else {
+        /*
+         * The invoke becomes no-op so it needs an explicit branch to jump to
+         * the chaining cell.
+         */
+        invokeBB->needFallThroughBranch = true;
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonoSetterInlined++;
+#endif
+    }
+
+    return true;
+}
+
+static bool tryInlineSingletonCallsite(CompilationUnit *cUnit,
+                                       const Method *calleeMethod,
+                                       MIR *invokeMIR,
+                                       BasicBlock *invokeBB,
+                                       bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return false;
+
+    CompilerMethodStats *methodStats =
+        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+    /* Empty callee - do nothing */
+    if (methodStats->attributes & METHOD_IS_EMPTY) {
+        /* The original invoke instruction is effectively turned into NOP */
+        invokeMIR->OptimizationFlags |= MIR_INLINED;
+        /*
+         * Need to insert an explicit branch to catch the falling knife (into
+         * the PC reconstruction or chaining cell).
+         */
+        invokeBB->needFallThroughBranch = true;
+        return true;
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
+                            isRange);
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
+                            isRange);
+    }
+    return false;
+}
+
+static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB)
+{
+    MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+    *invokeMIRSlow = *invokeMIR;
+    invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
+    invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+    return true;
+}
+
+static bool tryInlineVirtualCallsite(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB,
+                                     bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return false;
+
+    CompilerMethodStats *methodStats =
+        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+    /* Empty callee - do nothing by checking the clazz pointer */
+    if (methodStats->attributes & METHOD_IS_EMPTY) {
+        return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR,
+                                        invokeBB);
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
+                            isRange);
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
+                            isRange);
+    }
+    return false;
+}
+
+
+void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    bool isRange = false;
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+    /*
+     * Analyze the basic block containing an invoke to see if it can be inlined
+     */
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->blockType != kDalvikByteCode)
+            continue;
+        MIR *lastMIRInsn = bb->lastMIRInsn;
+        Opcode opcode = lastMIRInsn->dalvikInsn.opcode;
+        int flags = (int)dexGetFlagsFromOpcode(opcode);
+
+        /* No invoke - continue */
+        if ((flags & kInstrInvoke) == 0)
+            continue;
+
+        /* Disable inlining when doing method tracing */
+        if (gDvmJit.methodTraceSupport)
+            continue;
+
+        /*
+         * If the invoke itself is selected for single stepping, don't bother
+         * to inline it.
+         */
+        if (SINGLE_STEP_OP(opcode))
+            continue;
+
+        const Method *calleeMethod;
+
+        switch (opcode) {
+            case OP_INVOKE_SUPER:
+            case OP_INVOKE_DIRECT:
+            case OP_INVOKE_STATIC:
+            case OP_INVOKE_SUPER_QUICK:
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+            case OP_INVOKE_DIRECT_RANGE:
+            case OP_INVOKE_STATIC_RANGE:
+            case OP_INVOKE_SUPER_QUICK_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                calleeMethod = NULL;
+                break;
+        }
+
+        if (calleeMethod) {
+            bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod,
+                                                      lastMIRInsn, bb, isRange);
+            if (!inlined &&
+                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
+                !dvmIsNativeMethod(calleeMethod)) {
+                CompilerMethodStats *methodStats =
+                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+                if ((methodStats->attributes & METHOD_IS_LEAF) &&
+                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
+                    /* Callee has been previously compiled */
+                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
+                    } else {
+                        /* Compile the callee first */
+                        dvmCompileMethod(calleeMethod, info);
+                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                            lastMIRInsn->OptimizationFlags |=
+                                MIR_INVOKE_METHOD_JIT;
+                        } else {
+                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
+                        }
+                    }
+                }
+            }
+            return;
+        }
+
+        switch (opcode) {
+            case OP_INVOKE_VIRTUAL:
+            case OP_INVOKE_VIRTUAL_QUICK:
+            case OP_INVOKE_INTERFACE:
+                isRange = false;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+            case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+            case OP_INVOKE_INTERFACE_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                break;
+        }
+
+        if (calleeMethod) {
+            bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod,
+                                                    lastMIRInsn, bb, isRange);
+            if (!inlined &&
+                !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
+                !dvmIsNativeMethod(calleeMethod)) {
+                CompilerMethodStats *methodStats =
+                    dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+                if ((methodStats->attributes & METHOD_IS_LEAF) &&
+                    !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
+                    /* Callee has been previously compiled */
+                    if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                        lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
+                    } else {
+                        /* Compile the callee first */
+                        dvmCompileMethod(calleeMethod, info);
+                        if (dvmJitGetMethodAddr(calleeMethod->insns)) {
+                            lastMIRInsn->OptimizationFlags |=
+                                MIR_INVOKE_METHOD_JIT;
+                        } else {
+                            methodStats->attributes |= METHOD_CANNOT_COMPILE;
+                        }
+                    }
+                }
+            }
+            return;
+        }
+    }
+}
diff --git a/vm/compiler/IntermediateRep.cpp b/vm/compiler/IntermediateRep.cpp
new file mode 100644
index 0000000..db68c3c
--- /dev/null
+++ b/vm/compiler/IntermediateRep.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+/* Allocate a new basic block */
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId)
+{
+    BasicBlock *bb = (BasicBlock *)dvmCompilerNew(sizeof(BasicBlock), true);
+    bb->blockType = blockType;
+    bb->id = blockId;
+    bb->predecessors = dvmCompilerAllocBitVector(blockId > 32 ? blockId : 32,
+                                                 true /* expandable */);
+    return bb;
+}
+
+/* Insert an MIR instruction to the end of a basic block */
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir)
+{
+    if (bb->firstMIRInsn == NULL) {
+        assert(bb->lastMIRInsn == NULL);
+        bb->lastMIRInsn = bb->firstMIRInsn = mir;
+        mir->prev = mir->next = NULL;
+    } else {
+        bb->lastMIRInsn->next = mir;
+        mir->prev = bb->lastMIRInsn;
+        mir->next = NULL;
+        bb->lastMIRInsn = mir;
+    }
+}
+
+/* Insert an MIR instruction to the head of a basic block */
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir)
+{
+    if (bb->firstMIRInsn == NULL) {
+        assert(bb->lastMIRInsn == NULL);
+        bb->lastMIRInsn = bb->firstMIRInsn = mir;
+        mir->prev = mir->next = NULL;
+    } else {
+        bb->firstMIRInsn->prev = mir;
+        mir->next = bb->firstMIRInsn;
+        mir->prev = NULL;
+        bb->firstMIRInsn = mir;
+    }
+}
+
+/* Insert an MIR instruction after the specified MIR */
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR)
+{
+    newMIR->prev = currentMIR;
+    newMIR->next = currentMIR->next;
+    currentMIR->next = newMIR;
+
+    if (newMIR->next) {
+        /* Is not the last MIR in the block */
+        newMIR->next->prev = newMIR;
+    } else {
+        /* Is the last MIR in the block */
+        bb->lastMIRInsn = newMIR;
+    }
+}
+
+/*
+ * Append an LIR instruction to the LIR list maintained by a compilation
+ * unit
+ */
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir)
+{
+    if (cUnit->firstLIRInsn == NULL) {
+        assert(cUnit->lastLIRInsn == NULL);
+        cUnit->lastLIRInsn = cUnit->firstLIRInsn = lir;
+        lir->prev = lir->next = NULL;
+    } else {
+        cUnit->lastLIRInsn->next = lir;
+        lir->prev = cUnit->lastLIRInsn;
+        lir->next = NULL;
+        cUnit->lastLIRInsn = lir;
+    }
+}
+
+/*
+ * Insert an LIR instruction before the current instruction, which cannot be the
+ * first instruction.
+ *
+ * prevLIR <-> newLIR <-> currentLIR
+ */
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR)
+{
+    assert(currentLIR->prev != NULL);
+    LIR *prevLIR = currentLIR->prev;
+
+    prevLIR->next = newLIR;
+    newLIR->prev = prevLIR;
+    newLIR->next = currentLIR;
+    currentLIR->prev = newLIR;
+}
+
+/*
+ * Insert an LIR instruction after the current instruction, which cannot be the
+ * first instruction.
+ *
+ * currentLIR -> newLIR -> oldNext
+ */
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR)
+{
+    newLIR->prev = currentLIR;
+    newLIR->next = currentLIR->next;
+    currentLIR->next = newLIR;
+    newLIR->next->prev = newLIR;
+}
diff --git a/vm/compiler/Loop.cpp b/vm/compiler/Loop.cpp
new file mode 100644
index 0000000..dc04a11
--- /dev/null
+++ b/vm/compiler/Loop.cpp
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+#include "Loop.h"
+
+#define DEBUG_LOOP(X)
+
+#if 0
+/* Debugging routines */
+static void dumpConstants(CompilationUnit *cUnit)
+{
+    int i;
+    ALOGE("LOOP starting offset: %x", cUnit->entryBlock->startOffset);
+    for (i = 0; i < cUnit->numSSARegs; i++) {
+        if (dvmIsBitSet(cUnit->isConstantV, i)) {
+            int subNReg = dvmConvertSSARegToDalvik(cUnit, i);
+            ALOGE("CONST: s%d(v%d_%d) has %d", i,
+                 DECODE_REG(subNReg), DECODE_SUB(subNReg),
+                 cUnit->constantValues[i]);
+        }
+    }
+}
+
+static void dumpIVList(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    GrowableList *ivList = cUnit->loopAnalysis->ivList;
+
+    for (i = 0; i < ivList->numUsed; i++) {
+        InductionVariableInfo *ivInfo =
+            (InductionVariableInfo *) ivList->elemList[i];
+        int iv = dvmConvertSSARegToDalvik(cUnit, ivInfo->ssaReg);
+        /* Basic IV */
+        if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+            ALOGE("BIV %d: s%d(v%d_%d) + %d", i,
+                 ivInfo->ssaReg,
+                 DECODE_REG(iv), DECODE_SUB(iv),
+                 ivInfo->inc);
+        /* Dependent IV */
+        } else {
+            int biv = dvmConvertSSARegToDalvik(cUnit, ivInfo->basicSSAReg);
+
+            ALOGE("DIV %d: s%d(v%d_%d) = %d * s%d(v%d_%d) + %d", i,
+                 ivInfo->ssaReg,
+                 DECODE_REG(iv), DECODE_SUB(iv),
+                 ivInfo->m,
+                 ivInfo->basicSSAReg,
+                 DECODE_REG(biv), DECODE_SUB(biv),
+                 ivInfo->c);
+        }
+    }
+}
+
+static void dumpHoistedChecks(CompilationUnit *cUnit)
+{
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    unsigned int i;
+
+    for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+        ArrayAccessInfo *arrayAccessInfo =
+            GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                       ArrayAccessInfo*, i);
+        int arrayReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+        int idxReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+        ALOGE("Array access %d", i);
+        ALOGE("  arrayReg %d", arrayReg);
+        ALOGE("  idxReg %d", idxReg);
+        ALOGE("  endReg %d", loopAnalysis->endConditionReg);
+        ALOGE("  maxC %d", arrayAccessInfo->maxC);
+        ALOGE("  minC %d", arrayAccessInfo->minC);
+        ALOGE("  opcode %d", loopAnalysis->loopBranchOpcode);
+    }
+}
+
+#endif
+
+static BasicBlock *findPredecessorBlock(const CompilationUnit *cUnit,
+                                        const BasicBlock *bb)
+{
+    int numPred = dvmCountSetBits(bb->predecessors);
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+
+    if (numPred == 1) {
+        int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+        return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                        predIdx);
+    /* First loop block */
+    } else if ((numPred == 2) &&
+               dvmIsBitSet(bb->predecessors, cUnit->entryBlock->id)) {
+        while (true) {
+            int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+            if (predIdx == cUnit->entryBlock->id) continue;
+            return (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                            predIdx);
+        }
+    /* Doesn't support other shape of control flow yet */
+    } else {
+        return NULL;
+    }
+}
+
+/* Used for normalized loop exit condition checks */
+static Opcode negateOpcode(Opcode opcode)
+{
+    switch (opcode) {
+        /* reg/reg cmp */
+        case OP_IF_EQ:
+            return OP_IF_NE;
+        case OP_IF_NE:
+            return OP_IF_EQ;
+        case OP_IF_LT:
+            return OP_IF_GE;
+        case OP_IF_GE:
+            return OP_IF_LT;
+        case OP_IF_GT:
+            return OP_IF_LE;
+        case OP_IF_LE:
+            return OP_IF_GT;
+        /* reg/zero cmp */
+        case OP_IF_EQZ:
+            return OP_IF_NEZ;
+        case OP_IF_NEZ:
+            return OP_IF_EQZ;
+        case OP_IF_LTZ:
+            return OP_IF_GEZ;
+        case OP_IF_GEZ:
+            return OP_IF_LTZ;
+        case OP_IF_GTZ:
+            return OP_IF_LEZ;
+        case OP_IF_LEZ:
+            return OP_IF_GTZ;
+        default:
+            ALOGE("opcode %d cannot be negated", opcode);
+            dvmAbort();
+            break;
+    }
+    return (Opcode)-1;  // unreached
+}
+
+/*
+ * A loop is considered optimizable if:
+ * 1) It has one basic induction variable.
+ * 2) The loop back branch compares the BIV with a constant.
+ * 3) We need to normalize the loop exit condition so that the loop is exited
+ *    via the taken path.
+ * 4) If it is a count-up loop, the condition is GE/GT. Otherwise it is
+ *    LE/LT/LEZ/LTZ for a count-down loop.
+ *
+ * Return false for loops that fail the above tests.
+ */
+static bool isSimpleCountedLoop(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    BasicBlock *loopBackBlock = cUnit->entryBlock->fallThrough;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+
+    if (loopAnalysis->numBasicIV != 1) return false;
+    for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+        InductionVariableInfo *ivInfo;
+
+        ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+        /* Count up or down loop? */
+        if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+            /* Infinite loop */
+            if (ivInfo->inc == 0) {
+                return false;
+            }
+            loopAnalysis->isCountUpLoop = ivInfo->inc > 0;
+            break;
+        }
+    }
+
+    /* Find the block that ends with a branch to exit the loop */
+    while (true) {
+        loopBackBlock = findPredecessorBlock(cUnit, loopBackBlock);
+        /* Loop structure not recognized as counted blocks */
+        if (loopBackBlock == NULL) {
+            return false;
+        }
+        /* Unconditional goto - continue to trace up the predecessor chain */
+        if (loopBackBlock->taken == NULL) {
+            continue;
+        }
+        break;
+    }
+
+    MIR *branch = loopBackBlock->lastMIRInsn;
+    Opcode opcode = branch->dalvikInsn.opcode;
+
+    /* Last instruction is not a conditional branch - bail */
+    if (dexGetFlagsFromOpcode(opcode) != (kInstrCanContinue|kInstrCanBranch)) {
+        return false;
+    }
+
+    int endSSAReg;
+    int endDalvikReg;
+
+    /* reg/reg comparison */
+    if (branch->ssaRep->numUses == 2) {
+        if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) {
+            endSSAReg = branch->ssaRep->uses[1];
+        } else if (branch->ssaRep->uses[1] == loopAnalysis->ssaBIV) {
+            endSSAReg = branch->ssaRep->uses[0];
+            opcode = negateOpcode(opcode);
+        } else {
+            return false;
+        }
+        endDalvikReg = dvmConvertSSARegToDalvik(cUnit, endSSAReg);
+        /*
+         * If the comparison is not between the BIV and a loop invariant,
+         * return false. endDalvikReg is loop invariant if one of the
+         * following is true:
+         * - It is not defined in the loop (ie DECODE_SUB returns 0)
+         * - It is reloaded with a constant
+         */
+        if ((DECODE_SUB(endDalvikReg) != 0) &&
+            !dvmIsBitSet(cUnit->isConstantV, endSSAReg)) {
+            return false;
+        }
+    /* Compare against zero */
+    } else if (branch->ssaRep->numUses == 1) {
+        if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) {
+            /* Keep the compiler happy */
+            endDalvikReg = -1;
+        } else {
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    /* Normalize the loop exit check as "if (iv op end) exit;" */
+    if (loopBackBlock->taken->blockType == kDalvikByteCode) {
+        opcode = negateOpcode(opcode);
+    }
+
+    if (loopAnalysis->isCountUpLoop) {
+        /*
+         * If the normalized condition op is not > or >=, this is not an
+         * optimization candidate.
+         */
+        switch (opcode) {
+            case OP_IF_GT:
+            case OP_IF_GE:
+                break;
+            default:
+                return false;
+        }
+        loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg);
+    } else  {
+        /*
+         * If the normalized condition op is not < or <=, this is not an
+         * optimization candidate.
+         */
+        switch (opcode) {
+            case OP_IF_LT:
+            case OP_IF_LE:
+                loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg);
+                break;
+            case OP_IF_LTZ:
+            case OP_IF_LEZ:
+                break;
+            default:
+                return false;
+        }
+    }
+    /*
+     * Remember the normalized opcode, which will be used to determine the end
+     * value used for the yanked range checks.
+     */
+    loopAnalysis->loopBranchOpcode = opcode;
+    return true;
+}
+
+/*
+ * Record the upper and lower bound information for range checks for each
+ * induction variable. If array A is accessed by index "i+5", the upper and
+ * lower bound will be len(A)-5 and -5, respectively.
+ */
+static void updateRangeCheckInfo(CompilationUnit *cUnit, int arrayReg,
+                                 int idxReg)
+{
+    InductionVariableInfo *ivInfo;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    unsigned int i, j;
+
+    for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+        ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+        if (ivInfo->ssaReg == idxReg) {
+            ArrayAccessInfo *arrayAccessInfo = NULL;
+            for (j = 0; j < loopAnalysis->arrayAccessInfo->numUsed; j++) {
+                ArrayAccessInfo *existingArrayAccessInfo =
+                    GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                               ArrayAccessInfo*,
+                               j);
+                if (existingArrayAccessInfo->arrayReg == arrayReg) {
+                    if (ivInfo->c > existingArrayAccessInfo->maxC) {
+                        existingArrayAccessInfo->maxC = ivInfo->c;
+                    }
+                    if (ivInfo->c < existingArrayAccessInfo->minC) {
+                        existingArrayAccessInfo->minC = ivInfo->c;
+                    }
+                    arrayAccessInfo = existingArrayAccessInfo;
+                    break;
+                }
+            }
+            if (arrayAccessInfo == NULL) {
+                arrayAccessInfo =
+                    (ArrayAccessInfo *)dvmCompilerNew(sizeof(ArrayAccessInfo),
+                                                      false);
+                arrayAccessInfo->ivReg = ivInfo->basicSSAReg;
+                arrayAccessInfo->arrayReg = arrayReg;
+                arrayAccessInfo->maxC = (ivInfo->c > 0) ? ivInfo->c : 0;
+                arrayAccessInfo->minC = (ivInfo->c < 0) ? ivInfo->c : 0;
+                dvmInsertGrowableList(loopAnalysis->arrayAccessInfo,
+                                      (intptr_t) arrayAccessInfo);
+            }
+            break;
+        }
+    }
+}
+
+/* Returns true if the loop body cannot throw any exceptions */
+static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
+{
+    BasicBlock *loopBody = cUnit->entryBlock->fallThrough;
+    MIR *mir;
+    bool loopBodyCanThrow = false;
+
+    for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
+        DecodedInstruction *dInsn = &mir->dalvikInsn;
+        int dfAttributes =
+            dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
+
+        /* Skip extended MIR instructions */
+        if ((u2) dInsn->opcode >= kNumPackedOpcodes) continue;
+
+        int instrFlags = dexGetFlagsFromOpcode(dInsn->opcode);
+
+        /* Instruction is clean */
+        if ((instrFlags & kInstrCanThrow) == 0) continue;
+
+        /*
+         * Currently we can only optimize away null and range checks. Punt on
+         * instructions that can throw due to other exceptions.
+         */
+        if (!(dfAttributes & DF_HAS_NR_CHECKS)) {
+            loopBodyCanThrow = true;
+            continue;
+        }
+
+        /*
+         * This comparison is redundant now, but we will have more than one
+         * group of flags to check soon.
+         */
+        if (dfAttributes & DF_HAS_NR_CHECKS) {
+            /*
+             * Check if the null check is applied on a loop invariant register?
+             * If the register's SSA id is less than the number of Dalvik
+             * registers, then it is loop invariant.
+             */
+            int refIdx;
+            switch (dfAttributes & DF_HAS_NR_CHECKS) {
+                case DF_NULL_N_RANGE_CHECK_0:
+                    refIdx = 0;
+                    break;
+                case DF_NULL_N_RANGE_CHECK_1:
+                    refIdx = 1;
+                    break;
+                case DF_NULL_N_RANGE_CHECK_2:
+                    refIdx = 2;
+                    break;
+                default:
+                    refIdx = 0;
+                    ALOGE("Jit: bad case in doLoopBodyCodeMotion");
+                    dvmCompilerAbort(cUnit);
+            }
+
+            int useIdx = refIdx + 1;
+            int subNRegArray =
+                dvmConvertSSARegToDalvik(cUnit, mir->ssaRep->uses[refIdx]);
+            int arraySub = DECODE_SUB(subNRegArray);
+
+            /*
+             * If the register is never updated in the loop (ie subscript == 0),
+             * it is an optimization candidate.
+             */
+            if (arraySub != 0) {
+                loopBodyCanThrow = true;
+                continue;
+            }
+
+            /*
+             * Then check if the range check can be hoisted out of the loop if
+             * it is basic or dependent induction variable.
+             */
+            if (dvmIsBitSet(cUnit->loopAnalysis->isIndVarV,
+                            mir->ssaRep->uses[useIdx])) {
+                mir->OptimizationFlags |=
+                    MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK;
+                updateRangeCheckInfo(cUnit, mir->ssaRep->uses[refIdx],
+                                     mir->ssaRep->uses[useIdx]);
+            }
+        }
+    }
+
+    return !loopBodyCanThrow;
+}
+
+static void genHoistedChecks(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    BasicBlock *entry = cUnit->entryBlock;
+    LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+    int globalMaxC = 0;
+    int globalMinC = 0;
+    /* Should be loop invariant */
+    int idxReg = 0;
+
+    for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+        ArrayAccessInfo *arrayAccessInfo =
+            GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+                       ArrayAccessInfo*, i);
+        int arrayReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+        idxReg = DECODE_REG(
+            dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+
+        MIR *rangeCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+        rangeCheckMIR->dalvikInsn.opcode = (loopAnalysis->isCountUpLoop) ?
+            (Opcode)kMirOpNullNRangeUpCheck : (Opcode)kMirOpNullNRangeDownCheck;
+        rangeCheckMIR->dalvikInsn.vA = arrayReg;
+        rangeCheckMIR->dalvikInsn.vB = idxReg;
+        rangeCheckMIR->dalvikInsn.vC = loopAnalysis->endConditionReg;
+        rangeCheckMIR->dalvikInsn.arg[0] = arrayAccessInfo->maxC;
+        rangeCheckMIR->dalvikInsn.arg[1] = arrayAccessInfo->minC;
+        rangeCheckMIR->dalvikInsn.arg[2] = loopAnalysis->loopBranchOpcode;
+        dvmCompilerAppendMIR(entry, rangeCheckMIR);
+        if (arrayAccessInfo->maxC > globalMaxC) {
+            globalMaxC = arrayAccessInfo->maxC;
+        }
+        if (arrayAccessInfo->minC < globalMinC) {
+            globalMinC = arrayAccessInfo->minC;
+        }
+    }
+
+    if (loopAnalysis->arrayAccessInfo->numUsed != 0) {
+        if (loopAnalysis->isCountUpLoop) {
+            MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+            boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpLowerBound;
+            boundCheckMIR->dalvikInsn.vA = idxReg;
+            boundCheckMIR->dalvikInsn.vB = globalMinC;
+            dvmCompilerAppendMIR(entry, boundCheckMIR);
+        } else {
+            if (loopAnalysis->loopBranchOpcode == OP_IF_LT ||
+                loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+                MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
+                boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpLowerBound;
+                boundCheckMIR->dalvikInsn.vA = loopAnalysis->endConditionReg;
+                boundCheckMIR->dalvikInsn.vB = globalMinC;
+                /*
+                 * If the end condition is ">" in the source, the check in the
+                 * Dalvik bytecode is OP_IF_LE. In this case add 1 back to the
+                 * constant field to reflect the fact that the smallest index
+                 * value is "endValue + constant + 1".
+                 */
+                if (loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+                    boundCheckMIR->dalvikInsn.vB++;
+                }
+                dvmCompilerAppendMIR(entry, boundCheckMIR);
+            } else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) {
+                /* Array index will fall below 0 */
+                if (globalMinC < 0) {
+                    MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+                                                               true);
+                    boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
+                /* Array index will fall below 0 */
+                if (globalMinC < -1) {
+                    MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR),
+                                                               true);
+                    boundCheckMIR->dalvikInsn.opcode = (Opcode)kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else {
+                ALOGE("Jit: bad case in genHoistedChecks");
+                dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+}
+
+void resetBlockEdges(BasicBlock *bb)
+{
+    bb->taken = NULL;
+    bb->fallThrough = NULL;
+    bb->successorBlockList.blockListType = kNotUsed;
+}
+
+static bool clearPredecessorVector(struct CompilationUnit *cUnit,
+                                   struct BasicBlock *bb)
+{
+    dvmClearAllBits(bb->predecessors);
+    return false;
+}
+
+bool dvmCompilerFilterLoopBlocks(CompilationUnit *cUnit)
+{
+    BasicBlock *firstBB = cUnit->entryBlock->fallThrough;
+
+    int numPred = dvmCountSetBits(firstBB->predecessors);
+    /*
+     * A loop body should have at least two incoming edges.
+     */
+    if (numPred < 2) return false;
+
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Record blocks included in the loop */
+    dvmClearAllBits(cUnit->tempBlockV);
+
+    dvmCompilerSetBit(cUnit->tempBlockV, cUnit->entryBlock->id);
+    dvmCompilerSetBit(cUnit->tempBlockV, firstBB->id);
+
+    BasicBlock *bodyBB = firstBB;
+
+    /*
+     * First try to include the fall-through block in the loop, then the taken
+     * block. Stop loop formation on the first backward branch that enters the
+     * first block (ie only include the inner-most loop).
+     */
+    while (true) {
+        /* Loop formed */
+        if (bodyBB->taken == firstBB) {
+            /* Check if the fallThrough edge will cause a nested loop */
+            if (bodyBB->fallThrough &&
+                dvmIsBitSet(cUnit->tempBlockV, bodyBB->fallThrough->id)) {
+                return false;
+            }
+            /* Single loop formed */
+            break;
+        } else if (bodyBB->fallThrough == firstBB) {
+            /* Check if the taken edge will cause a nested loop */
+            if (bodyBB->taken &&
+                dvmIsBitSet(cUnit->tempBlockV, bodyBB->taken->id)) {
+                return false;
+            }
+            /* Single loop formed */
+            break;
+        }
+
+        /* Inner loops formed first - quit */
+        if (bodyBB->fallThrough &&
+            dvmIsBitSet(cUnit->tempBlockV, bodyBB->fallThrough->id)) {
+            return false;
+        }
+        if (bodyBB->taken &&
+            dvmIsBitSet(cUnit->tempBlockV, bodyBB->taken->id)) {
+            return false;
+        }
+
+        if (bodyBB->fallThrough) {
+            if (bodyBB->fallThrough->iDom == bodyBB) {
+                bodyBB = bodyBB->fallThrough;
+                dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+                /*
+                 * Loop formation to be detected at the beginning of next
+                 * iteration.
+                 */
+                continue;
+            }
+        }
+        if (bodyBB->taken) {
+            if (bodyBB->taken->iDom == bodyBB) {
+                bodyBB = bodyBB->taken;
+                dvmCompilerSetBit(cUnit->tempBlockV, bodyBB->id);
+                /*
+                 * Loop formation to be detected at the beginning of next
+                 * iteration.
+                 */
+                continue;
+            }
+        }
+        /*
+         * Current block is not the immediate dominator of either fallthrough
+         * nor taken block - bail out of loop formation.
+         */
+        return false;
+    }
+
+
+    /* Now mark blocks not included in the loop as hidden */
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(blockList, &iterator);
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (!dvmIsBitSet(cUnit->tempBlockV, bb->id)) {
+            bb->hidden = true;
+            /* Clear the insn list */
+            bb->firstMIRInsn = bb->lastMIRInsn = NULL;
+            resetBlockEdges(bb);
+        }
+    }
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, clearPredecessorVector,
+                                          kAllNodes, false /* isIterative */);
+
+    dvmGrowableListIteratorInit(blockList, &iterator);
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (dvmIsBitSet(cUnit->tempBlockV, bb->id)) {
+            if (bb->taken) {
+                /*
+                 * exit block means we run into control-flow that we don't want
+                 * to handle.
+                 */
+                if (bb->taken == cUnit->exitBlock) {
+                    return false;
+                }
+                if (bb->taken->hidden) {
+                    bb->taken->blockType = kChainingCellNormal;
+                    bb->taken->hidden = false;
+                }
+                dvmCompilerSetBit(bb->taken->predecessors, bb->id);
+            }
+            if (bb->fallThrough) {
+                /*
+                 * exit block means we run into control-flow that we don't want
+                 * to handle.
+                 */
+                if (bb->fallThrough == cUnit->exitBlock) {
+                    return false;
+                }
+                if (bb->fallThrough->hidden) {
+                    bb->fallThrough->blockType = kChainingCellNormal;
+                    bb->fallThrough->hidden = false;
+                }
+                dvmCompilerSetBit(bb->fallThrough->predecessors, bb->id);
+            }
+            /* Loop blocks shouldn't contain any successor blocks (yet) */
+            assert(bb->successorBlockList.blockListType == kNotUsed);
+        }
+    }
+    return true;
+}
+
+/*
+ * Main entry point to do loop optimization.
+ * Return false if sanity checks for loop formation/optimization failed.
+ */
+bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
+{
+    LoopAnalysis *loopAnalysis =
+        (LoopAnalysis *)dvmCompilerNew(sizeof(LoopAnalysis), true);
+    cUnit->loopAnalysis = loopAnalysis;
+
+    /* Constant propagation */
+    cUnit->isConstantV = dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+    cUnit->constantValues =
+        (int *)dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+                              true);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerDoConstantPropagation,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    DEBUG_LOOP(dumpConstants(cUnit);)
+
+    /* Find induction variables - basic and dependent */
+    loopAnalysis->ivList =
+        (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+    dvmInitGrowableList(loopAnalysis->ivList, 4);
+    loopAnalysis->isIndVarV = dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerFindInductionVariables,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    DEBUG_LOOP(dumpIVList(cUnit);)
+
+    /* Only optimize array accesses for simple counted loop for now */
+    if (!isSimpleCountedLoop(cUnit))
+        return false;
+
+    loopAnalysis->arrayAccessInfo =
+        (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
+    dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
+    loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
+    DEBUG_LOOP(dumpHoistedChecks(cUnit);)
+
+    /*
+     * Convert the array access information into extended MIR code in the loop
+     * header.
+     */
+    genHoistedChecks(cUnit);
+    return true;
+}
+
+/*
+ * Select the target block of the backward branch.
+ */
+void dvmCompilerInsertBackwardChaining(CompilationUnit *cUnit)
+{
+    /*
+     * If we are not in self-verification or profiling mode, the backward
+     * branch can go to the entryBlock->fallThrough directly. Suspend polling
+     * code will be generated along the backward branch to honor the suspend
+     * requests.
+     */
+#ifndef ARCH_IA32
+#if !defined(WITH_SELF_VERIFICATION)
+    if (gDvmJit.profileMode != kTraceProfilingContinuous &&
+        gDvmJit.profileMode != kTraceProfilingPeriodicOn) {
+        return;
+    }
+#endif
+#endif
+
+    /*
+     * In self-verification or profiling mode, the backward branch is altered
+     * to go to the backward chaining cell. Without using the backward chaining
+     * cell we won't be able to do check-pointing on the target PC, or count the
+     * number of iterations accurately.
+     */
+    BasicBlock *firstBB = cUnit->entryBlock->fallThrough;
+    BasicBlock *backBranchBB = findPredecessorBlock(cUnit, firstBB);
+    if (backBranchBB->taken == firstBB) {
+        backBranchBB->taken = cUnit->backChainBlock;
+    } else {
+        assert(backBranchBB->fallThrough == firstBB);
+        backBranchBB->fallThrough = cUnit->backChainBlock;
+    }
+    cUnit->backChainBlock->startOffset = firstBB->startOffset;
+}
diff --git a/vm/compiler/Loop.h b/vm/compiler/Loop.h
new file mode 100644
index 0000000..8032093
--- /dev/null
+++ b/vm/compiler/Loop.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_LOOP_H_
+#define DALVIK_VM_LOOP_H_
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef struct LoopAnalysis {
+    BitVector *isIndVarV;               // length == numSSAReg
+    GrowableList *ivList;               // induction variables
+    GrowableList *arrayAccessInfo;      // hoisted checks for array accesses
+    int numBasicIV;                     // number of basic induction variables
+    int ssaBIV;                         // basic IV in SSA name
+    bool isCountUpLoop;                 // count up or down loop
+    Opcode loopBranchOpcode;            // OP_IF_XXX for the loop back branch
+    int endConditionReg;                // vB in "vA op vB"
+    LIR *branchToBody;                  // branch over to the body from entry
+    LIR *branchToPCR;                   // branch over to the PCR cell
+    bool bodyIsClean;                   // loop body cannot throw any exceptions
+} LoopAnalysis;
+
+bool dvmCompilerFilterLoopBlocks(CompilationUnit *cUnit);
+
+/*
+ * An unexecuted code path may contain unresolved fields or classes. Before we
+ * have a quiet resolver we simply bail out of the loop compilation mode.
+ */
+#define BAIL_LOOP_COMPILATION() if (cUnit->jitMode == kJitLoop) {       \
+                                    cUnit->quitLoopMode = true;         \
+                                    return false;                       \
+                                }
+
+#endif  // DALVIK_VM_LOOP_H_
diff --git a/vm/compiler/Ralloc.cpp b/vm/compiler/Ralloc.cpp
new file mode 100644
index 0000000..e2752b1
--- /dev/null
+++ b/vm/compiler/Ralloc.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+/*
+ * Quick & dirty - make FP usage sticky.  This is strictly a hint - local
+ * code generation will handle misses.  It might be worthwhile to collaborate
+ * with dx/dexopt to avoid reusing the same Dalvik temp for values of
+ * different types.
+ */
+static void inferTypes(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    if (bb->blockType != kDalvikByteCode && bb->blockType != kEntryBlock)
+        return;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        SSARepresentation *ssaRep = mir->ssaRep;
+        if (ssaRep) {
+            int i;
+            for (i=0; ssaRep->fpUse && i< ssaRep->numUses; i++) {
+                if (ssaRep->fpUse[i])
+                    cUnit->regLocation[ssaRep->uses[i]].fp = true;
+            }
+            for (i=0; ssaRep->fpDef && i< ssaRep->numDefs; i++) {
+                if (ssaRep->fpDef[i])
+                    cUnit->regLocation[ssaRep->defs[i]].fp = true;
+            }
+        }
+    }
+}
+
+static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
+                                     INVALID_REG, INVALID_SREG};
+
+/*
+ * Local register allocation for simple traces.  Most of the work for
+ * local allocation is done on the fly.  Here we do some initialization
+ * and type inference.
+ */
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit)
+{
+    int i;
+    RegLocation *loc;
+
+    /* Allocate the location map */
+    loc = (RegLocation*)dvmCompilerNew(cUnit->numSSARegs * sizeof(*loc), true);
+    for (i=0; i< cUnit->numSSARegs; i++) {
+        loc[i] = freshLoc;
+        loc[i].sRegLow = i;
+    }
+    cUnit->regLocation = loc;
+
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+    /* Do type inference pass */
+    while (true) {
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        inferTypes(cUnit, bb);
+    }
+
+    /* Remap SSA names back to original frame locations. */
+    for (i=0; i < cUnit->numSSARegs; i++) {
+        cUnit->regLocation[i].sRegLow =
+                DECODE_REG(dvmConvertSSARegToDalvik(cUnit, loc[i].sRegLow));
+    }
+}
diff --git a/vm/compiler/SSATransformation.cpp b/vm/compiler/SSATransformation.cpp
new file mode 100644
index 0000000..7dde594
--- /dev/null
+++ b/vm/compiler/SSATransformation.cpp
@@ -0,0 +1,642 @@
+/*
+ * 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.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/DexOpcodes.h"
+
+/* Enter the node to the dfsOrder list then visit its successors */
+static void recordDFSPreOrder(CompilationUnit *cUnit, BasicBlock *block)
+{
+
+    if (block->visited || block->hidden) return;
+    block->visited = true;
+
+    /* Enqueue the block id */
+    dvmInsertGrowableList(&cUnit->dfsOrder, block->id);
+
+    if (block->fallThrough) recordDFSPreOrder(cUnit, block->fallThrough);
+    if (block->taken) recordDFSPreOrder(cUnit, block->taken);
+    if (block->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&block->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            recordDFSPreOrder(cUnit, succBB);
+        }
+    }
+    return;
+}
+
+/* Sort the blocks by the Depth-First-Search pre-order */
+static void computeDFSOrder(CompilationUnit *cUnit)
+{
+    /* Initialize or reset the DFS order list */
+    if (cUnit->dfsOrder.elemList == NULL) {
+        dvmInitGrowableList(&cUnit->dfsOrder, cUnit->numBlocks);
+    } else {
+        /* Just reset the used length on the counter */
+        cUnit->dfsOrder.numUsed = 0;
+    }
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+    recordDFSPreOrder(cUnit, cUnit->entryBlock);
+    cUnit->numReachableBlocks = cUnit->dfsOrder.numUsed;
+}
+
+/*
+ * Mark block bit on the per-Dalvik register vector to denote that Dalvik
+ * register idx is defined in BasicBlock bb.
+ */
+static bool fillDefBlockMatrix(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    if (bb->dataFlowInfo == NULL) return false;
+
+    BitVectorIterator iterator;
+
+    dvmBitVectorIteratorInit(bb->dataFlowInfo->defV, &iterator);
+    while (true) {
+        int idx = dvmBitVectorIteratorNext(&iterator);
+        if (idx == -1) break;
+        /* Block bb defines register idx */
+        dvmCompilerSetBit(cUnit->defBlockMatrix[idx], bb->id);
+    }
+    return true;
+}
+
+static void computeDefBlockMatrix(CompilationUnit *cUnit)
+{
+    int numRegisters = cUnit->numDalvikRegisters;
+    /* Allocate numDalvikRegisters bit vector pointers */
+    cUnit->defBlockMatrix = (BitVector **)
+        dvmCompilerNew(sizeof(BitVector *) * numRegisters, true);
+    int i;
+
+    /* Initialize numRegister vectors with numBlocks bits each */
+    for (i = 0; i < numRegisters; i++) {
+        cUnit->defBlockMatrix[i] = dvmCompilerAllocBitVector(cUnit->numBlocks,
+                                                             false);
+    }
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLocalLiveIn,
+                                          kAllNodes,
+                                          false /* isIterative */);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, fillDefBlockMatrix,
+                                          kAllNodes,
+                                          false /* isIterative */);
+
+    if (cUnit->jitMode == kJitMethod) {
+        /*
+         * Also set the incoming parameters as defs in the entry block.
+         * Only need to handle the parameters for the outer method.
+         */
+        int inReg = cUnit->method->registersSize - cUnit->method->insSize;
+        for (; inReg < cUnit->method->registersSize; inReg++) {
+            dvmCompilerSetBit(cUnit->defBlockMatrix[inReg],
+                              cUnit->entryBlock->id);
+        }
+    }
+}
+
+/* Compute the post-order traversal of the CFG */
+static void computeDomPostOrderTraversal(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Iterate through the dominated blocks first */
+    while (true) {
+        int bbIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (bbIdx == -1) break;
+        BasicBlock *dominatedBB =
+            (BasicBlock *) dvmGrowableListGetElement(blockList, bbIdx);
+        computeDomPostOrderTraversal(cUnit, dominatedBB);
+    }
+
+    /* Enter the current block id */
+    dvmInsertGrowableList(&cUnit->domPostOrderTraversal, bb->id);
+
+    /* hacky loop detection */
+    if (bb->taken && dvmIsBitSet(bb->dominators, bb->taken->id)) {
+        cUnit->hasLoop = true;
+    }
+}
+
+static void checkForDominanceFrontier(BasicBlock *domBB,
+                                      const BasicBlock *succBB)
+{
+    /*
+     * TODO - evaluate whether phi will ever need to be inserted into exit
+     * blocks.
+     */
+    if (succBB->iDom != domBB &&
+        succBB->blockType == kDalvikByteCode &&
+        succBB->hidden == false) {
+        dvmSetBit(domBB->domFrontier, succBB->id);
+    }
+}
+
+/* Worker function to compute the dominance frontier */
+static bool computeDominanceFrontier(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+
+    /* Calculate DF_local */
+    if (bb->taken) {
+        checkForDominanceFrontier(bb, bb->taken);
+    }
+    if (bb->fallThrough) {
+        checkForDominanceFrontier(bb, bb->fallThrough);
+    }
+    if (bb->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            checkForDominanceFrontier(bb, succBB);
+        }
+    }
+
+    /* Calculate DF_up */
+    BitVectorIterator bvIterator;
+    dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+    while (true) {
+        int dominatedIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (dominatedIdx == -1) break;
+        BasicBlock *dominatedBB = (BasicBlock *)
+            dvmGrowableListGetElement(blockList, dominatedIdx);
+        BitVectorIterator dfIterator;
+        dvmBitVectorIteratorInit(dominatedBB->domFrontier, &dfIterator);
+        while (true) {
+            int dfUpIdx = dvmBitVectorIteratorNext(&dfIterator);
+            if (dfUpIdx == -1) break;
+            BasicBlock *dfUpBlock = (BasicBlock *)
+                dvmGrowableListGetElement(blockList, dfUpIdx);
+            checkForDominanceFrontier(bb, dfUpBlock);
+        }
+    }
+
+    return true;
+}
+
+/* Worker function for initializing domination-related data structures */
+static bool initializeDominationInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    int numTotalBlocks = cUnit->blockList.numUsed;
+
+    if (bb->dominators == NULL ) {
+        bb->dominators = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+        bb->iDominated = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+        bb->domFrontier = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                   false /* expandable */);
+    } else {
+        dvmClearAllBits(bb->dominators);
+        dvmClearAllBits(bb->iDominated);
+        dvmClearAllBits(bb->domFrontier);
+    }
+    /* Set all bits in the dominator vector */
+    dvmSetInitialBits(bb->dominators, numTotalBlocks);
+
+    return true;
+}
+
+/* Worker function to compute each block's dominators */
+static bool computeBlockDominators(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    int numTotalBlocks = blockList->numUsed;
+    BitVector *tempBlockV = cUnit->tempBlockV;
+    BitVectorIterator bvIterator;
+
+    /*
+     * The dominator of the entry block has been preset to itself and we need
+     * to skip the calculation here.
+     */
+    if (bb == cUnit->entryBlock) return false;
+
+    dvmSetInitialBits(tempBlockV, numTotalBlocks);
+
+    /* Iterate through the predecessors */
+    dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+    while (true) {
+        int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+        if (predIdx == -1) break;
+        BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+                                 blockList, predIdx);
+        /* tempBlockV = tempBlockV ^ dominators */
+        dvmIntersectBitVectors(tempBlockV, tempBlockV, predBB->dominators);
+    }
+    dvmSetBit(tempBlockV, bb->id);
+    if (dvmCompareBitVectors(tempBlockV, bb->dominators)) {
+        dvmCopyBitVector(bb->dominators, tempBlockV);
+        return true;
+    }
+    return false;
+}
+
+/* Worker function to compute the idom */
+static bool computeImmediateDominator(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    GrowableList *blockList = &cUnit->blockList;
+    BitVector *tempBlockV = cUnit->tempBlockV;
+    BitVectorIterator bvIterator;
+    BasicBlock *iDom;
+
+    if (bb == cUnit->entryBlock) return false;
+
+    dvmCopyBitVector(tempBlockV, bb->dominators);
+    dvmClearBit(tempBlockV, bb->id);
+    dvmBitVectorIteratorInit(tempBlockV, &bvIterator);
+
+    /* Should not see any dead block */
+    assert(dvmCountSetBits(tempBlockV) != 0);
+    if (dvmCountSetBits(tempBlockV) == 1) {
+        iDom = (BasicBlock *) dvmGrowableListGetElement(
+                       blockList, dvmBitVectorIteratorNext(&bvIterator));
+        bb->iDom = iDom;
+    } else {
+        int iDomIdx = dvmBitVectorIteratorNext(&bvIterator);
+        assert(iDomIdx != -1);
+        while (true) {
+            int nextDom = dvmBitVectorIteratorNext(&bvIterator);
+            if (nextDom == -1) break;
+            BasicBlock *nextDomBB = (BasicBlock *)
+                dvmGrowableListGetElement(blockList, nextDom);
+            /* iDom dominates nextDom - set new iDom */
+            if (dvmIsBitSet(nextDomBB->dominators, iDomIdx)) {
+                iDomIdx = nextDom;
+            }
+
+        }
+        iDom = (BasicBlock *) dvmGrowableListGetElement(blockList, iDomIdx);
+        /* Set the immediate dominator block for bb */
+        bb->iDom = iDom;
+    }
+    /* Add bb to the iDominated set of the immediate dominator block */
+    dvmCompilerSetBit(iDom->iDominated, bb->id);
+    return true;
+}
+
+/* Compute dominators, immediate dominator, and dominance fronter */
+static void computeDominators(CompilationUnit *cUnit)
+{
+    int numReachableBlocks = cUnit->numReachableBlocks;
+    int numTotalBlocks = cUnit->blockList.numUsed;
+
+    /* Initialize domination-related data structures */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, initializeDominationInfo,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    /* Set the dominator for the root node */
+    dvmClearAllBits(cUnit->entryBlock->dominators);
+    dvmSetBit(cUnit->entryBlock->dominators, cUnit->entryBlock->id);
+
+    if (cUnit->tempBlockV == NULL) {
+        cUnit->tempBlockV = dvmCompilerAllocBitVector(numTotalBlocks,
+                                                  false /* expandable */);
+    } else {
+        dvmClearAllBits(cUnit->tempBlockV);
+    }
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockDominators,
+                                          kPreOrderDFSTraversal,
+                                          true /* isIterative */);
+
+    cUnit->entryBlock->iDom = NULL;
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeImmediateDominator,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    /*
+     * Now go ahead and compute the post order traversal based on the
+     * iDominated sets.
+     */
+    if (cUnit->domPostOrderTraversal.elemList == NULL) {
+        dvmInitGrowableList(&cUnit->domPostOrderTraversal, numReachableBlocks);
+    } else {
+        cUnit->domPostOrderTraversal.numUsed = 0;
+    }
+
+    computeDomPostOrderTraversal(cUnit, cUnit->entryBlock);
+    assert(cUnit->domPostOrderTraversal.numUsed ==
+           (unsigned) cUnit->numReachableBlocks);
+
+    /* Now compute the dominance frontier for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeDominanceFrontier,
+                                          kPostOrderDOMTraversal,
+                                          false /* isIterative */);
+}
+
+/*
+ * Perform dest U= src1 ^ ~src2
+ * This is probably not general enough to be placed in BitVector.[ch].
+ */
+static void computeSuccLiveIn(BitVector *dest,
+                              const BitVector *src1,
+                              const BitVector *src2)
+{
+    if (dest->storageSize != src1->storageSize ||
+        dest->storageSize != src2->storageSize ||
+        dest->expandable != src1->expandable ||
+        dest->expandable != src2->expandable) {
+        ALOGE("Incompatible set properties");
+        dvmAbort();
+    }
+
+    unsigned int idx;
+    for (idx = 0; idx < dest->storageSize; idx++) {
+        dest->storage[idx] |= src1->storage[idx] & ~src2->storage[idx];
+    }
+}
+
+/*
+ * Iterate through all successor blocks and propagate up the live-in sets.
+ * The calculated result is used for phi-node pruning - where we only need to
+ * insert a phi node if the variable is live-in to the block.
+ */
+static bool computeBlockLiveIns(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVector *tempDalvikRegisterV = cUnit->tempDalvikRegisterV;
+
+    if (bb->dataFlowInfo == NULL) return false;
+    dvmCopyBitVector(tempDalvikRegisterV, bb->dataFlowInfo->liveInV);
+    if (bb->taken && bb->taken->dataFlowInfo)
+        computeSuccLiveIn(tempDalvikRegisterV, bb->taken->dataFlowInfo->liveInV,
+                          bb->dataFlowInfo->defV);
+    if (bb->fallThrough && bb->fallThrough->dataFlowInfo)
+        computeSuccLiveIn(tempDalvikRegisterV,
+                          bb->fallThrough->dataFlowInfo->liveInV,
+                          bb->dataFlowInfo->defV);
+    if (bb->successorBlockList.blockListType != kNotUsed) {
+        GrowableListIterator iterator;
+        dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+                                    &iterator);
+        while (true) {
+            SuccessorBlockInfo *successorBlockInfo =
+                (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+            if (successorBlockInfo == NULL) break;
+            BasicBlock *succBB = successorBlockInfo->block;
+            if (succBB->dataFlowInfo) {
+                computeSuccLiveIn(tempDalvikRegisterV,
+                                  succBB->dataFlowInfo->liveInV,
+                                  bb->dataFlowInfo->defV);
+            }
+        }
+    }
+    if (dvmCompareBitVectors(tempDalvikRegisterV, bb->dataFlowInfo->liveInV)) {
+        dvmCopyBitVector(bb->dataFlowInfo->liveInV, tempDalvikRegisterV);
+        return true;
+    }
+    return false;
+}
+
+/* Insert phi nodes to for each variable to the dominance frontiers */
+static void insertPhiNodes(CompilationUnit *cUnit)
+{
+    int dalvikReg;
+    const GrowableList *blockList = &cUnit->blockList;
+    BitVector *phiBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+    BitVector *tmpBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+    BitVector *inputBlocks =
+        dvmCompilerAllocBitVector(cUnit->numBlocks, false);
+
+    cUnit->tempDalvikRegisterV =
+        dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockLiveIns,
+                                          kPostOrderDFSTraversal,
+                                          true /* isIterative */);
+
+    /* Iterate through each Dalvik register */
+    for (dalvikReg = 0; dalvikReg < cUnit->numDalvikRegisters; dalvikReg++) {
+        bool change;
+        BitVectorIterator iterator;
+
+        dvmCopyBitVector(inputBlocks, cUnit->defBlockMatrix[dalvikReg]);
+        dvmClearAllBits(phiBlocks);
+
+        /* Calculate the phi blocks for each Dalvik register */
+        do {
+            change = false;
+            dvmClearAllBits(tmpBlocks);
+            dvmBitVectorIteratorInit(inputBlocks, &iterator);
+
+            while (true) {
+                int idx = dvmBitVectorIteratorNext(&iterator);
+                if (idx == -1) break;
+                BasicBlock *defBB =
+                    (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+
+                /* Merge the dominance frontier to tmpBlocks */
+                dvmUnifyBitVectors(tmpBlocks, tmpBlocks, defBB->domFrontier);
+            }
+            if (dvmCompareBitVectors(phiBlocks, tmpBlocks)) {
+                change = true;
+                dvmCopyBitVector(phiBlocks, tmpBlocks);
+
+                /*
+                 * Iterate through the original blocks plus the new ones in
+                 * the dominance frontier.
+                 */
+                dvmCopyBitVector(inputBlocks, phiBlocks);
+                dvmUnifyBitVectors(inputBlocks, inputBlocks,
+                                   cUnit->defBlockMatrix[dalvikReg]);
+            }
+        } while (change);
+
+        /*
+         * Insert a phi node for dalvikReg in the phiBlocks if the Dalvik
+         * register is in the live-in set.
+         */
+        dvmBitVectorIteratorInit(phiBlocks, &iterator);
+        while (true) {
+            int idx = dvmBitVectorIteratorNext(&iterator);
+            if (idx == -1) break;
+            BasicBlock *phiBB =
+                (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+            /* Variable will be clobbered before being used - no need for phi */
+            if (!dvmIsBitSet(phiBB->dataFlowInfo->liveInV, dalvikReg)) continue;
+            MIR *phi = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+            phi->dalvikInsn.opcode = (Opcode)kMirOpPhi;
+            phi->dalvikInsn.vA = dalvikReg;
+            phi->offset = phiBB->startOffset;
+            dvmCompilerPrependMIR(phiBB, phi);
+        }
+    }
+}
+
+/*
+ * Worker function to insert phi-operands with latest SSA names from
+ * predecessor blocks
+ */
+static bool insertPhiNodeOperands(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    BitVector *ssaRegV = cUnit->tempSSARegisterV;
+    BitVectorIterator bvIterator;
+    GrowableList *blockList = &cUnit->blockList;
+    MIR *mir;
+
+    /* Phi nodes are at the beginning of each block */
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        if (mir->dalvikInsn.opcode != (Opcode)kMirOpPhi)
+            return true;
+        int ssaReg = mir->ssaRep->defs[0];
+        int encodedDalvikValue =
+            (int) dvmGrowableListGetElement(cUnit->ssaToDalvikMap, ssaReg);
+        int dalvikReg = DECODE_REG(encodedDalvikValue);
+
+        dvmClearAllBits(ssaRegV);
+
+        /* Iterate through the predecessors */
+        dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+        while (true) {
+            int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+            if (predIdx == -1) break;
+            BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+                                     blockList, predIdx);
+            int encodedSSAValue =
+                predBB->dataFlowInfo->dalvikToSSAMap[dalvikReg];
+            int ssaReg = DECODE_REG(encodedSSAValue);
+            dvmSetBit(ssaRegV, ssaReg);
+        }
+
+        /* Count the number of SSA registers for a Dalvik register */
+        int numUses = dvmCountSetBits(ssaRegV);
+        mir->ssaRep->numUses = numUses;
+        mir->ssaRep->uses =
+            (int *) dvmCompilerNew(sizeof(int) * numUses, false);
+        mir->ssaRep->fpUse =
+            (bool *) dvmCompilerNew(sizeof(bool) * numUses, true);
+
+        BitVectorIterator phiIterator;
+
+        dvmBitVectorIteratorInit(ssaRegV, &phiIterator);
+        int *usePtr = mir->ssaRep->uses;
+
+        /* Set the uses array for the phi node */
+        while (true) {
+            int ssaRegIdx = dvmBitVectorIteratorNext(&phiIterator);
+            if (ssaRegIdx == -1) break;
+            *usePtr++ = ssaRegIdx;
+        }
+    }
+
+    return true;
+}
+
+/* Perform SSA transformation for the whole method */
+void dvmCompilerMethodSSATransformation(CompilationUnit *cUnit)
+{
+    /* Compute the DFS order */
+    computeDFSOrder(cUnit);
+
+    /* Compute the dominator info */
+    computeDominators(cUnit);
+
+    /* Allocate data structures in preparation for SSA conversion */
+    dvmInitializeSSAConversion(cUnit);
+
+    /* Find out the "Dalvik reg def x block" relation */
+    computeDefBlockMatrix(cUnit);
+
+    /* Insert phi nodes to dominance frontiers for all variables */
+    insertPhiNodes(cUnit);
+
+    /* Rename register names by local defs and phi nodes */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    /*
+     * Shared temp bit vector used by each block to count the number of defs
+     * from all the predecessor blocks.
+     */
+    cUnit->tempSSARegisterV = dvmCompilerAllocBitVector(cUnit->numSSARegs,
+                                                        false);
+
+    /* Insert phi-operands with latest SSA names from predecessor blocks */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, insertPhiNodeOperands,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+}
+
+/* Build a loop. Return true if a loop structure is successfully identified. */
+bool dvmCompilerBuildLoop(CompilationUnit *cUnit)
+{
+    /* Compute the DFS order */
+    computeDFSOrder(cUnit);
+
+    /* Compute the dominator info */
+    computeDominators(cUnit);
+
+    /* Loop structure not recognized/supported - return false */
+    if (dvmCompilerFilterLoopBlocks(cUnit) == false)
+        return false;
+
+    /* Re-compute the DFS order just for the loop */
+    computeDFSOrder(cUnit);
+
+    /* Re-compute the dominator info just for the loop */
+    computeDominators(cUnit);
+
+    /* Allocate data structures in preparation for SSA conversion */
+    dvmInitializeSSAConversion(cUnit);
+
+    /* Find out the "Dalvik reg def x block" relation */
+    computeDefBlockMatrix(cUnit);
+
+    /* Insert phi nodes to dominance frontiers for all variables */
+    insertPhiNodes(cUnit);
+
+    /* Rename register names by local defs and phi nodes */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    /*
+     * Shared temp bit vector used by each block to count the number of defs
+     * from all the predecessor blocks.
+     */
+    cUnit->tempSSARegisterV = dvmCompilerAllocBitVector(cUnit->numSSARegs,
+                                                        false);
+
+    /* Insert phi-operands with latest SSA names from predecessor blocks */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, insertPhiNodeOperands,
+                                          kReachableNodes,
+                                          false /* isIterative */);
+
+    if (gDvmJit.receivedSIGUSR2 || gDvmJit.printMe) {
+        dvmDumpCFG(cUnit, "/sdcard/cfg/");
+    }
+
+    return true;
+}
diff --git a/vm/compiler/Utility.cpp b/vm/compiler/Utility.cpp
new file mode 100644
index 0000000..eaa562b
--- /dev/null
+++ b/vm/compiler/Utility.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+static ArenaMemBlock *arenaHead, *currentArena;
+static int numArenaBlocks;
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void)
+{
+    assert(arenaHead == NULL);
+    arenaHead =
+        (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE);
+    if (arenaHead == NULL) {
+        ALOGE("No memory left to create compiler heap memory");
+        return false;
+    }
+    arenaHead->blockSize = ARENA_DEFAULT_SIZE;
+    currentArena = arenaHead;
+    currentArena->bytesAllocated = 0;
+    currentArena->next = NULL;
+    numArenaBlocks = 1;
+
+    return true;
+}
+
+/* Arena-based malloc for compilation tasks */
+void * dvmCompilerNew(size_t size, bool zero)
+{
+    size = (size + 3) & ~3;
+retry:
+    /* Normal case - space is available in the current page */
+    if (size + currentArena->bytesAllocated <= currentArena->blockSize) {
+        void *ptr;
+        ptr = &currentArena->ptr[currentArena->bytesAllocated];
+        currentArena->bytesAllocated += size;
+        if (zero) {
+            memset(ptr, 0, size);
+        }
+        return ptr;
+    } else {
+        /*
+         * See if there are previously allocated arena blocks before the last
+         * reset
+         */
+        if (currentArena->next) {
+            currentArena = currentArena->next;
+            goto retry;
+        }
+
+        size_t blockSize = (size < ARENA_DEFAULT_SIZE) ?
+                          ARENA_DEFAULT_SIZE : size;
+        /* Time to allocate a new arena */
+        ArenaMemBlock *newArena = (ArenaMemBlock *)
+            malloc(sizeof(ArenaMemBlock) + blockSize);
+        if (newArena == NULL) {
+            ALOGE("Arena allocation failure");
+            dvmAbort();
+        }
+        newArena->blockSize = blockSize;
+        newArena->bytesAllocated = 0;
+        newArena->next = NULL;
+        currentArena->next = newArena;
+        currentArena = newArena;
+        numArenaBlocks++;
+        if (numArenaBlocks > 10)
+            ALOGI("Total arena pages for JIT: %d", numArenaBlocks);
+        goto retry;
+    }
+    /* Should not reach here */
+    dvmAbort();
+}
+
+/* Reclaim all the arena blocks allocated so far */
+void dvmCompilerArenaReset(void)
+{
+    ArenaMemBlock *block;
+
+    for (block = arenaHead; block; block = block->next) {
+        block->bytesAllocated = 0;
+    }
+    currentArena = arenaHead;
+}
+
+/* Growable List initialization */
+void dvmInitGrowableList(GrowableList *gList, size_t initLength)
+{
+    gList->numAllocated = initLength;
+    gList->numUsed = 0;
+    gList->elemList = (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * initLength,
+                                                  true);
+}
+
+/* Expand the capacity of a growable list */
+static void expandGrowableList(GrowableList *gList)
+{
+    int newLength = gList->numAllocated;
+    if (newLength < 128) {
+        newLength <<= 1;
+    } else {
+        newLength += 128;
+    }
+    intptr_t *newArray =
+        (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * newLength, true);
+    memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated);
+    gList->numAllocated = newLength;
+    gList->elemList = newArray;
+}
+
+/* Insert a new element into the growable list */
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem)
+{
+    assert(gList->numAllocated != 0);
+    if (gList->numUsed == gList->numAllocated) {
+        expandGrowableList(gList);
+    }
+    gList->elemList[gList->numUsed++] = elem;
+}
+
+void dvmGrowableListIteratorInit(GrowableList *gList,
+                                 GrowableListIterator *iterator)
+{
+    iterator->list = gList;
+    iterator->idx = 0;
+    iterator->size = gList->numUsed;
+}
+
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator)
+{
+    assert(iterator->size == iterator->list->numUsed);
+    if (iterator->idx == iterator->size) return 0;
+    return iterator->list->elemList[iterator->idx++];
+}
+
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx)
+{
+    assert(idx < gList->numUsed);
+    return gList->elemList[idx];
+}
+
+/* Debug Utility - dump a compilation unit */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
+{
+    BasicBlock *bb;
+    const char *blockTypeNames[] = {
+        "Normal Chaining Cell",
+        "Hot Chaining Cell",
+        "Singleton Chaining Cell",
+        "Predicted Chaining Cell",
+        "Backward Branch",
+        "Chaining Cell Gap",
+        "N/A",
+        "Entry Block",
+        "Code Block",
+        "Exit Block",
+        "PC Reconstruction",
+        "Exception Handling",
+    };
+
+    ALOGD("Compiling %s %s", cUnit->method->clazz->descriptor,
+         cUnit->method->name);
+    ALOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
+    ALOGD("%d blocks in total", cUnit->numBlocks);
+    GrowableListIterator iterator;
+
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    while (true) {
+        bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        ALOGD("Block %d (%s) (insn %04x - %04x%s)",
+             bb->id,
+             blockTypeNames[bb->blockType],
+             bb->startOffset,
+             bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset,
+             bb->lastMIRInsn ? "" : " empty");
+        if (bb->taken) {
+            ALOGD("  Taken branch: block %d (%04x)",
+                 bb->taken->id, bb->taken->startOffset);
+        }
+        if (bb->fallThrough) {
+            ALOGD("  Fallthrough : block %d (%04x)",
+                 bb->fallThrough->id, bb->fallThrough->startOffset);
+        }
+    }
+}
+
+/*
+ * dvmHashForeach callback.
+ */
+static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats)
+{
+    CompilerMethodStats *methodStats =
+        (CompilerMethodStats *) compilerMethodStats;
+    CompilerMethodStats *totalStats =
+        (CompilerMethodStats *) totalMethodStats;
+
+    totalStats->dalvikSize += methodStats->dalvikSize;
+    totalStats->compiledDalvikSize += methodStats->compiledDalvikSize;
+    totalStats->nativeSize += methodStats->nativeSize;
+
+    /* Enable the following when fine-tuning the JIT performance */
+#if 0
+    int limit = (methodStats->dalvikSize >> 2) * 3;
+
+    /* If over 3/4 of the Dalvik code is compiled, print something */
+    if (methodStats->compiledDalvikSize >= limit) {
+        ALOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)",
+             methodStats->method->clazz->descriptor,
+             methodStats->method->name,
+             methodStats->compiledDalvikSize,
+             methodStats->dalvikSize,
+             methodStats->nativeSize);
+    }
+#endif
+    return 0;
+}
+
+/*
+ * Dump the current stats of the compiler, including number of bytes used in
+ * the code cache, arena size, and work queue length, and various JIT stats.
+ */
+void dvmCompilerDumpStats(void)
+{
+    CompilerMethodStats totalMethodStats;
+
+    memset(&totalMethodStats, 0, sizeof(CompilerMethodStats));
+    ALOGD("%d compilations using %d + %d bytes",
+         gDvmJit.numCompilations,
+         gDvmJit.templateSize,
+         gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+    ALOGD("Compiler arena uses %d blocks (%d bytes each)",
+         numArenaBlocks, ARENA_DEFAULT_SIZE);
+    ALOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
+         gDvmJit.compilerMaxQueued);
+    dvmJitStats();
+    dvmCompilerArchDump();
+    if (gDvmJit.methodStatsTable) {
+        dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
+                       &totalMethodStats);
+        ALOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)",
+             totalMethodStats.compiledDalvikSize,
+             totalMethodStats.dalvikSize,
+             totalMethodStats.nativeSize);
+    }
+}
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ *
+ * NOTE: this is the sister implementation of dvmAllocBitVector. In this version
+ * memory is allocated from the compiler arena.
+ */
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable)
+{
+    BitVector* bv;
+    unsigned int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+
+    bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false);
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->expandable = expandable;
+    bv->storage = (u4*) dvmCompilerNew(count * sizeof(u4), true);
+    return bv;
+}
+
+/*
+ * Mark the specified bit as "set".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmSetBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerSetBit(BitVector *pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        if (!pBits->expandable)
+            dvmAbort();
+
+        /* Round up to word boundaries for "num+1" bits */
+        unsigned int newSize = (num + 1 + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        u4 *newStorage = (u4*)dvmCompilerNew(newSize * sizeof(u4), false);
+        memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
+        memset(&newStorage[pBits->storageSize], 0,
+               (newSize - pBits->storageSize) * sizeof(u4));
+        pBits->storage = newStorage;
+        pBits->storageSize = newSize;
+    }
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+    return true;
+}
+
+/*
+ * Mark the specified bit as "unset".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmClearBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerClearBit(BitVector *pBits, unsigned int num)
+{
+    if (num >= pBits->storageSize * sizeof(u4) * 8) {
+        ALOGE("Trying to clear a bit that is not set in the vector yet!");
+        dvmAbort();
+    }
+
+    pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+    return true;
+}
+
+/*
+ * If set is true, mark all bits as 1. Otherwise mark all bits as 0.
+ */
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set)
+{
+    int value = set ? -1 : 0;
+    memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4));
+}
+
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
+{
+    int i;
+
+    ALOGE("%s", msg);
+    for (i = 0; i < length; i++) {
+        if (dvmIsBitSet(bv, i)) {
+            ALOGE("    Bit %d is set", i);
+        }
+    }
+}
+
+void dvmCompilerAbort(CompilationUnit *cUnit)
+{
+    ALOGE("Jit: aborting trace compilation, reverting to interpreter");
+    /* Force a traceback in debug builds */
+    assert(0);
+    /*
+     * Abort translation and force to interpret-only for this trace
+     * Matching setjmp in compiler thread work loop in Compiler.c.
+     */
+    longjmp(*cUnit->bailPtr, 1);
+}
+
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+                           const BitVector *bv, int length)
+{
+    int i;
+
+    ALOGE("%s", msg);
+    for (i = 0; i < length; i++) {
+        if (dvmIsBitSet(bv, i)) {
+            BasicBlock *bb =
+                (BasicBlock *) dvmGrowableListGetElement(blocks, i);
+            char blockName[BLOCK_NAME_LEN];
+            dvmGetBlockName(bb, blockName);
+            ALOGE("Bit %d / %s is set", i, blockName);
+        }
+    }
+}
+
+void dvmGetBlockName(BasicBlock *bb, char *name)
+{
+    switch (bb->blockType) {
+        case kEntryBlock:
+            snprintf(name, BLOCK_NAME_LEN, "entry");
+            break;
+        case kExitBlock:
+            snprintf(name, BLOCK_NAME_LEN, "exit");
+            break;
+        case kDalvikByteCode:
+            snprintf(name, BLOCK_NAME_LEN, "block%04x", bb->startOffset);
+            break;
+        case kChainingCellNormal:
+            snprintf(name, BLOCK_NAME_LEN, "chain%04x", bb->startOffset);
+            break;
+        case kExceptionHandling:
+            snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset);
+            break;
+        default:
+            snprintf(name, BLOCK_NAME_LEN, "??");
+            break;
+    }
+}
+
+void dvmCompilerCacheFlush(uintptr_t start, uintptr_t end) {
+    __builtin___clear_cache(reinterpret_cast<void*>(start), reinterpret_cast<void*>(end));
+}
diff --git a/vm/compiler/codegen/CodegenFactory.cpp b/vm/compiler/codegen/CodegenFactory.cpp
new file mode 100644
index 0000000..f42ae74
--- /dev/null
+++ b/vm/compiler/codegen/CodegenFactory.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains target-independent codegen and support, and is
+ * included by:
+ *
+ *        $(TARGET_ARCH)/Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directories below this one.
+ *
+ * Prior to including this file, TGT_LIR should be #defined.
+ * For example, for arm:
+ *    #define TGT_LIR ArmLIR
+ * and for x86:
+ *    #define TGT_LIR X86LIR
+ */
+
+
+/* Load a word at base + displacement.  Displacement must be word multiple */
+static TGT_LIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rDest)
+{
+    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+                        INVALID_SREG);
+}
+
+static TGT_LIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc)
+{
+    return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register.  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                            int reg1)
+{
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopy(cUnit, reg1, rlSrc.lowReg);
+    } else  if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+        loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                     reg1);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * register.  Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 int reg1)
+{
+    dvmCompilerClobber(cUnit, reg1);
+    dvmCompilerMarkInUse(cUnit, reg1);
+    loadValueDirect(cUnit, rlSrc, reg1);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s].  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int regLo, int regHi)
+{
+    rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF,
+                         offsetof(Thread, interpSave.retval),
+                         regLo, regHi, INVALID_SREG);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+            loadBaseDispWide(cUnit, NULL, rFP,
+                             dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                             regLo, regHi, INVALID_SREG);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * registers.  Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                     int regLo, int regHi)
+{
+    dvmCompilerClobber(cUnit, regLo);
+    dvmCompilerClobber(cUnit, regHi);
+    dvmCompilerMarkInUse(cUnit, regLo);
+    dvmCompilerMarkInUse(cUnit, regHi);
+    loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
+}
+
+static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
+                             RegisterClass opKind)
+{
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+    } else if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                     rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+    }
+    return rlSrc;
+}
+
+static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(!rlDest.wide);
+    assert(!rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                      rlDest.lowReg, kWord);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    } else {
+        dvmCompilerResetDefLoc(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+
+static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 RegisterClass opKind)
+{
+    assert(rlSrc.wide);
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+        dvmCompilerMarkLive(cUnit, rlSrc.highReg,
+                            dvmCompilerSRegHi(rlSrc.sRegLow));
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF,
+                         offsetof(Thread, interpSave.retval),
+                         rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc.highReg);
+    }
+    return rlSrc;
+}
+
+static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
+                           RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
+    assert(rlDest.wide);
+    assert(rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
+                           rlSrc.lowReg, rlSrc.highReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            rlDest.highReg = rlSrc.highReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+            dvmCompilerClobber(cUnit, rlSrc.highReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
+                            rlDest.highReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkLive(cUnit, rlDest.highReg,
+                        dvmCompilerSRegHi(rlDest.sRegLow));
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+    dvmCompilerMarkDirty(cUnit, rlDest.highReg);
+    dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                          rlDest.lowReg, rlDest.highReg);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        dvmCompilerResetDefLocWide(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
+            dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            assert((vReg+1) == dvmCompilerS2VReg(cUnit,
+                                     dvmCompilerSRegHi(rlDest.sRegLow)));
+            storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
+                              rlDest.highReg);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            dvmCompilerMarkClean(cUnit, rlDest.highReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h
new file mode 100644
index 0000000..f2b36a3
--- /dev/null
+++ b/vm/compiler/codegen/CompilerCodegen.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILERCODEGEN_H_
+#define DALVIK_VM_COMPILERCODEGEN_H_
+
+#include "compiler/CompilerIR.h"
+
+/* Maximal number of switch cases to have inline chains */
+#define MAX_CHAINED_SWITCH_CASES 64
+
+/* Work unit is architecture dependent */
+bool dvmCompilerDoWork(CompilerWorkOrder *work);
+
+/* Lower middle-level IR to low-level IR */
+#ifndef ARCH_IA32
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit);
+#else /* ARCH_IA32 */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit, JitTranslationInfo* info);
+#endif
+
+/* Lower middle-level IR to low-level IR for the whole method */
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit);
+
+/* Assemble LIR into machine code */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info);
+
+/* Perform translation chain operation. */
+extern "C" void* dvmJitChain(void* tgtAddr, u4* branchAddr);
+
+/* Install class objects in the literal pool */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit,
+                                      char *codeAddress);
+
+/* Patch inline cache content for polymorphic callsites */
+bool dvmJitPatchInlineCache(void *cellPtr, void *contentPtr);
+
+/* Implemented in the codegen/<target>/ArchUtility.c */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit);
+
+/* Implemented in the codegen/<target>/Assembler.c */
+void dvmCompilerPatchInlineCache(void);
+
+/* Implemented in codegen/<target>/Ralloc.c */
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/Thumb<version>Util.c */
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+JitInstructionSetType dvmCompilerInstructionSet(void);
+
+/*
+ * Implemented in codegen/<target>/<target_variant>/ArchVariant.c
+ * Architecture-specific initializations and checks
+ */
+bool dvmCompilerArchVariantInit(void);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+int dvmCompilerTargetOptHint(int key);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind);
+
+#endif  // DALVIK_VM_COMPILERCODEGEN_H_
diff --git a/vm/compiler/codegen/Optimizer.h b/vm/compiler/codegen/Optimizer.h
new file mode 100644
index 0000000..43d98ed
--- /dev/null
+++ b/vm/compiler/codegen/Optimizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_OPTIMIZATION_H_
+#define DALVIK_VM_COMPILER_OPTIMIZATION_H_
+
+#include "Dalvik.h"
+
+/*
+ * If the corresponding bit is set in gDvmJit.disableOpt, the selected
+ * optimization will be suppressed.
+ */
+enum optControlVector {
+    kLoadStoreElimination = 0,
+    kLoadHoisting,
+    kTrackLiveTemps,
+    kSuppressLoads,
+    kMethodInlining,
+    kMethodJit,
+};
+
+/* Forward declarations */
+struct CompilationUnit;
+struct LIR;
+
+void dvmCompilerApplyLocalOptimizations(struct CompilationUnit *cUnit,
+                                        struct LIR *head,
+                                        struct LIR *tail);
+
+void dvmCompilerApplyGlobalOptimizations(struct CompilationUnit *cUnit);
+
+#endif  // DALVIK_VM_COMPILER_OPTIMIZATION_H_
diff --git a/vm/compiler/codegen/Ralloc.h b/vm/compiler/codegen/Ralloc.h
new file mode 100644
index 0000000..2296fbc
--- /dev/null
+++ b/vm/compiler/codegen/Ralloc.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains target independent register alloction support.
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+
+/*
+ * Return most flexible allowed register class based on size.
+ * Bug: 2813841
+ * Must use a core register for data types narrower than word (due
+ * to possible unaligned load/store.
+ */
+static inline RegisterClass dvmCompilerRegClassBySize(OpSize size)
+{
+    return (size == kUnsignedHalf ||
+            size == kSignedHalf ||
+            size == kUnsignedByte ||
+            size == kSignedByte ) ? kCoreReg : kAnyReg;
+}
+
+static inline int dvmCompilerS2VReg(CompilationUnit *cUnit, int sReg)
+{
+    assert(sReg != INVALID_SREG);
+    return DECODE_REG(dvmConvertSSARegToDalvik(cUnit, sReg));
+}
+
+/* Reset the tracker to unknown state */
+static inline void dvmCompilerResetNullCheck(CompilationUnit *cUnit)
+{
+    dvmClearAllBits(cUnit->regPool->nullCheckedRegs);
+}
+
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+static inline int dvmCompilerSRegHi(int lowSreg) {
+    return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+
+static inline bool dvmCompilerLiveOut(CompilationUnit *cUnit, int sReg)
+{
+    //TODO: fully implement
+    return true;
+}
+
+static inline int dvmCompilerSSASrc(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numUses > num);
+    return mir->ssaRep->uses[num];
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update);
+/* Mark a temp register as dead.  Does not affect allocation state. */
+extern void dvmCompilerClobber(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit,
+                                        RegLocation loc);
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc);
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg);
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg,
+                                int highReg);
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl);
+
+/* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num);
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                               LIR *start, LIR *finish);
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                                   LIR *start, LIR *finish);
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high);
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high);
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num);
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num);
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit);
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit);
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg);
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit);
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit);
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit);
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc);
+
+//FIXME - this needs to also check the preserved pool.
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg);
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit);
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg);
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit);
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl);
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit);
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushRegWide(CompilationUnit *cUnit, int reg1, int reg2);
+
+extern void dvmCompilerFlushReg(CompilationUnit *cUnit, int reg);
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * ${TARGET_ARCH}/${TARGET_ARCH_VARIANT}/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass);
+
+extern ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+                                   int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                                    int displacement, int rSrc, OpSize size);
+
+extern void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                        int displacement, int rSrcLo,
+                                        int rSrcHi);
diff --git a/vm/compiler/codegen/RallocUtil.cpp b/vm/compiler/codegen/RallocUtil.cpp
new file mode 100644
index 0000000..f4a46f0
--- /dev/null
+++ b/vm/compiler/codegen/RallocUtil.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "Ralloc.h"
+
+#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i < cUnit->regPool->numCoreTemps; i++) {
+        cUnit->regPool->coreTemps[i].inUse = false;
+    }
+    for (i=0; i < cUnit->regPool->numFPTemps; i++) {
+        cUnit->regPool->FPTemps[i].inUse = false;
+    }
+}
+
+ /* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num)
+{
+    int i;
+    for (i=0; i < num; i++) {
+        regs[i].reg = regNums[i];
+        regs[i].inUse = false;
+        regs[i].pair = false;
+        regs[i].live = false;
+        regs[i].dirty = false;
+        regs[i].sReg = INVALID_SREG;
+    }
+}
+
+static void dumpRegPool(RegisterInfo *p, int numRegs)
+{
+    int i;
+    ALOGE("================================================");
+    for (i=0; i < numRegs; i++ ){
+        ALOGE("R[%d]: U:%d, P:%d, part:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+           p[i].reg, p[i].inUse, p[i].pair, p[i].partner, p[i].live,
+           p[i].dirty, p[i].sReg,(int)p[i].defStart, (int)p[i].defEnd);
+    }
+    ALOGE("================================================");
+}
+
+static RegisterInfo *getRegInfo(CompilationUnit *cUnit, int reg)
+{
+    int numTemps = cUnit->regPool->numCoreTemps;
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    ALOGE("Tried to get info on a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+    return NULL;
+}
+
+void dvmCompilerFlushRegWide(CompilationUnit *cUnit, int reg1, int reg2)
+{
+    RegisterInfo *info1 = getRegInfo(cUnit, reg1);
+    RegisterInfo *info2 = getRegInfo(cUnit, reg2);
+    assert(info1 && info2 && info1->pair && info2->pair &&
+           (info1->partner == info2->reg) &&
+           (info2->partner == info1->reg));
+    if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+        info1->dirty = false;
+        info2->dirty = false;
+        if (dvmCompilerS2VReg(cUnit, info2->sReg) <
+            dvmCompilerS2VReg(cUnit, info1->sReg))
+            info1 = info2;
+        dvmCompilerFlushRegWideImpl(cUnit, rFP,
+                                    dvmCompilerS2VReg(cUnit, info1->sReg) << 2,
+                                    info1->reg, info1->partner);
+    }
+}
+
+void dvmCompilerFlushReg(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if (info->live && info->dirty) {
+        info->dirty = false;
+        dvmCompilerFlushRegImpl(cUnit, rFP,
+                                dvmCompilerS2VReg(cUnit, info->sReg) << 2,
+                                reg, kWord);
+    }
+}
+
+/* return true if found reg to clobber */
+static bool clobberRegBody(CompilationUnit *cUnit, RegisterInfo *p,
+                           int numTemps, int reg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            if (p[i].live && p[i].dirty) {
+                if (p[i].pair) {
+                    dvmCompilerFlushRegWide(cUnit, p[i].reg, p[i].partner);
+                } else {
+                    dvmCompilerFlushReg(cUnit, p[i].reg);
+                }
+            }
+            p[i].live = false;
+            p[i].sReg = INVALID_SREG;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+            if (p[i].pair) {
+                p[i].pair = false;
+                /* partners should be in same pool */
+                clobberRegBody(cUnit, p, numTemps, p[i].partner);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Mark a temp register as dead.  Does not affect allocation state. */
+void dvmCompilerClobber(CompilationUnit *cUnit, int reg)
+{
+    if (!clobberRegBody(cUnit, cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps, reg)) {
+        clobberRegBody(cUnit, cUnit->regPool->FPTemps,
+                       cUnit->regPool->numFPTemps, reg);
+    }
+}
+
+static void clobberSRegBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].sReg == sReg) {
+            p[i].live = false;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+        }
+    }
+}
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg)
+{
+    clobberSRegBody(cUnit->regPool->coreTemps, cUnit->regPool->numCoreTemps,
+                    sReg);
+    clobberSRegBody(cUnit->regPool->FPTemps, cUnit->regPool->numFPTemps,
+                    sReg);
+}
+
+static int allocTempBody(CompilationUnit *cUnit, RegisterInfo *p, int numTemps,
+                         int *nextTemp, bool required)
+{
+    int i;
+    int next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next].live) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    if (required) {
+        ALOGE("No free temp registers");
+        dvmCompilerAbort(cUnit);
+    }
+    return -1;  // No register available
+}
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit)
+{
+    RegisterInfo *p = cUnit->regPool->FPTemps;
+    int numTemps = cUnit->regPool->numFPTemps;
+    int next = cUnit->regPool->nextFPTemp;
+    int i;
+
+    for (i=0; i < numTemps; i+=2) {
+        /* Cleanup - not all targets need aligned regs */
+        if (next & 1)
+            next++;
+        if (next >= numTemps)
+            next = 0;
+        if ((!p[next].inUse && !p[next].live) &&
+            (!p[next+1].inUse && !p[next+1].live)) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    next = cUnit->regPool->nextFPTemp;
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next+1].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    ALOGE("No free temp registers");
+    dvmCompilerAbort(cUnit);
+    return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->FPTemps,
+                         cUnit->regPool->numFPTemps,
+                         &cUnit->regPool->nextFPTemp, true);
+}
+
+static RegisterInfo *allocLiveBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    if (sReg == -1)
+        return NULL;
+    for (i=0; i < numTemps; i++) {
+        if (p[i].live && (p[i].sReg == sReg)) {
+            p[i].inUse = true;
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+static RegisterInfo *allocLive(CompilationUnit *cUnit, int sReg,
+                               int regClass)
+{
+    RegisterInfo *res = NULL;
+    switch(regClass) {
+        case kAnyReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            if (res)
+                break;
+            /* Intentional fallthrough */
+        case kCoreReg:
+            res = allocLiveBody(cUnit->regPool->coreTemps,
+                                cUnit->regPool->numCoreTemps, sReg);
+            break;
+        case kFPReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            break;
+        default:
+            ALOGE("Invalid register type");
+            dvmCompilerAbort(cUnit);
+    }
+    return res;
+}
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    ALOGE("Tried to free a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    return NULL;
+}
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    ALOGE("Tried to lock a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = getRegInfo(cUnit, reg);
+    p->defStart = NULL;
+    p->defEnd = NULL;
+}
+
+static void nullifyRange(CompilationUnit *cUnit, LIR *start, LIR *finish,
+                         int sReg1, int sReg2)
+{
+    if (start && finish) {
+        LIR *p;
+        assert(sReg1 == sReg2);
+        for (p = start; ;p = p->next) {
+            ((ArmLIR *)p)->flags.isNop = true;
+            if (p == finish)
+                break;
+        }
+    }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                    LIR *start, LIR *finish)
+{
+    assert(!rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                        LIR *start, LIR *finish)
+{
+    assert(rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);  // Only track low of pair
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl)
+{
+    assert(rl.wide);
+    if (rl.location == kLocPhysReg) {
+        RegisterInfo *infoLo = getRegInfo(cUnit, rl.lowReg);
+        RegisterInfo *infoHi = getRegInfo(cUnit, rl.highReg);
+        if (!infoLo->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoLo->pair);
+        }
+        if (!infoHi->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoHi->pair);
+        }
+        assert(infoLo->pair);
+        assert(infoHi->pair);
+        assert(infoLo->partner == infoHi->reg);
+        assert(infoHi->partner == infoLo->reg);
+        infoLo->pair = false;
+        infoHi->pair = false;
+        infoLo->defStart = NULL;
+        infoLo->defEnd = NULL;
+        infoHi->defStart = NULL;
+        infoHi->defEnd = NULL;
+    }
+    rl.wide = false;
+    return rl;
+}
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(!rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(!p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+}
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);
+}
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerLockTemp(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+}
+
+// Make sure nothing is live and dirty
+static void flushAllRegsBody(CompilationUnit *cUnit, RegisterInfo *info,
+                             int numRegs)
+{
+    int i;
+    for (i=0; i < numRegs; i++) {
+        if (info[i].live && info[i].dirty) {
+            if (info[i].pair) {
+                dvmCompilerFlushRegWide(cUnit, info[i].reg, info[i].partner);
+            } else {
+                dvmCompilerFlushReg(cUnit, info[i].reg);
+            }
+        }
+    }
+}
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit)
+{
+    flushAllRegsBody(cUnit, cUnit->regPool->coreTemps,
+                     cUnit->regPool->numCoreTemps);
+    flushAllRegsBody(cUnit, cUnit->regPool->FPTemps,
+                     cUnit->regPool->numFPTemps);
+    dvmCompilerClobberAllRegs(cUnit);
+}
+
+
+//TUNING: rewrite all of this reg stuff.  Probably use an attribute table
+static bool regClassMatches(int regClass, int reg)
+{
+    if (regClass == kAnyReg) {
+        return true;
+    } else if (regClass == kCoreReg) {
+        return !FPREG(reg);
+    } else {
+        return FPREG(reg);
+    }
+}
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
+        return;  /* already live */
+    } else if (sReg != INVALID_SREG) {
+        dvmCompilerClobberSReg(cUnit, sReg);
+        info->live = true;
+    } else {
+        /* Can't be live if no associated sReg */
+        info->live = false;
+    }
+    info->sReg = sReg;
+}
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg, int highReg)
+{
+    RegisterInfo *infoLo = getRegInfo(cUnit, lowReg);
+    RegisterInfo *infoHi = getRegInfo(cUnit, highReg);
+    infoLo->pair = infoHi->pair = true;
+    infoLo->partner = highReg;
+    infoHi->partner = lowReg;
+}
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = false;
+}
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = true;
+}
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg)
+{
+      RegisterInfo *info = getRegInfo(cUnit, reg);
+          info->inUse = true;
+}
+
+static void copyRegInfo(CompilationUnit *cUnit, int newReg, int oldReg)
+{
+    RegisterInfo *newInfo = getRegInfo(cUnit, newReg);
+    RegisterInfo *oldInfo = getRegInfo(cUnit, oldReg);
+    *newInfo = *oldInfo;
+    newInfo->reg = newReg;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact.  No code
+ * is generated.  The the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs.  Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit, RegLocation loc)
+{
+    assert(!loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        if (infoLo) {
+            if (infoLo->pair) {
+                dvmCompilerClobber(cUnit, infoLo->reg);
+                dvmCompilerClobber(cUnit, infoLo->partner);
+            } else {
+                loc.lowReg = infoLo->reg;
+                loc.location = kLocPhysReg;
+            }
+        }
+    }
+
+    return loc;
+}
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc)
+{
+    assert(loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        // Are the dalvik regs already live in physical registers?
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        RegisterInfo *infoHi = allocLive(cUnit,
+              dvmCompilerSRegHi(loc.sRegLow), kAnyReg);
+        bool match = true;
+        match = match && (infoLo != NULL);
+        match = match && (infoHi != NULL);
+        // Are they both core or both FP?
+        match = match && (FPREG(infoLo->reg) == FPREG(infoHi->reg));
+        // If a pair of floating point singles, are they properly aligned?
+        if (match && FPREG(infoLo->reg)) {
+            match &= ((infoLo->reg & 0x1) == 0);
+            match &= ((infoHi->reg - infoLo->reg) == 1);
+        }
+        // If previously used as a pair, it is the same pair?
+        if (match && (infoLo->pair || infoHi->pair)) {
+            match = (infoLo->pair == infoHi->pair);
+            match &= ((infoLo->reg == infoHi->partner) &&
+                      (infoHi->reg == infoLo->partner));
+        }
+        if (match) {
+            // Can reuse - update the register usage info
+            loc.lowReg = infoLo->reg;
+            loc.highReg = infoHi->reg;
+            loc.location = kLocPhysReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+            return loc;
+        }
+        // Can't easily reuse - clobber any overlaps
+        if (infoLo) {
+            dvmCompilerClobber(cUnit, infoLo->reg);
+            if (infoLo->pair)
+                dvmCompilerClobber(cUnit, infoLo->partner);
+        }
+        if (infoHi) {
+            dvmCompilerClobber(cUnit, infoHi->reg);
+            if (infoHi->pair)
+                dvmCompilerClobber(cUnit, infoHi->partner);
+        }
+    }
+
+    return loc;
+}
+
+static RegLocation evalLocWide(CompilationUnit *cUnit, RegLocation loc,
+                               int regClass, bool update)
+{
+    assert(loc.wide);
+    int newRegs;
+    int lowReg;
+    int highReg;
+
+    loc = dvmCompilerUpdateLocWide(cUnit, loc);
+
+    /* If already in registers, we can assume proper form.  Right reg class? */
+    if (loc.location == kLocPhysReg) {
+        assert(FPREG(loc.lowReg) == FPREG(loc.highReg));
+        assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Reallocate and copy */
+            newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+            lowReg = newRegs & 0xff;
+            highReg = (newRegs >> 8) & 0xff;
+            dvmCompilerRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
+                                   loc.highReg);
+            copyRegInfo(cUnit, lowReg, loc.lowReg);
+            copyRegInfo(cUnit, highReg, loc.highReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.highReg);
+            loc.lowReg = lowReg;
+            loc.highReg = highReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+    assert((loc.location != kLocRetval) ||
+           (dvmCompilerSRegHi(loc.sRegLow) == INVALID_SREG));
+
+    newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+    loc.lowReg = newRegs & 0xff;
+    loc.highReg = (newRegs >> 8) & 0xff;
+
+    dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+        dvmCompilerMarkLive(cUnit, loc.highReg, dvmCompilerSRegHi(loc.sRegLow));
+    }
+    assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+    return loc;
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update)
+{
+    int newReg;
+    if (loc.wide)
+        return evalLocWide(cUnit, loc, regClass, update);
+    loc = dvmCompilerUpdateLoc(cUnit, loc);
+
+    if (loc.location == kLocPhysReg) {
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Realloc, copy and transfer ownership */
+            newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+            dvmCompilerRegCopy(cUnit, newReg, loc.lowReg);
+            copyRegInfo(cUnit, newReg, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            loc.lowReg = newReg;
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+
+    newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+    loc.lowReg = newReg;
+
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+    }
+    return loc;
+}
+
+static inline int getDestSSAName(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numDefs > num);
+    return mir->ssaRep->defs[num];
+}
+
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num)
+{
+    RegLocation loc = cUnit->regLocation[
+         SREG(cUnit, dvmCompilerSSASrc(mir, num))];
+    loc.fp = cUnit->regLocation[dvmCompilerSSASrc(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num)
+{
+    RegLocation loc = cUnit->regLocation[SREG(cUnit, getDestSSAName(mir, num))];
+    loc.fp = cUnit->regLocation[getDestSSAName(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+static RegLocation getLocWide(CompilationUnit *cUnit, MIR *mir,
+                              int low, int high, bool isSrc)
+{
+    RegLocation lowLoc;
+    RegLocation highLoc;
+    /* Copy loc record for low word and patch in data from high word */
+    if (isSrc) {
+        lowLoc = dvmCompilerGetSrc(cUnit, mir, low);
+        highLoc = dvmCompilerGetSrc(cUnit, mir, high);
+    } else {
+        lowLoc = dvmCompilerGetDest(cUnit, mir, low);
+        highLoc = dvmCompilerGetDest(cUnit, mir, high);
+    }
+    /* Avoid this case by either promoting both or neither. */
+    assert(lowLoc.location == highLoc.location);
+    if (lowLoc.location == kLocPhysReg) {
+        /* This case shouldn't happen if we've named correctly */
+        assert(lowLoc.fp == highLoc.fp);
+    }
+    lowLoc.wide = true;
+    lowLoc.highReg = highLoc.lowReg;
+    return lowLoc;
+}
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, false);
+}
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, true);
+}
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc)
+{
+    if (loc.location != kLocRetval) {
+        assert(loc.sRegLow != INVALID_SREG);
+        dvmClearBit(cUnit->regPool->nullCheckedRegs, loc.sRegLow);
+        if (loc.wide) {
+            assert(dvmCompilerSRegHi(loc.sRegLow) != INVALID_SREG);
+            dvmClearBit(cUnit->regPool->nullCheckedRegs,
+                        dvmCompilerSRegHi(loc.sRegLow));
+        }
+    }
+}
diff --git a/vm/compiler/codegen/arm/ArchFactory.cpp b/vm/compiler/codegen/arm/ArchFactory.cpp
new file mode 100644
index 0000000..2daa7bc
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchFactory.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file contains arm-specific codegen factory support.
+ * It is included by
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static TGT_LIR *genRegImmCheck(CompilationUnit *cUnit,
+                               ArmConditionCode cond, int reg,
+                               int checkValue, int dOffset,
+                               TGT_LIR *pcrLabel)
+{
+    TGT_LIR *branch = genCmpImmBranch(cUnit, cond, reg, checkValue);
+    if (cUnit->jitMode == kJitMethod) {
+        BasicBlock *bb = cUnit->curBlock;
+        if (bb->taken) {
+            ArmLIR  *exceptionLabel = (ArmLIR *) cUnit->blockLabelList;
+            exceptionLabel += bb->taken->id;
+            branch->generic.target = (LIR *) exceptionLabel;
+            return exceptionLabel;
+        } else {
+            ALOGE("Catch blocks not handled yet");
+            dvmAbort();
+            return NULL;
+        }
+    } else {
+        return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    }
+}
+
+/*
+ * Perform null-check on a register. sReg is the ssa register being checked,
+ * and mReg is the machine register holding the actual value. If internal state
+ * indicates that sReg has been checked before the check request is ignored.
+ */
+static TGT_LIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+                             int dOffset, TGT_LIR *pcrLabel)
+{
+    /* This particular Dalvik register has been null-checked */
+    if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
+        return pcrLabel;
+    }
+    dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
+    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static TGT_LIR *genRegRegCheck(CompilationUnit *cUnit,
+                               ArmConditionCode cond,
+                               int reg1, int reg2, int dOffset,
+                               TGT_LIR *pcrLabel)
+{
+    TGT_LIR *res;
+    res = opRegReg(cUnit, kOpCmp, reg1, reg2);
+    TGT_LIR *branch = opCondBranch(cUnit, cond);
+    genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    return res;
+}
+
+/*
+ * Perform zero-check on a register. Similar to genNullCheck but the value being
+ * checked does not have a corresponding Dalvik register.
+ */
+static TGT_LIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+                             int dOffset, TGT_LIR *pcrLabel)
+{
+    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static TGT_LIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                               int rBound, int dOffset, TGT_LIR *pcrLabel)
+{
+    return genRegRegCheck(cUnit, kArmCondCs, rIndex, rBound, dOffset,
+                          pcrLabel);
+}
+
+/*
+ * Jump to the out-of-line handler in ARM mode to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opcode)
+{
+    /*
+     * NOTE - In practice BLX only needs one operand, but since the assembler
+     * may abort itself and retry due to other out-of-range conditions we
+     * cannot really use operand[0] to store the absolute target address since
+     * it may get clobbered by the final relative offset. Therefore,
+     * we fake BLX_1 is a two operand instruction and the absolute target
+     * address is stored in operand[1].
+     */
+    dvmCompilerClobberHandlerRegs(cUnit);
+    newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+    newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+}
diff --git a/vm/compiler/codegen/arm/ArchUtility.cpp b/vm/compiler/codegen/arm/ArchUtility.cpp
new file mode 100644
index 0000000..b5e739f
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchUtility.cpp
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "../../CompilerInternals.h"
+#include "libdex/DexOpcodes.h"
+#include "ArmLIR.h"
+
+static const char *shiftNames[4] = {
+    "lsl",
+    "lsr",
+    "asr",
+    "ror"};
+
+/* Decode and print a ARM register name */
+static char * decodeRegList(ArmOpcode opcode, int vector, char *buf)
+{
+    int i;
+    bool printed = false;
+    buf[0] = 0;
+    for (i = 0; i < 16; i++, vector >>= 1) {
+        if (vector & 0x1) {
+            int regId = i;
+            if (opcode == kThumbPush && i == 8) {
+                regId = r14lr;
+            } else if (opcode == kThumbPop && i == 8) {
+                regId = r15pc;
+            }
+            if (printed) {
+                sprintf(buf + strlen(buf), ", r%d", regId);
+            } else {
+                printed = true;
+                sprintf(buf, "r%d", regId);
+            }
+        }
+    }
+    return buf;
+}
+
+static int expandImmediate(int value)
+{
+    int mode = (value & 0xf00) >> 8;
+    u4 bits = value & 0xff;
+    switch(mode) {
+        case 0:
+            return bits;
+       case 1:
+            return (bits << 16) | bits;
+       case 2:
+            return (bits << 24) | (bits << 8);
+       case 3:
+            return (bits << 24) | (bits << 16) | (bits << 8) | bits;
+      default:
+            break;
+    }
+    bits = (bits | 0x80) << 24;
+    return bits >> (((value & 0xf80) >> 7) - 8);
+}
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+static void buildInsnString(const char *fmt, ArmLIR *lir, char* buf,
+                            unsigned char *baseAddr, int size)
+{
+    int i;
+    char *bufEnd = &buf[size-1];
+    const char *fmtEnd = &fmt[strlen(fmt)];
+    char tbuf[256];
+    const char *name;
+    char nc;
+    while (fmt < fmtEnd) {
+        int operand;
+        if (*fmt == '!') {
+            fmt++;
+            assert(fmt < fmtEnd);
+            nc = *fmt++;
+            if (nc=='!') {
+                strcpy(tbuf, "!");
+            } else {
+               assert(fmt < fmtEnd);
+               assert((unsigned)(nc-'0') < 4);
+               operand = lir->operands[nc-'0'];
+               switch(*fmt++) {
+                   case 'H':
+                       if (operand != 0) {
+                           sprintf(tbuf, ", %s %d",shiftNames[operand & 0x3],
+                                   operand >> 2);
+                       } else {
+                           strcpy(tbuf,"");
+                       }
+                       break;
+                   case 'B':
+                       switch (operand) {
+                           case kSY:
+                               name = "sy";
+                               break;
+                           case kST:
+                               name = "st";
+                               break;
+                           case kISH:
+                               name = "ish";
+                               break;
+                           case kISHST:
+                               name = "ishst";
+                               break;
+                           case kNSH:
+                               name = "nsh";
+                               break;
+                           case kNSHST:
+                               name = "shst";
+                               break;
+                           default:
+                               name = "DecodeError";
+                               break;
+                       }
+                       strcpy(tbuf, name);
+                       break;
+                   case 'b':
+                       strcpy(tbuf,"0000");
+                       for (i=3; i>= 0; i--) {
+                           tbuf[i] += operand & 1;
+                           operand >>= 1;
+                       }
+                       break;
+                   case 'n':
+                       operand = ~expandImmediate(operand);
+                       sprintf(tbuf,"%d [%#x]", operand, operand);
+                       break;
+                   case 'm':
+                       operand = expandImmediate(operand);
+                       sprintf(tbuf,"%d [%#x]", operand, operand);
+                       break;
+                   case 's':
+                       sprintf(tbuf,"s%d",operand & FP_REG_MASK);
+                       break;
+                   case 'S':
+                       sprintf(tbuf,"d%d",(operand & FP_REG_MASK) >> 1);
+                       break;
+                   case 'h':
+                       sprintf(tbuf,"%04x", operand);
+                       break;
+                   case 'M':
+                   case 'd':
+                       sprintf(tbuf,"%d", operand);
+                       break;
+                   case 'E':
+                       sprintf(tbuf,"%d", operand*4);
+                       break;
+                   case 'F':
+                       sprintf(tbuf,"%d", operand*2);
+                       break;
+                   case 'c':
+                       switch (operand) {
+                           case kArmCondEq:
+                               strcpy(tbuf, "eq");
+                               break;
+                           case kArmCondNe:
+                               strcpy(tbuf, "ne");
+                               break;
+                           case kArmCondLt:
+                               strcpy(tbuf, "lt");
+                               break;
+                           case kArmCondGe:
+                               strcpy(tbuf, "ge");
+                               break;
+                           case kArmCondGt:
+                               strcpy(tbuf, "gt");
+                               break;
+                           case kArmCondLe:
+                               strcpy(tbuf, "le");
+                               break;
+                           case kArmCondCs:
+                               strcpy(tbuf, "cs");
+                               break;
+                           case kArmCondMi:
+                               strcpy(tbuf, "mi");
+                               break;
+                           default:
+                               strcpy(tbuf, "");
+                               break;
+                       }
+                       break;
+                   case 't':
+                       sprintf(tbuf,"0x%08x (L%p)",
+                               (int) baseAddr + lir->generic.offset + 4 +
+                               (operand << 1),
+                               lir->generic.target);
+                       break;
+                   case 'u': {
+                       int offset_1 = lir->operands[0];
+                       int offset_2 = NEXT_LIR(lir)->operands[0];
+                       intptr_t target =
+                           ((((intptr_t) baseAddr + lir->generic.offset + 4) &
+                            ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+                           0xfffffffc;
+                       sprintf(tbuf, "%p", (void *) target);
+                       break;
+                    }
+
+                   /* Nothing to print for BLX_2 */
+                   case 'v':
+                       strcpy(tbuf, "see above");
+                       break;
+                   case 'R':
+                       decodeRegList(lir->opcode, operand, tbuf);
+                       break;
+                   default:
+                       strcpy(tbuf,"DecodeError");
+                       break;
+               }
+               if (buf+strlen(tbuf) <= bufEnd) {
+                   strcpy(buf, tbuf);
+                   buf += strlen(tbuf);
+               } else {
+                   break;
+               }
+            }
+        } else {
+           *buf++ = *fmt++;
+        }
+        if (buf == bufEnd)
+            break;
+    }
+    *buf = 0;
+}
+
+void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
+{
+    char buf[256];
+    buf[0] = 0;
+    ArmLIR *armLIR = (ArmLIR *) lir;
+
+    if (mask == ENCODE_ALL) {
+        strcpy(buf, "all");
+    } else {
+        char num[8];
+        int i;
+
+        for (i = 0; i < kRegEnd; i++) {
+            if (mask & (1ULL << i)) {
+                sprintf(num, "%d ", i);
+                strcat(buf, num);
+            }
+        }
+
+        if (mask & ENCODE_CCODE) {
+            strcat(buf, "cc ");
+        }
+        if (mask & ENCODE_FP_STATUS) {
+            strcat(buf, "fpcc ");
+        }
+
+        /* Memory bits */
+        if (armLIR && (mask & ENCODE_DALVIK_REG)) {
+            sprintf(buf + strlen(buf), "dr%d%s", armLIR->aliasInfo & 0xffff,
+                    (armLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+        }
+        if (mask & ENCODE_LITERAL) {
+            strcat(buf, "lit ");
+        }
+
+        if (mask & ENCODE_HEAP_REF) {
+            strcat(buf, "heap ");
+        }
+        if (mask & ENCODE_MUST_NOT_ALIAS) {
+            strcat(buf, "noalias ");
+        }
+    }
+    if (buf[0]) {
+        ALOGD("%s: %s", prefix, buf);
+    }
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+#define DUMP_SSA_REP(X)
+
+/* Pretty-print a LIR instruction */
+void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
+{
+    ArmLIR *lir = (ArmLIR *) arg;
+    char buf[256];
+    char opName[256];
+    int offset = lir->generic.offset;
+    int dest = lir->operands[0];
+    const bool dumpNop = false;
+
+    /* Handle pseudo-ops individually, and all regular insns as a group */
+    switch(lir->opcode) {
+        case kArmChainingCellBottom:
+            ALOGD("-------- end of chaining cells (0x%04x)", offset);
+            break;
+        case kArmPseudoBarrier:
+            ALOGD("-------- BARRIER");
+            break;
+        case kArmPseudoExtended:
+            ALOGD("-------- %s", (char *) dest);
+            break;
+        case kArmPseudoSSARep:
+            DUMP_SSA_REP(LOGD("-------- %s", (char *) dest));
+            break;
+        case kArmPseudoChainingCellBackwardBranch:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (backward branch): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellNormal:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (normal): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellHot:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (hot): 0x%04x", dest);
+            break;
+        case kArmPseudoChainingCellInvokePredicted:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (predicted): %s%s",
+                 dest ? ((Method *) dest)->clazz->descriptor : "",
+                 dest ? ((Method *) dest)->name : "N/A");
+            break;
+        case kArmPseudoChainingCellInvokeSingleton:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (invoke singleton): %s%s/%p",
+                 ((Method *)dest)->clazz->descriptor,
+                 ((Method *)dest)->name,
+                 ((Method *)dest)->insns);
+            break;
+        case kArmPseudoEntryBlock:
+            ALOGD("-------- entry offset: 0x%04x", dest);
+            break;
+        case kArmPseudoDalvikByteCodeBoundary:
+            ALOGD("-------- dalvik offset: 0x%04x @ %s", dest,
+                 (char *) lir->operands[1]);
+            break;
+        case kArmPseudoExitBlock:
+            ALOGD("-------- exit offset: 0x%04x", dest);
+            break;
+        case kArmPseudoPseudoAlign4:
+            ALOGD("%p (%04x): .align4", baseAddr + offset, offset);
+            break;
+        case kArmPseudoPCReconstructionCell:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x", dest,
+                 lir->operands[1]);
+            break;
+        case kArmPseudoPCReconstructionBlockLabel:
+            /* Do nothing */
+            break;
+        case kArmPseudoEHBlockLabel:
+            ALOGD("Exception_Handling:");
+            break;
+        case kArmPseudoTargetLabel:
+        case kArmPseudoNormalBlockLabel:
+            ALOGD("L%p:", lir);
+            break;
+        default:
+            if (lir->flags.isNop && !dumpNop) {
+                break;
+            }
+            buildInsnString(EncodingMap[lir->opcode].name, lir, opName,
+                            baseAddr, 256);
+            buildInsnString(EncodingMap[lir->opcode].fmt, lir, buf, baseAddr,
+                            256);
+            ALOGD("%p (%04x): %-8s%s%s",
+                 baseAddr + offset, offset, opName, buf,
+                 lir->flags.isNop ? "(nop)" : "");
+            break;
+    }
+
+    if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->useMask, "use"));
+    }
+    if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->defMask, "def"));
+    }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+    ALOGD("Dumping LIR insns");
+    LIR *lirInsn;
+    ArmLIR *armLIR;
+
+    ALOGD("installed code is at %p", cUnit->baseAddr);
+    ALOGD("total size is %d bytes", cUnit->totalSize);
+    for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+        dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
+    }
+    for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
+        armLIR = (ArmLIR *) lirInsn;
+        ALOGD("%p (%04x): .class (%s)",
+             (char*)cUnit->baseAddr + armLIR->generic.offset,
+             armLIR->generic.offset,
+             ((CallsiteInfo *) armLIR->operands[0])->classDescriptor);
+    }
+    for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
+        armLIR = (ArmLIR *) lirInsn;
+        ALOGD("%p (%04x): .word (%#x)",
+             (char*)cUnit->baseAddr + armLIR->generic.offset,
+             armLIR->generic.offset,
+             armLIR->operands[0]);
+    }
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /*
+     * de is an invalid opcode for arm.
+     * From gdb disassembly:  <UNDEFINED> instruction: 0xdede
+     */
+
+    memset(start, 0xde, size);
+}
diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h
new file mode 100644
index 0000000..e427889
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmLIR.h
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
+
+#include "Dalvik.h"
+#include "compiler/CompilerInternals.h"
+
+/*
+ * r0, r1, r2, r3 are always scratch
+ * r4 (rPC) is scratch for Jit, but most be restored when resuming interp
+ * r5 (rFP) is reserved [holds Dalvik frame pointer]
+ * r6 (rSELF) is reserved [holds current &Thread]
+ * r7 (rINST) is scratch for Jit
+ * r8 (rIBASE) is scratch for Jit, but must be restored when resuming interp
+ * r9 is reserved
+ * r10 is always scratch
+ * r11 (fp) used by gcc unless -fomit-frame-pointer set [available for jit?]
+ * r12 is always scratch
+ * r13 (sp) is reserved
+ * r14 (lr) is scratch for Jit
+ * r15 (pc) is reserved
+ *
+ * Preserved across C calls: r4, r5, r6, r7, r8, r10, r11
+ * Trashed across C calls: r0, r1, r2, r3, r12, r14
+ *
+ * Floating pointer registers
+ * s0-s31
+ * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ *
+ * s16-s31 (d8-d15) preserved across C calls
+ * s0-s15 (d0-d7) trashed across C calls
+ *
+ * For Thumb code use:
+ *       r0, r1, r2, r3 to hold operands/results
+ *       r4, r7 for temps
+ *
+ * For Thumb2 code use:
+ *       r0, r1, r2, r3, r8, r9, r10, r11, r12, r14 for operands/results
+ *       r4, r7 for temps
+ *       s16-s31/d8-d15 for operands/results
+ *       s0-s15/d0-d7 for temps
+ *
+ * When transitioning from code cache to interp:
+ *       restore rIBASE
+ *       restore rPC
+ *       restore r11?
+ */
+
+/* Offset to distingish FP regs */
+#define FP_REG_OFFSET 32
+/* Offset to distinguish DP FP regs */
+#define FP_DOUBLE 64
+/* Reg types */
+#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
+#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
+#define LOWREG(x) ((x & 0x7) == x)
+#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
+#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area.  Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define S2D(x,y) ((x) | FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define FP_REG_MASK (FP_REG_OFFSET-1)
+/* non-existent Dalvik register */
+#define vNone   (-1)
+/* non-existant physical register */
+#define rNone   (-1)
+
+/* RegisterLocation templates return values (r0, or r0/r1) */
+#define LOC_C_RETURN {kLocPhysReg, 0, 0, r0, 0, -1}
+#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, r0, r1, -1}
+/* RegisterLocation templates for interpState->retVal; */
+#define LOC_DALVIK_RETURN_VAL {kLocRetval, 0, 0, 0, 0, -1}
+#define LOC_DALVIK_RETURN_VAL_WIDE {kLocRetval, 1, 0, 0, 0, -1}
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+typedef struct RegisterInfo {
+    int reg;                    // Reg number
+    bool inUse;                 // Has it been allocated?
+    bool pair;                  // Part of a register pair?
+    int partner;                // If pair, other reg of pair
+    bool live;                  // Is there an associated SSA name?
+    bool dirty;                 // If live, is it dirty?
+    int sReg;                   // Name of live value
+    struct LIR *defStart;       // Starting inst in last def sequence
+    struct LIR *defEnd;         // Ending inst in last def sequence
+} RegisterInfo;
+
+typedef struct RegisterPool {
+    BitVector *nullCheckedRegs; // Track which registers have been null-checked
+    int numCoreTemps;
+    RegisterInfo *coreTemps;
+    int nextCoreTemp;
+    int numFPTemps;
+    RegisterInfo *FPTemps;
+    int nextFPTemp;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+    kGPReg0     = 0,
+    kRegSP      = 13,
+    kRegLR      = 14,
+    kRegPC      = 15,
+    kFPReg0     = 16,
+    kRegEnd     = 48,
+    kCCode      = kRegEnd,
+    kFPStatus,          // FP status word
+    // The following four bits are for memory disambiguation
+    kDalvikReg,         // 1 Dalvik Frame (can be fully disambiguated)
+    kLiteral,           // 2 Literal pool (can be fully disambiguated)
+    kHeapRef,           // 3 Somewhere on the heap (alias with any other heap)
+    kMustNotAlias,      // 4 Guaranteed to be non-alias (eg *(r6+x))
+} ResourceEncodingPos;
+
+#define ENCODE_REG_LIST(N)      ((u8) N)
+#define ENCODE_REG_SP           (1ULL << kRegSP)
+#define ENCODE_REG_LR           (1ULL << kRegLR)
+#define ENCODE_REG_PC           (1ULL << kRegPC)
+#define ENCODE_CCODE            (1ULL << kCCode)
+#define ENCODE_FP_STATUS        (1ULL << kFPStatus)
+
+/* Abstract memory locations */
+#define ENCODE_DALVIK_REG       (1ULL << kDalvikReg)
+#define ENCODE_LITERAL          (1ULL << kLiteral)
+#define ENCODE_HEAP_REF         (1ULL << kHeapRef)
+#define ENCODE_MUST_NOT_ALIAS   (1ULL << kMustNotAlias)
+
+#define ENCODE_ALL              (~0ULL)
+#define ENCODE_MEM              (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
+                                 ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+#define DECODE_ALIAS_INFO_REG(X)        (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE(X)       ((X & 0x80000000) ? 1 : 0)
+
+typedef enum OpSize {
+    kWord,
+    kLong,
+    kSingle,
+    kDouble,
+    kUnsignedHalf,
+    kSignedHalf,
+    kUnsignedByte,
+    kSignedByte,
+} OpSize;
+
+typedef enum OpKind {
+    kOpMov,
+    kOpMvn,
+    kOpCmp,
+    kOpLsl,
+    kOpLsr,
+    kOpAsr,
+    kOpRor,
+    kOpNot,
+    kOpAnd,
+    kOpOr,
+    kOpXor,
+    kOpNeg,
+    kOpAdd,
+    kOpAdc,
+    kOpSub,
+    kOpSbc,
+    kOpRsub,
+    kOpMul,
+    kOpDiv,
+    kOpRem,
+    kOpBic,
+    kOpCmn,
+    kOpTst,
+    kOpBkpt,
+    kOpBlx,
+    kOpPush,
+    kOpPop,
+    kOp2Char,
+    kOp2Short,
+    kOp2Byte,
+    kOpCondBr,
+    kOpUncondBr,
+} OpKind;
+
+/*
+ * Annotate special-purpose core registers:
+ *   - VM: r4PC, r5FP, and r6SELF
+ *   - ARM architecture: r13sp, r14lr, and r15pc
+ *
+ * rPC, rFP, and rSELF are for architecture-independent code to use.
+ */
+typedef enum NativeRegisterPool {
+    r0     = 0,
+    r1     = 1,
+    r2     = 2,
+    r3     = 3,
+    rPC    = 4,
+    r4PC   = rPC,
+    rFP    = 5,
+    r5FP   = rFP,
+    rSELF  = 6,
+    r6SELF = rSELF,
+    r7     = 7,
+    r8     = 8,
+    r9     = 9,
+    r10    = 10,
+    r11    = 11,
+    r12    = 12,
+    r13sp  = 13,
+    r14lr  = 14,
+    r15pc  = 15,
+    fr0  =  0 + FP_REG_OFFSET,
+    fr1  =  1 + FP_REG_OFFSET,
+    fr2  =  2 + FP_REG_OFFSET,
+    fr3  =  3 + FP_REG_OFFSET,
+    fr4  =  4 + FP_REG_OFFSET,
+    fr5  =  5 + FP_REG_OFFSET,
+    fr6  =  6 + FP_REG_OFFSET,
+    fr7  =  7 + FP_REG_OFFSET,
+    fr8  =  8 + FP_REG_OFFSET,
+    fr9  =  9 + FP_REG_OFFSET,
+    fr10 = 10 + FP_REG_OFFSET,
+    fr11 = 11 + FP_REG_OFFSET,
+    fr12 = 12 + FP_REG_OFFSET,
+    fr13 = 13 + FP_REG_OFFSET,
+    fr14 = 14 + FP_REG_OFFSET,
+    fr15 = 15 + FP_REG_OFFSET,
+    fr16 = 16 + FP_REG_OFFSET,
+    fr17 = 17 + FP_REG_OFFSET,
+    fr18 = 18 + FP_REG_OFFSET,
+    fr19 = 19 + FP_REG_OFFSET,
+    fr20 = 20 + FP_REG_OFFSET,
+    fr21 = 21 + FP_REG_OFFSET,
+    fr22 = 22 + FP_REG_OFFSET,
+    fr23 = 23 + FP_REG_OFFSET,
+    fr24 = 24 + FP_REG_OFFSET,
+    fr25 = 25 + FP_REG_OFFSET,
+    fr26 = 26 + FP_REG_OFFSET,
+    fr27 = 27 + FP_REG_OFFSET,
+    fr28 = 28 + FP_REG_OFFSET,
+    fr29 = 29 + FP_REG_OFFSET,
+    fr30 = 30 + FP_REG_OFFSET,
+    fr31 = 31 + FP_REG_OFFSET,
+    dr0 = fr0 + FP_DOUBLE,
+    dr1 = fr2 + FP_DOUBLE,
+    dr2 = fr4 + FP_DOUBLE,
+    dr3 = fr6 + FP_DOUBLE,
+    dr4 = fr8 + FP_DOUBLE,
+    dr5 = fr10 + FP_DOUBLE,
+    dr6 = fr12 + FP_DOUBLE,
+    dr7 = fr14 + FP_DOUBLE,
+    dr8 = fr16 + FP_DOUBLE,
+    dr9 = fr18 + FP_DOUBLE,
+    dr10 = fr20 + FP_DOUBLE,
+    dr11 = fr22 + FP_DOUBLE,
+    dr12 = fr24 + FP_DOUBLE,
+    dr13 = fr26 + FP_DOUBLE,
+    dr14 = fr28 + FP_DOUBLE,
+    dr15 = fr30 + FP_DOUBLE,
+} NativeRegisterPool;
+
+/* Shift encodings */
+typedef enum ArmShiftEncodings {
+    kArmLsl = 0x0,
+    kArmLsr = 0x1,
+    kArmAsr = 0x2,
+    kArmRor = 0x3
+} ArmShiftEncodings;
+
+/* Thumb condition encodings */
+typedef enum ArmConditionCode {
+    kArmCondEq = 0x0,    /* 0000 */
+    kArmCondNe = 0x1,    /* 0001 */
+    kArmCondCs = 0x2,    /* 0010 */
+    kArmCondCc = 0x3,    /* 0011 */
+    kArmCondMi = 0x4,    /* 0100 */
+    kArmCondPl = 0x5,    /* 0101 */
+    kArmCondVs = 0x6,    /* 0110 */
+    kArmCondVc = 0x7,    /* 0111 */
+    kArmCondHi = 0x8,    /* 1000 */
+    kArmCondLs = 0x9,    /* 1001 */
+    kArmCondGe = 0xa,    /* 1010 */
+    kArmCondLt = 0xb,    /* 1011 */
+    kArmCondGt = 0xc,    /* 1100 */
+    kArmCondLe = 0xd,    /* 1101 */
+    kArmCondAl = 0xe,    /* 1110 */
+    kArmCondNv = 0xf,    /* 1111 */
+} ArmConditionCode;
+
+#define isPseudoOpcode(opcode) ((int)(opcode) < 0)
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding snippet positions will be defined in
+ * Assemble.c.
+ */
+typedef enum ArmOpcode {
+    kArmChainingCellBottom = -18,
+    kArmPseudoBarrier = -17,
+    kArmPseudoExtended = -16,
+    kArmPseudoSSARep = -15,
+    kArmPseudoEntryBlock = -14,
+    kArmPseudoExitBlock = -13,
+    kArmPseudoTargetLabel = -12,
+    kArmPseudoChainingCellBackwardBranch = -11,
+    kArmPseudoChainingCellHot = -10,
+    kArmPseudoChainingCellInvokePredicted = -9,
+    kArmPseudoChainingCellInvokeSingleton = -8,
+    kArmPseudoChainingCellNormal = -7,
+    kArmPseudoDalvikByteCodeBoundary = -6,
+    kArmPseudoPseudoAlign4 = -5,
+    kArmPseudoPCReconstructionCell = -4,
+    kArmPseudoPCReconstructionBlockLabel = -3,
+    kArmPseudoEHBlockLabel = -2,
+    kArmPseudoNormalBlockLabel = -1,
+    /************************************************************************/
+    kArm16BitData,       /* DATA   [0] rd[15..0] */
+    kThumbAdcRR,         /* adc     [0100000101] rm[5..3] rd[2..0] */
+    kThumbAddRRI3,       /* add(1)  [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/
+    kThumbAddRI8,        /* add(2)  [00110] rd[10..8] imm_8[7..0] */
+    kThumbAddRRR,        /* add(3)  [0001100] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbAddRRLH,       /* add(4)  [01000100] H12[01] rm[5..3] rd[2..0] */
+    kThumbAddRRHL,       /* add(4)  [01001000] H12[10] rm[5..3] rd[2..0] */
+    kThumbAddRRHH,       /* add(4)  [01001100] H12[11] rm[5..3] rd[2..0] */
+    kThumbAddPcRel,      /* add(5)  [10100] rd[10..8] imm_8[7..0] */
+    kThumbAddSpRel,      /* add(6)  [10101] rd[10..8] imm_8[7..0] */
+    kThumbAddSpI7,       /* add(7)  [101100000] imm_7[6..0] */
+    kThumbAndRR,         /* and     [0100000000] rm[5..3] rd[2..0] */
+    kThumbAsrRRI5,       /* asr(1)  [00010] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbAsrRR,         /* asr(2)  [0100000100] rs[5..3] rd[2..0] */
+    kThumbBCond,         /* b(1)    [1101] cond[11..8] offset_8[7..0] */
+    kThumbBUncond,       /* b(2)    [11100] offset_11[10..0] */
+    kThumbBicRR,         /* bic     [0100001110] rm[5..3] rd[2..0] */
+    kThumbBkpt,          /* bkpt    [10111110] imm_8[7..0] */
+    kThumbBlx1,          /* blx(1)  [111] H[10] offset_11[10..0] */
+    kThumbBlx2,          /* blx(1)  [111] H[01] offset_11[10..0] */
+    kThumbBl1,           /* blx(1)  [111] H[10] offset_11[10..0] */
+    kThumbBl2,           /* blx(1)  [111] H[11] offset_11[10..0] */
+    kThumbBlxR,          /* blx(2)  [010001111] rm[6..3] [000] */
+    kThumbBx,            /* bx      [010001110] H2[6..6] rm[5..3] SBZ[000] */
+    kThumbCmnRR,         /* cmn     [0100001011] rm[5..3] rd[2..0] */
+    kThumbCmpRI8,        /* cmp(1)  [00101] rn[10..8] imm_8[7..0] */
+    kThumbCmpRR,         /* cmp(2)  [0100001010] rm[5..3] rd[2..0] */
+    kThumbCmpLH,         /* cmp(3)  [01000101] H12[01] rm[5..3] rd[2..0] */
+    kThumbCmpHL,         /* cmp(3)  [01000110] H12[10] rm[5..3] rd[2..0] */
+    kThumbCmpHH,         /* cmp(3)  [01000111] H12[11] rm[5..3] rd[2..0] */
+    kThumbEorRR,         /* eor     [0100000001] rm[5..3] rd[2..0] */
+    kThumbLdmia,         /* ldmia   [11001] rn[10..8] reglist [7..0] */
+    kThumbLdrRRI5,       /* ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrRRR,        /* ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrPcRel,      /* ldr(3)  [01001] rd[10..8] imm_8[7..0] */
+    kThumbLdrSpRel,      /* ldr(4)  [10011] rd[10..8] imm_8[7..0] */
+    kThumbLdrbRRI5,      /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrbRRR,       /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrhRRI5,      /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbLdrhRRR,       /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrsbRRR,      /* ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLdrshRRR,      /* ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbLslRRI5,       /* lsl(1)  [00000] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbLslRR,         /* lsl(2)  [0100000010] rs[5..3] rd[2..0] */
+    kThumbLsrRRI5,       /* lsr(1)  [00001] imm_5[10..6] rm[5..3] rd[2..0] */
+    kThumbLsrRR,         /* lsr(2)  [0100000011] rs[5..3] rd[2..0] */
+    kThumbMovImm,        /* mov(1)  [00100] rd[10..8] imm_8[7..0] */
+    kThumbMovRR,         /* mov(2)  [0001110000] rn[5..3] rd[2..0] */
+    kThumbMovRR_H2H,     /* mov(3)  [01000111] H12[11] rm[5..3] rd[2..0] */
+    kThumbMovRR_H2L,     /* mov(3)  [01000110] H12[01] rm[5..3] rd[2..0] */
+    kThumbMovRR_L2H,     /* mov(3)  [01000101] H12[10] rm[5..3] rd[2..0] */
+    kThumbMul,           /* mul     [0100001101] rm[5..3] rd[2..0] */
+    kThumbMvn,           /* mvn     [0100001111] rm[5..3] rd[2..0] */
+    kThumbNeg,           /* neg     [0100001001] rm[5..3] rd[2..0] */
+    kThumbOrr,           /* orr     [0100001100] rm[5..3] rd[2..0] */
+    kThumbPop,           /* pop     [1011110] r[8..8] rl[7..0] */
+    kThumbPush,          /* push    [1011010] r[8..8] rl[7..0] */
+    kThumbRorRR,         /* ror     [0100000111] rs[5..3] rd[2..0] */
+    kThumbSbc,           /* sbc     [0100000110] rm[5..3] rd[2..0] */
+    kThumbStmia,         /* stmia   [11000] rn[10..8] reglist [7.. 0] */
+    kThumbStrRRI5,       /* str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrRRR,        /* str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbStrSpRel,      /* str(3)  [10010] rd[10..8] imm_8[7..0] */
+    kThumbStrbRRI5,      /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrbRRR,       /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbStrhRRI5,      /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */
+    kThumbStrhRRR,       /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbSubRRI3,       /* sub(1)  [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/
+    kThumbSubRI8,        /* sub(2)  [00111] rd[10..8] imm_8[7..0] */
+    kThumbSubRRR,        /* sub(3)  [0001101] rm[8..6] rn[5..3] rd[2..0] */
+    kThumbSubSpI7,       /* sub(4)  [101100001] imm_7[6..0] */
+    kThumbSwi,           /* swi     [11011111] imm_8[7..0] */
+    kThumbTst,           /* tst     [0100001000] rm[5..3] rn[2..0] */
+    kThumb2Vldrs,        /* vldr low  sx [111011011001] rn[19..16] rd[15-12]
+                                    [1010] imm_8[7..0] */
+    kThumb2Vldrd,        /* vldr low  dx [111011011001] rn[19..16] rd[15-12]
+                                    [1011] imm_8[7..0] */
+    kThumb2Vmuls,        /* vmul vd, vn, vm [111011100010] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vmuld,        /* vmul vd, vn, vm [111011100010] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    kThumb2Vstrs,        /* vstr low  sx [111011011000] rn[19..16] rd[15-12]
+                                    [1010] imm_8[7..0] */
+    kThumb2Vstrd,        /* vstr low  dx [111011011000] rn[19..16] rd[15-12]
+                                    [1011] imm_8[7..0] */
+    kThumb2Vsubs,        /* vsub vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10100040] rm[3..0] */
+    kThumb2Vsubd,        /* vsub vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10110040] rm[3..0] */
+    kThumb2Vadds,        /* vadd vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vaddd,        /* vadd vd, vn, vm [111011100011] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    kThumb2Vdivs,        /* vdiv vd, vn, vm [111011101000] rn[19..16]
+                                    rd[15-12] [10100000] rm[3..0] */
+    kThumb2Vdivd,        /* vdiv vd, vn, vm [111011101000] rn[19..16]
+                                    rd[15-12] [10110000] rm[3..0] */
+    kThumb2VmlaF64,      /* vmla.F64 vd, vn, vm [111011100000] vn[19..16]
+                                     vd[15..12] [10110000] vm[3..0] */
+    kThumb2VcvtIF,       /* vcvt.F32 vd, vm [1110111010111000] vd[15..12]
+                                    [10101100] vm[3..0] */
+    kThumb2VcvtID,       /* vcvt.F64 vd, vm [1110111010111000] vd[15..12]
+                                       [10111100] vm[3..0] */
+    kThumb2VcvtFI,       /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12]
+                                       [10101100] vm[3..0] */
+    kThumb2VcvtDI,       /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12]
+                                       [10111100] vm[3..0] */
+    kThumb2VcvtFd,       /* vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12]
+                                       [10101100] vm[3..0] */
+    kThumb2VcvtDF,       /* vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12]
+                                       [10111100] vm[3..0] */
+    kThumb2VcvtF64S32,   /* vcvt.F64.S32 vd, vm [1110111010111000] vd[15..12]
+                                       [10111100] vm[3..0] */
+    kThumb2VcvtF64U32,   /* vcvt.F64.U32 vd, vm [1110111010111000] vd[15..12]
+                                       [10110100] vm[3..0] */
+    kThumb2Vsqrts,       /* vsqrt.f32 vd, vm [1110111010110001] vd[15..12]
+                                       [10101100] vm[3..0] */
+    kThumb2Vsqrtd,       /* vsqrt.f64 vd, vm [1110111010110001] vd[15..12]
+                                       [10111100] vm[3..0] */
+    kThumb2MovImmShift,  /* mov(T2) rd, #<const> [11110] i [00001001111]
+                                       imm3 rd[11..8] imm8 */
+    kThumb2MovImm16,     /* mov(T3) rd, #<const> [11110] i [0010100] imm4 [0]
+                                       imm3 rd[11..8] imm8 */
+    kThumb2StrRRI12,     /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+    kThumb2LdrRRI12,     /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+    kThumb2StrRRI8Predec, /* str(Imm,T4) rd,[rn,#-imm8] [111110000100]
+                                       rn[19..16] rt[15..12] [1100] imm[7..0]*/
+    kThumb2LdrRRI8Predec, /* ldr(Imm,T4) rd,[rn,#-imm8] [111110000101]
+                                       rn[19..16] rt[15..12] [1100] imm[7..0]*/
+    kThumb2Cbnz,         /* cbnz rd,<label> [101110] i [1] imm5[7..3]
+                                       rn[2..0] */
+    kThumb2Cbz,          /* cbn rd,<label> [101100] i [1] imm5[7..3]
+                                       rn[2..0] */
+    kThumb2AddRRI12,     /* add rd, rn, #imm12 [11110] i [100000] rn[19..16]
+                                       [0] imm3[14..12] rd[11..8] imm8[7..0] */
+    kThumb2MovRR,        /* mov rd, rm [11101010010011110000] rd[11..8]
+                                       [0000] rm[3..0] */
+    kThumb2Vmovs,        /* vmov.f32 vd, vm [111011101] D [110000]
+                                       vd[15..12] 101001] M [0] vm[3..0] */
+    kThumb2Vmovd,        /* vmov.f64 vd, vm [111011101] D [110000]
+                                       vd[15..12] 101101] M [0] vm[3..0] */
+    kThumb2Ldmia,        /* ldmia  [111010001001[ rn[19..16] mask[15..0] */
+    kThumb2Stmia,        /* stmia  [111010001000[ rn[19..16] mask[15..0] */
+    kThumb2AddRRR,       /* add [111010110000] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2SubRRR,       /* sub [111010111010] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2SbcRRR,       /* sbc [111010110110] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2CmpRR,        /* cmp [111010111011] rn[19..16] [0000] [1111]
+                                   [0000] rm[3..0] */
+    kThumb2SubRRI12,     /* sub rd, rn, #imm12 [11110] i [01010] rn[19..16]
+                                       [0] imm3[14..12] rd[11..8] imm8[7..0] */
+    kThumb2MvnImmShift,  /* mov(T2) rd, #<const> [11110] i [00011011110]
+                                       imm3 rd[11..8] imm8 */
+    kThumb2Sel,          /* sel rd, rn, rm [111110101010] rn[19-16] rd[11-8]
+                                       rm[3-0] */
+    kThumb2Ubfx,         /* ubfx rd,rn,#lsb,#width [111100111100] rn[19..16]
+                                       [0] imm3[14-12] rd[11-8] w[4-0] */
+    kThumb2Sbfx,         /* ubfx rd,rn,#lsb,#width [111100110100] rn[19..16]
+                                       [0] imm3[14-12] rd[11-8] w[4-0] */
+    kThumb2LdrRRR,       /* ldr rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2LdrhRRR,      /* ldrh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2LdrshRRR,     /* ldrsh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2LdrbRRR,      /* ldrb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2LdrsbRRR,     /* ldrsb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2StrRRR,       /* str rt,[rn,rm,LSL #imm] [111110000100] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2StrhRRR,      /* str rt,[rn,rm,LSL #imm] [111110000010] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2StrbRRR,      /* str rt,[rn,rm,LSL #imm] [111110000000] rn[19-16]
+                                       rt[15-12] [000000] imm[5-4] rm[3-0] */
+    kThumb2LdrhRRI12,    /* ldrh rt,[rn,#imm12] [111110001011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2LdrshRRI12,   /* ldrsh rt,[rn,#imm12] [111110011011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2LdrbRRI12,    /* ldrb rt,[rn,#imm12] [111110001001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2LdrsbRRI12,   /* ldrsb rt,[rn,#imm12] [111110011001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2StrhRRI12,    /* strh rt,[rn,#imm12] [111110001010]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2StrbRRI12,    /* strb rt,[rn,#imm12] [111110001000]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+    kThumb2Pop,          /* pop     [1110100010111101] list[15-0]*/
+    kThumb2Push,         /* push    [1110100100101101] list[15-0]*/
+    kThumb2CmpRI8,       /* cmp rn, #<const> [11110] i [011011] rn[19-16] [0]
+                                       imm3 [1111] imm8[7..0] */
+    kThumb2AdcRRR,       /* adc [111010110101] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2AndRRR,       /* and [111010100000] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2BicRRR,       /* bic [111010100010] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2CmnRR,        /* cmn [111010110001] rn[19..16] [0000] [1111]
+                                   [0000] rm[3..0] */
+    kThumb2EorRRR,       /* eor [111010101000] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2MulRRR,       /* mul [111110110000] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2MnvRR,        /* mvn [11101010011011110] rd[11-8] [0000]
+                                   rm[3..0] */
+    kThumb2RsubRRI8,     /* rsub [111100011100] rn[19..16] [0000] rd[11..8]
+                                   imm8[7..0] */
+    kThumb2NegRR,        /* actually rsub rd, rn, #0 */
+    kThumb2OrrRRR,       /* orr [111010100100] rn[19..16] [0000] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2TstRR,        /* tst [111010100001] rn[19..16] [0000] [1111]
+                                   [0000] rm[3..0] */
+    kThumb2LslRRR,       /* lsl [111110100000] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2LsrRRR,       /* lsr [111110100010] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2AsrRRR,       /* asr [111110100100] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2RorRRR,       /* ror [111110100110] rn[19..16] [1111] rd[11..8]
+                                   [0000] rm[3..0] */
+    kThumb2LslRRI5,      /* lsl [11101010010011110] imm[14.12] rd[11..8]
+                                   [00] rm[3..0] */
+    kThumb2LsrRRI5,      /* lsr [11101010010011110] imm[14.12] rd[11..8]
+                                   [01] rm[3..0] */
+    kThumb2AsrRRI5,      /* asr [11101010010011110] imm[14.12] rd[11..8]
+                                   [10] rm[3..0] */
+    kThumb2RorRRI5,      /* ror [11101010010011110] imm[14.12] rd[11..8]
+                                   [11] rm[3..0] */
+    kThumb2BicRRI8,      /* bic [111100000010] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AndRRI8,      /* bic [111100000000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2OrrRRI8,      /* orr [111100000100] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2EorRRI8,      /* eor [111100001000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AddRRI8,      /* add [111100001000] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2AdcRRI8,      /* adc [111100010101] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2SubRRI8,      /* sub [111100011011] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2SbcRRI8,      /* sbc [111100010111] rn[19..16] [0] imm3
+                                   rd[11..8] imm8 */
+    kThumb2It,           /* it [10111111] firstcond[7-4] mask[3-0] */
+    kThumb2Fmstat,       /* fmstat [11101110111100011111101000010000] */
+    kThumb2Vcmpd,        /* vcmp [111011101] D [11011] rd[15-12] [1011]
+                                   E [1] M [0] rm[3-0] */
+    kThumb2Vcmps,        /* vcmp [111011101] D [11010] rd[15-12] [1011]
+                                   E [1] M [0] rm[3-0] */
+    kThumb2LdrPcRel12,   /* ldr rd,[pc,#imm12] [1111100011011111] rt[15-12]
+                                  imm12[11-0] */
+    kThumb2BCond,        /* b<c> [1110] S cond[25-22] imm6[21-16] [10]
+                                  J1 [0] J2 imm11[10..0] */
+    kThumb2Vmovd_RR,     /* vmov [111011101] D [110000] vd[15-12 [101101]
+                                  M [0] vm[3-0] */
+    kThumb2Vmovs_RR,     /* vmov [111011101] D [110000] vd[15-12 [101001]
+                                  M [0] vm[3-0] */
+    kThumb2Fmrs,         /* vmov [111011100000] vn[19-16] rt[15-12] [1010]
+                                  N [0010000] */
+    kThumb2Fmsr,         /* vmov [111011100001] vn[19-16] rt[15-12] [1010]
+                                  N [0010000] */
+    kThumb2Fmrrd,        /* vmov [111011000100] rt2[19-16] rt[15-12]
+                                  [101100] M [1] vm[3-0] */
+    kThumb2Fmdrr,        /* vmov [111011000101] rt2[19-16] rt[15-12]
+                                  [101100] M [1] vm[3-0] */
+    kThumb2Vabsd,        /* vabs.f64 [111011101] D [110000] rd[15-12]
+                                  [1011110] M [0] vm[3-0] */
+    kThumb2Vabss,        /* vabs.f32 [111011101] D [110000] rd[15-12]
+                                  [1010110] M [0] vm[3-0] */
+    kThumb2Vnegd,        /* vneg.f64 [111011101] D [110000] rd[15-12]
+                                  [1011110] M [0] vm[3-0] */
+    kThumb2Vnegs,        /* vneg.f32 [111011101] D [110000] rd[15-12]
+                                 [1010110] M [0] vm[3-0] */
+    kThumb2Vmovs_IMM8,   /* vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12]
+                                  [10100000] imm4l[3-0] */
+    kThumb2Vmovd_IMM8,   /* vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12]
+                                  [10110000] imm4l[3-0] */
+    kThumb2Mla,          /* mla [111110110000] rn[19-16] ra[15-12] rd[7-4]
+                                  [0000] rm[3-0] */
+    kThumb2MlsRRRR,      /* mls [1111101110000] rn[19-16] ra[15-12] rd[11-8]
+                                  [0001] rm[3-0] */
+    kThumb2Umull,        /* umull [111110111010] rn[19-16], rdlo[15-12]
+                                  rdhi[11-8] [0000] rm[3-0] */
+    kThumb2SdivRRR,      /* sdiv [1111101111001 rn[19-16] [1111] rd[11-8]
+                                  [1111] rm[3-0] */
+    kThumb2UdivRRR,      /* udiv [1111101111011 rn[19-16] [1111] rd[11-8]
+                                  [1111] rm[3-0] */
+    kThumb2Ldrex,        /* ldrex [111010000101] rn[19-16] rt[11-8] [1111]
+                                  imm8[7-0] */
+    kThumb2Strex,        /* strex [111010000100] rn[19-16] rt[11-8] rd[11-8]
+                                  imm8[7-0] */
+    kThumb2Clrex,        /* clrex [111100111011111110000111100101111] */
+    kThumb2Bfi,          /* bfi [111100110110] rn[19-16] [0] imm3[14-12]
+                                  rd[11-8] imm2[7-6] [0] msb[4-0] */
+    kThumb2Bfc,          /* bfc [11110011011011110] [0] imm3[14-12]
+                                  rd[11-8] imm2[7-6] [0] msb[4-0] */
+    kThumb2Dmb,          /* dmb [1111001110111111100011110101] option[3-0] */
+    kThumb2LdrPcReln12,  /* ldr rd,[pc,-#imm12] [1111100011011111] rt[15-12]
+                                  imm12[11-0] */
+    kThumb2RsbRRR,       /* rsb [111010111101] rn[19..16] [0000] rd[11..8]
+                                  [0000] rm[3..0] */
+    kThumbUndefined,     /* undefined [11011110xxxxxxxx] */
+    kArmLast,
+} ArmOpcode;
+
+/* DMB option encodings */
+typedef enum ArmOpDmbOptions {
+    kSY = 0xf,
+    kST = 0xe,
+    kISH = 0xb,
+    kISHST = 0xa,
+    kNSH = 0x7,
+    kNSHST = 0x6
+} ArmOpDmbOptions;
+
+/* Bit flags describing the behavior of each native opcode */
+typedef enum ArmOpFeatureFlags {
+    kIsBranch = 0,
+    kRegDef0,
+    kRegDef1,
+    kRegDefSP,
+    kRegDefLR,
+    kRegDefList0,
+    kRegDefList1,
+    kRegUse0,
+    kRegUse1,
+    kRegUse2,
+    kRegUse3,
+    kRegUseSP,
+    kRegUsePC,
+    kRegUseList0,
+    kRegUseList1,
+    kNoOperand,
+    kIsUnaryOp,
+    kIsBinaryOp,
+    kIsTertiaryOp,
+    kIsQuadOp,
+    kIsIT,
+    kSetsCCodes,
+    kUsesCCodes,
+    kMemLoad,
+    kMemStore,
+} ArmOpFeatureFlags;
+
+#define IS_LOAD         (1 << kMemLoad)
+#define IS_STORE        (1 << kMemStore)
+#define IS_BRANCH       (1 << kIsBranch)
+#define REG_DEF0        (1 << kRegDef0)
+#define REG_DEF1        (1 << kRegDef1)
+#define REG_DEF_SP      (1 << kRegDefSP)
+#define REG_DEF_LR      (1 << kRegDefLR)
+#define REG_DEF_LIST0   (1 << kRegDefList0)
+#define REG_DEF_LIST1   (1 << kRegDefList1)
+#define REG_USE0        (1 << kRegUse0)
+#define REG_USE1        (1 << kRegUse1)
+#define REG_USE2        (1 << kRegUse2)
+#define REG_USE3        (1 << kRegUse3)
+#define REG_USE_SP      (1 << kRegUseSP)
+#define REG_USE_PC      (1 << kRegUsePC)
+#define REG_USE_LIST0   (1 << kRegUseList0)
+#define REG_USE_LIST1   (1 << kRegUseList1)
+#define NO_OPERAND      (1 << kNoOperand)
+#define IS_UNARY_OP     (1 << kIsUnaryOp)
+#define IS_BINARY_OP    (1 << kIsBinaryOp)
+#define IS_TERTIARY_OP  (1 << kIsTertiaryOp)
+#define IS_QUAD_OP      (1 << kIsQuadOp)
+#define IS_IT           (1 << kIsIT)
+#define SETS_CCODES     (1 << kSetsCCodes)
+#define USES_CCODES     (1 << kUsesCCodes)
+
+/* Common combo register usage patterns */
+#define REG_USE01       (REG_USE0 | REG_USE1)
+#define REG_USE012      (REG_USE01 | REG_USE2)
+#define REG_USE12       (REG_USE1 | REG_USE2)
+#define REG_DEF0_USE0   (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE1   (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE01  (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE12  (REG_DEF0 | REG_USE12)
+#define REG_DEF01_USE2  (REG_DEF0 | REG_DEF1 | REG_USE2)
+
+/* Instruction assembly fieldLoc kind */
+typedef enum ArmEncodingKind {
+    kFmtUnused,
+    kFmtBitBlt,        /* Bit string using end/start */
+    kFmtDfp,           /* Double FP reg */
+    kFmtSfp,           /* Single FP reg */
+    kFmtModImm,        /* Shifted 8-bit immed using [26,14..12,7..0] */
+    kFmtImm16,         /* Zero-extended immed using [26,19..16,14..12,7..0] */
+    kFmtImm6,          /* Encoded branch target using [9,7..3]0 */
+    kFmtImm12,         /* Zero-extended immediate using [26,14..12,7..0] */
+    kFmtShift,         /* Shift descriptor, [14..12,7..4] */
+    kFmtLsb,           /* least significant bit using [14..12][7..6] */
+    kFmtBWidth,        /* bit-field width, encoded as width-1 */
+    kFmtShift5,        /* Shift count, [14..12,7..6] */
+    kFmtBrOffset,      /* Signed extended [26,11,13,21-16,10-0]:0 */
+    kFmtFPImm,         /* Encoded floating point immediate */
+} ArmEncodingKind;
+
+/* Struct used to define the snippet positions for each Thumb opcode */
+typedef struct ArmEncodingMap {
+    u4 skeleton;
+    struct {
+        ArmEncodingKind kind;
+        int end;   /* end for kFmtBitBlt, 1-bit slice end for FP regs */
+        int start; /* start for kFmtBitBlt, 4-bit slice end for FP regs */
+    } fieldLoc[4];
+    ArmOpcode opcode;
+    int flags;
+    const char* name;
+    const char* fmt;
+    int size;
+} ArmEncodingMap;
+
+/* Keys for target-specific scheduling and other optimization hints */
+typedef enum ArmTargetOptHints {
+    kMaxHoistDistance,
+} ArmTargetOptHints;
+
+extern ArmEncodingMap EncodingMap[kArmLast];
+
+/*
+ * Each instance of this struct holds a pseudo or real LIR instruction:
+ * - pseudo ones (eg labels and marks) and will be discarded by the assembler.
+ * - real ones will be assembled into Thumb instructions.
+ *
+ * Machine resources are encoded into a 64-bit vector, where the encodings are
+ * as following:
+ * - [ 0..15]: general purpose registers including PC, SP, and LR
+ * - [16..47]: floating-point registers where d0 is expanded to s[01] and s0
+ *   starts at bit 16
+ * - [48]: IT block
+ * - [49]: integer condition code
+ * - [50]: floatint-point status word
+ */
+typedef struct ArmLIR {
+    LIR generic;
+    ArmOpcode opcode;
+    int operands[4];            // [0..3] = [dest, src1, src2, extra]
+    struct {
+        bool isNop:1;           // LIR is optimized away
+        bool insertWrapper:1;   // insert branch to emulate memory accesses
+        unsigned int age:4;     // default is 0, set lazily by the optimizer
+        unsigned int size:3;    // bytes (2 for thumb, 2/4 for thumb2)
+        unsigned int unused:23;
+    } flags;
+    int aliasInfo;              // For Dalvik register & litpool disambiguation
+    u8 useMask;                 // Resource mask for use
+    u8 defMask;                 // Resource mask for def
+} ArmLIR;
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+
+/* Utility macros to traverse the LIR/ArmLIR list */
+#define NEXT_LIR(lir) ((ArmLIR *) lir->generic.next)
+#define PREV_LIR(lir) ((ArmLIR *) lir->generic.prev)
+
+#define NEXT_LIR_LVALUE(lir) (lir)->generic.next
+#define PREV_LIR_LVALUE(lir) (lir)->generic.prev
+
+#define CHAIN_CELL_OFFSET_TAG   0xcdab
+
+#define CHAIN_CELL_NORMAL_SIZE 12
+#define CHAIN_CELL_PREDICTED_SIZE 16
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H_
diff --git a/vm/compiler/codegen/arm/ArmRallocUtil.cpp b/vm/compiler/codegen/arm/ArmRallocUtil.cpp
new file mode 100644
index 0000000..3a5afa2
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmRallocUtil.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file contains Arm-specific register alloction support.
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include "compiler/codegen/Ralloc.h"
+
+/*
+ * Register usage for 16-bit Thumb systems:
+ *     r0-r3: Temp/argument
+ *     lr(r14):      Temp for translations, return address for handlers
+ *     rSELF(r6):    Pointer to Thread
+ *     rFP(r5):      Dalvik frame pointer
+ *     r4, r7:       Temp for translations
+ *     r8, r9, r10:   Temp preserved across C calls
+ *     r11, ip(r12):  Temp not preserved across C calls
+ *
+ * Register usage for 32-bit Thumb systems:
+ *     r0-r3: Temp/argument
+ *     lr(r14):      Temp for translations, return address for handlers
+ *     rSELF(r6):    Pointer to Thread
+ *     rFP(r5):      Dalvik frame pointer
+ *     r4, r7:       Temp for translations
+ *     r8, r9, r10   Temp preserved across C calls
+ *     r11, ip(r12):      Temp not preserved across C calls
+ *     fp0-fp15:     Hot temps, not preserved across C calls
+ *     fp16-fp31:    Promotion pool
+ *
+ */
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit)
+{
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerClobber(cUnit, r2);
+    dvmCompilerClobber(cUnit, r3);
+    dvmCompilerClobber(cUnit, r9); // Need to do this?, be conservative
+    dvmCompilerClobber(cUnit, r11);
+    dvmCompilerClobber(cUnit, r12);
+    dvmCompilerClobber(cUnit, r14lr);
+}
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit)
+{
+    //TUNING: reduce the set of regs used by handlers.  Only a few need lots.
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, r7);
+    dvmCompilerClobber(cUnit, r8);
+    dvmCompilerClobber(cUnit, r9);
+    dvmCompilerClobber(cUnit, r10);
+}
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerMarkInUse(cUnit, r0);
+    dvmCompilerMarkInUse(cUnit, r1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    res.lowReg = r2;
+    res.highReg = r3;
+    dvmCompilerClobber(cUnit, r2);
+    dvmCompilerClobber(cUnit, r3);
+    dvmCompilerMarkInUse(cUnit, r2);
+    dvmCompilerMarkInUse(cUnit, r3);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    dvmCompilerClobber(cUnit, r0);
+    dvmCompilerMarkInUse(cUnit, r0);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    res.lowReg = r1;
+    dvmCompilerClobber(cUnit, r1);
+    dvmCompilerMarkInUse(cUnit, r1);
+    return res;
+}
diff --git a/vm/compiler/codegen/arm/Assemble.cpp b/vm/compiler/codegen/arm/Assemble.cpp
new file mode 100644
index 0000000..7ed2d47
--- /dev/null
+++ b/vm/compiler/codegen/arm/Assemble.cpp
@@ -0,0 +1,2977 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+
+#include "../../CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include <sys/mman.h>           /* for protection change */
+
+#define MAX_ASSEMBLER_RETRIES 10
+
+/*
+ * opcode: ArmOpcode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+                     k3, k3s, k3e, flags, name, fmt, size) \
+        {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+                    {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ *     0 -> operands[0] (dest)
+ *     1 -> operands[1] (src1)
+ *     2 -> operands[2] (src2)
+ *     3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ *     h -> 4-digit hex
+ *     d -> decimal
+ *     E -> decimal*4
+ *     F -> decimal*2
+ *     c -> branch condition (beq, bne, etc.)
+ *     t -> pc-relative target
+ *     u -> 1st half of bl[x] target
+ *     v -> 2nd half ob bl[x] target
+ *     R -> register list
+ *     s -> single precision floating point register
+ *     S -> double precision floating point register
+ *     m -> Thumb2 modified immediate
+ *     n -> complimented Thumb2 modified immediate
+ *     M -> Thumb2 16-bit zero-extended immediate
+ *     b -> 4-digit binary
+ *     B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
+ *     H -> operand shift
+ *
+ *  [!] escape.  To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum ArmOpcode from ArmLIR.h */
+ArmEncodingMap EncodingMap[kArmLast] = {
+    ENCODING_MAP(kArm16BitData,    0x0000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 1),
+    ENCODING_MAP(kThumbAdcRR,        0x4140,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
+                 "adcs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRI3,      0x1c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbAddRI8,       0x3000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+                 "adds", "r!0d, r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbAddRRR,       0x1800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, r!2d", 1),
+    ENCODING_MAP(kThumbAddRRLH,     0x4440,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRHL,     0x4480,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddRRHH,     0x44c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+                 "add", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAddPcRel,    0xa000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH,
+                 "add", "r!0d, pc, #!1E", 1),
+    ENCODING_MAP(kThumbAddSpRel,    0xa800,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "add", "r!0d, sp, #!2E", 1),
+    ENCODING_MAP(kThumbAddSpI7,      0xb000,
+                 kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "add", "sp, #!0d*4", 1),
+    ENCODING_MAP(kThumbAndRR,        0x4000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "ands", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbAsrRRI5,      0x1000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "asrs", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbAsrRR,        0x4100,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "asrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbBCond,        0xd000,
+                 kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+                 "b!1c", "!0t", 1),
+    ENCODING_MAP(kThumbBUncond,      0xe000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+                 "b", "!0t", 1),
+    ENCODING_MAP(kThumbBicRR,        0x4380,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "bics", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbBkpt,          0xbe00,
+                 kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "bkpt", "!0d", 1),
+    ENCODING_MAP(kThumbBlx1,         0xf000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "blx_1", "!0u", 1),
+    ENCODING_MAP(kThumbBlx2,         0xe800,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "blx_2", "!0v", 1),
+    ENCODING_MAP(kThumbBl1,          0xf000,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "bl_1", "!0u", 1),
+    ENCODING_MAP(kThumbBl2,          0xf800,
+                 kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "bl_2", "!0v", 1),
+    ENCODING_MAP(kThumbBlxR,         0x4780,
+                 kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
+                 "blx", "r!0d", 1),
+    ENCODING_MAP(kThumbBx,            0x4700,
+                 kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "bx", "r!0d", 1),
+    ENCODING_MAP(kThumbCmnRR,        0x42c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmn", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpRI8,       0x2800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+                 "cmp", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbCmpRR,        0x4280,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpLH,        0x4540,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpHL,        0x4580,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbCmpHH,        0x45c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbEorRR,        0x4040,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "eors", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbLdmia,         0xc800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+                 "ldmia", "r!0d!!, <!1R>", 1),
+    ENCODING_MAP(kThumbLdrRRI5,      0x6800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, #!2E]", 1),
+    ENCODING_MAP(kThumbLdrRRR,       0x5800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrPcRel,    0x4800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
+                 | IS_LOAD, "ldr", "r!0d, [pc, #!1E]", 1),
+    ENCODING_MAP(kThumbLdrSpRel,    0x9800,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
+                 | IS_LOAD, "ldr", "r!0d, [sp, #!2E]", 1),
+    ENCODING_MAP(kThumbLdrbRRI5,     0x7800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, #2d]", 1),
+    ENCODING_MAP(kThumbLdrbRRR,      0x5c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrhRRI5,     0x8800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, #!2F]", 1),
+    ENCODING_MAP(kThumbLdrhRRR,      0x5a00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrsbRRR,     0x5600,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLdrshRRR,     0x5e00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbLslRRI5,      0x0000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "lsls", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbLslRR,        0x4080,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "lsls", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbLsrRRI5,      0x0800,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "lsrs", "r!0d, r!1d, #!2d", 1),
+    ENCODING_MAP(kThumbLsrRR,        0x40c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "lsrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovImm,       0x2000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
+                 "movs", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbMovRR,        0x1c00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "movs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_H2H,    0x46c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_H2L,    0x4640,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMovRR_L2H,    0x4680,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMul,           0x4340,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "muls", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbMvn,           0x43c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "mvns", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbNeg,           0x4240,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "negs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbOrr,           0x4300,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "orrs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbPop,           0xbc00,
+                 kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+                 | IS_LOAD, "pop", "<!0R>", 1),
+    ENCODING_MAP(kThumbPush,          0xb400,
+                 kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+                 | IS_STORE, "push", "<!0R>", 1),
+    ENCODING_MAP(kThumbRorRR,        0x41c0,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+                 "rors", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbSbc,           0x4180,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
+                 "sbcs", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumbStmia,         0xc000,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+                 "stmia", "r!0d!!, <!1R>", 1),
+    ENCODING_MAP(kThumbStrRRI5,      0x6000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "str", "r!0d, [r!1d, #!2E]", 1),
+    ENCODING_MAP(kThumbStrRRR,       0x5000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "str", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbStrSpRel,    0x9000,
+                 kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
+                 | IS_STORE, "str", "r!0d, [sp, #!2E]", 1),
+    ENCODING_MAP(kThumbStrbRRI5,     0x7000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strb", "r!0d, [r!1d, #!2d]", 1),
+    ENCODING_MAP(kThumbStrbRRR,      0x5400,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "strb", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbStrhRRI5,     0x8000,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strh", "r!0d, [r!1d, #!2F]", 1),
+    ENCODING_MAP(kThumbStrhRRR,      0x5200,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+                 "strh", "r!0d, [r!1d, r!2d]", 1),
+    ENCODING_MAP(kThumbSubRRI3,      0x1e00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, #!2d]", 1),
+    ENCODING_MAP(kThumbSubRI8,       0x3800,
+                 kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+                 "subs", "r!0d, #!1d", 1),
+    ENCODING_MAP(kThumbSubRRR,       0x1a00,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, r!2d", 1),
+    ENCODING_MAP(kThumbSubSpI7,      0xb080,
+                 kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+                 "sub", "sp, #!0d", 1),
+    ENCODING_MAP(kThumbSwi,           0xdf00,
+                 kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,                       kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+                 "swi", "!0d", 1),
+    ENCODING_MAP(kThumbTst,           0x4200,
+                 kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
+                 "tst", "r!0d, r!1d", 1),
+    ENCODING_MAP(kThumb2Vldrs,       0xed900a00,
+                 kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "vldr", "!0s, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vldrd,       0xed900b00,
+                 kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "vldr", "!0S, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vmuls,        0xee200a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vmuls", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vmuld,        0xee200b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vmuld", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vstrs,       0xed800a00,
+                 kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "vstr", "!0s, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vstrd,       0xed800b00,
+                 kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "vstr", "!0S, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Vsubs,        0xee300a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vsub", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vsubd,        0xee300b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vsub", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vadds,        0xee300a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vadd", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vaddd,        0xee300b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vadd", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2Vdivs,        0xee800a00,
+                 kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vdivs", "!0s, !1s, !2s", 2),
+    ENCODING_MAP(kThumb2Vdivd,        0xee800b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "vdivd", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2VmlaF64,     0xee000b00,
+                 kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE012,
+                 "vmla", "!0S, !1S, !2S", 2),
+    ENCODING_MAP(kThumb2VcvtIF,       0xeeb80ac0,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2VcvtID,       0xeeb80bc0,
+                 kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f64", "!0S, !1s", 2),
+    ENCODING_MAP(kThumb2VcvtFI,       0xeebd0ac0,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.s32.f32 ", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2VcvtDI,       0xeebd0bc0,
+                 kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.s32.f64 ", "!0s, !1S", 2),
+    ENCODING_MAP(kThumb2VcvtFd,       0xeeb70ac0,
+                 kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f64.f32 ", "!0S, !1s", 2),
+    ENCODING_MAP(kThumb2VcvtDF,       0xeeb70bc0,
+                 kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f32.f64 ", "!0s, !1S", 2),
+    ENCODING_MAP(kThumb2VcvtF64S32,   0xeeb80bc0,
+                 kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f64.s32 ", "!0S, !1s", 2),
+    ENCODING_MAP(kThumb2VcvtF64U32,   0xeeb80b40,
+                 kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vcvt.f64.u32 ", "!0S, !1s", 2),
+    ENCODING_MAP(kThumb2Vsqrts,       0xeeb10ac0,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vsqrt.f32 ", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vsqrtd,       0xeeb10bc0,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vsqrt.f64 ", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "mov", "r!0d, #!1m", 2),
+    ENCODING_MAP(kThumb2MovImm16,       0xf2400000,
+                 kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "mov", "r!0d, #!1M", 2),
+    ENCODING_MAP(kThumb2StrRRI12,       0xf8c00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "str", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2LdrRRI12,       0xf8d00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2StrRRI8Predec,       0xf8400c00,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "str", "r!0d, [r!1d, #-!2d]", 2),
+    ENCODING_MAP(kThumb2LdrRRI8Predec,       0xf8500c00,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, #-!2d]", 2),
+    ENCODING_MAP(kThumb2Cbnz,       0xb900, /* Note: does not affect flags */
+                 kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH,
+                 "cbnz", "r!0d,!1t", 1),
+    ENCODING_MAP(kThumb2Cbz,       0xb100, /* Note: does not affect flags */
+                 kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH,
+                 "cbz", "r!0d,!1t", 1),
+    ENCODING_MAP(kThumb2AddRRI12,       0xf2000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+                 "add", "r!0d,r!1d,#!2d", 2),
+    ENCODING_MAP(kThumb2MovRR,       0xea4f0000, /* no setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov", "r!0d, r!1d", 2),
+    ENCODING_MAP(kThumb2Vmovs,       0xeeb00a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f32 ", " !0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vmovd,       0xeeb00b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f64 ", " !0S, !1S", 2),
+    ENCODING_MAP(kThumb2Ldmia,         0xe8900000,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+                 "ldmia", "r!0d!!, <!1R>", 2),
+    ENCODING_MAP(kThumb2Stmia,         0xe8800000,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
+                 "stmia", "r!0d!!, <!1R>", 2),
+    ENCODING_MAP(kThumb2AddRRR,  0xeb100000, /* setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2SubRRR,       0xebb00000, /* setflags enconding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2SbcRRR,       0xeb700000, /* setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
+                 "sbcs", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2CmpRR,       0xebb00f00,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+                 "cmp", "r!0d, r!1d", 2),
+    ENCODING_MAP(kThumb2SubRRI12,       0xf2a00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+                 "sub", "r!0d,r!1d,#!2d", 2),
+    ENCODING_MAP(kThumb2MvnImmShift,  0xf06f0000, /* no setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "mvn", "r!0d, #!1n", 2),
+    ENCODING_MAP(kThumb2Sel,       0xfaa0f080,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
+                 "sel", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2Ubfx,       0xf3c00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+                 kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+                 "ubfx", "r!0d, r!1d, #!2d, #!3d", 2),
+    ENCODING_MAP(kThumb2Sbfx,       0xf3400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+                 kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+                 "sbfx", "r!0d, r!1d, #!2d, #!3d", 2),
+    ENCODING_MAP(kThumb2LdrRRR,    0xf8500000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldr", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2LdrhRRR,    0xf8300000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2LdrshRRR,    0xf9300000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2LdrbRRR,    0xf8100000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2LdrsbRRR,    0xf9100000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+                 "ldrsb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2StrRRR,    0xf8400000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+                 "str", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2StrhRRR,    0xf8200000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+                 "strh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2StrbRRR,    0xf8000000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+                 "strb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+    ENCODING_MAP(kThumb2LdrhRRI12,       0xf8b00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrh", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2LdrshRRI12,       0xf9b00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrsh", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2LdrbRRI12,       0xf8900000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrb", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2LdrsbRRI12,       0xf9900000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrsb", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2StrhRRI12,       0xf8a00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strh", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2StrbRRI12,       0xf8800000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+                 "strb", "r!0d, [r!1d, #!2d]", 2),
+    ENCODING_MAP(kThumb2Pop,           0xe8bd0000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+                 | IS_LOAD, "pop", "<!0R>", 2),
+    ENCODING_MAP(kThumb2Push,          0xe92d0000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+                 | IS_STORE, "push", "<!0R>", 2),
+    ENCODING_MAP(kThumb2CmpRI8, 0xf1b00f00,
+                 kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+                 "cmp", "r!0d, #!1m", 2),
+    ENCODING_MAP(kThumb2AdcRRR,  0xeb500000, /* setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "adcs", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2AndRRR,  0xea000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "and", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2BicRRR,  0xea200000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "bic", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2CmnRR,  0xeb000000,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "cmn", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2EorRRR,  0xea800000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "eor", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2MulRRR,  0xfb00f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2MnvRR,  0xea6f0000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "mvn", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2RsubRRI8,       0xf1d00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "rsb", "r!0d,r!1d,#!2m", 2),
+    ENCODING_MAP(kThumb2NegRR,       0xf1d00000, /* instance of rsub */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "neg", "r!0d,r!1d", 2),
+    ENCODING_MAP(kThumb2OrrRRR,  0xea400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+                 "orr", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumb2TstRR,       0xea100f00,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+                 "tst", "r!0d, r!1d, shift !2d", 2),
+    ENCODING_MAP(kThumb2LslRRR,  0xfa00f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "lsl", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2LsrRRR,  0xfa20f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "lsr", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2AsrRRR,  0xfa40f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "asr", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2RorRRR,  0xfa60f000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "ror", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2LslRRI5,  0xea4f0000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lsl", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2LsrRRI5,  0xea4f0010,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lsr", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2AsrRRI5,  0xea4f0020,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "asr", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2RorRRI5,  0xea4f0030,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "ror", "r!0d, r!1d, #!2d", 2),
+    ENCODING_MAP(kThumb2BicRRI8,  0xf0200000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "bic", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AndRRI8,  0xf0000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "and", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2OrrRRI8,  0xf0400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "orr", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2EorRRI8,  0xf0800000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "eor", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AddRRI8,  0xf1100000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "adds", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2AdcRRI8,  0xf1500000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+                 "adcs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2SubRRI8,  0xf1b00000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+                 "subs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2SbcRRI8,  0xf1700000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+                 "sbcs", "r!0d, r!1d, #!2m", 2),
+    ENCODING_MAP(kThumb2It,  0xbf00,
+                 kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
+                 "it:!1b", "!0c", 1),
+    ENCODING_MAP(kThumb2Fmstat,  0xeef1fa10,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
+                 "fmstat", "", 2),
+    ENCODING_MAP(kThumb2Vcmpd,        0xeeb40b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+                 "vcmp.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vcmps,        0xeeb40a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+                 "vcmp.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2LdrPcRel12,       0xf8df0000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+                 "ldr", "r!0d, [r15pc, #!1d]", 2),
+    ENCODING_MAP(kThumb2BCond,        0xf0008000,
+                 kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+                 "b!1c", "!0t", 2),
+    ENCODING_MAP(kThumb2Vmovd_RR,       0xeeb00b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vmovs_RR,       0xeeb00a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vmov.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Fmrs,       0xee100a10,
+                 kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "fmrs", "r!0d, !1s", 2),
+    ENCODING_MAP(kThumb2Fmsr,       0xee000a10,
+                 kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "fmsr", "!0s, r!1d", 2),
+    ENCODING_MAP(kThumb2Fmrrd,       0xec500b10,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
+                 "fmrrd", "r!0d, r!1d, !2S", 2),
+    ENCODING_MAP(kThumb2Fmdrr,       0xec400b10,
+                 kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "fmdrr", "!0S, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2Vabsd,       0xeeb00bc0,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vabs.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vabss,       0xeeb00ac0,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vabs.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vnegd,       0xeeb10b40,
+                 kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vneg.f64", "!0S, !1S", 2),
+    ENCODING_MAP(kThumb2Vnegs,       0xeeb10a40,
+                 kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "vneg.f32", "!0s, !1s", 2),
+    ENCODING_MAP(kThumb2Vmovs_IMM8,       0xeeb00a00,
+                 kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "vmov.f32", "!0s, #0x!1h", 2),
+    ENCODING_MAP(kThumb2Vmovd_IMM8,       0xeeb00b00,
+                 kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "vmov.f64", "!0S, #0x!1h", 2),
+    ENCODING_MAP(kThumb2Mla,  0xfb000000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 15, 12,
+                 IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+                 "mla", "r!0d, r!1d, r!2d, r!3d", 2),
+    ENCODING_MAP(kThumb2MlsRRRR,  0xfb000010,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 15, 12,
+                 IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+                 "mls", "r!0d, r!1d, r!2d, r!3d", 2),
+    ENCODING_MAP(kThumb2Umull,  0xfba00000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+                 kFmtBitBlt, 3, 0,
+                 IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+                 "umull", "r!0d, r!1d, r!2d, r!3d", 2),
+    ENCODING_MAP(kThumb2SdivRRR,  0xfb90f0f0,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sdiv", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2UdivRRR,  0xfbb0f0f0,
+                 kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0,
+                 kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_DEF0 | REG_USE1 | REG_USE2,
+                 "udiv", "r!0d, r!1d, r!2d", 2),
+    ENCODING_MAP(kThumb2Ldrex,       0xe8500f00,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+                 "ldrex", "r!0d, [r!1d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Strex,       0xe8400000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+                 kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
+                 "strex", "r!0d,r!1d, [r!2d, #!2E]", 2),
+    ENCODING_MAP(kThumb2Clrex,       0xf3bf8f2f,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "clrex", "", 2),
+    ENCODING_MAP(kThumb2Bfi,         0xf3600000,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
+                 kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+                 "bfi", "r!0d,r!1d,#!2d,#!3d", 2),
+    ENCODING_MAP(kThumb2Bfc,         0xf36f0000,
+                 kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
+                 "bfc", "r!0d,#!1d,#!2d", 2),
+    ENCODING_MAP(kThumb2Dmb,         0xf3bf8f50,
+                 kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP,
+                 "dmb","#!0B",2),
+    ENCODING_MAP(kThumb2LdrPcReln12,       0xf85f0000,
+                 kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1,
+                 IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+                 "ldr", "r!0d, [r15pc, -#!1d]", 2),
+    ENCODING_MAP(kThumb2RsbRRR,  0xebd00000, /* setflags encoding */
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtShift, -1, -1,
+                 IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+                 "rsb", "r!0d, r!1d, r!2d!3H", 2),
+    ENCODING_MAP(kThumbUndefined,       0xde00,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "undefined", "", 1),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5FP is not updated often, it is less likely to
+ * generate unnecessary stall cycles.
+ */
+#define PADDING_MOV_R5_R5               0x1C2D
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+/* Write the numbers in the constant and class pool to the output stream */
+static void installLiteralPools(CompilationUnit *cUnit)
+{
+    int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+    /* Install number of class pointer literals */
+    *dataPtr++ = cUnit->numClassPointers;
+    ArmLIR *dataLIR = (ArmLIR *) cUnit->classPointerList;
+    while (dataLIR) {
+        /*
+         * Install the callsiteinfo pointers into the cells for now. They will
+         * be converted into real pointers in dvmJitInstallClassObjectPointers.
+         */
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+    dataLIR = (ArmLIR *) cUnit->literalList;
+    while (dataLIR) {
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+}
+
+/*
+ * Assemble the LIR into binary instruction format.  Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.  In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+static AssemblerStatus assembleInstructions(CompilationUnit *cUnit,
+                                            intptr_t startAddr)
+{
+    short *bufferAddr = (short *) cUnit->codeBuffer;
+    ArmLIR *lir;
+
+    for (lir = (ArmLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
+        if (lir->opcode < 0) {
+            if ((lir->opcode == kArmPseudoPseudoAlign4) &&
+                /* 1 means padding is needed */
+                (lir->operands[0] == 1)) {
+                *bufferAddr++ = PADDING_MOV_R5_R5;
+            }
+            continue;
+        }
+
+        if (lir->flags.isNop) {
+            continue;
+        }
+
+        if (lir->opcode == kThumbLdrPcRel ||
+            lir->opcode == kThumb2LdrPcRel12 ||
+            lir->opcode == kThumbAddPcRel ||
+            ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
+            ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
+            ArmLIR *lirTarget = (ArmLIR *) lir->generic.target;
+            intptr_t pc = (lir->generic.offset + 4) & ~3;
+            intptr_t target = lirTarget->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiples of 4: %d", delta);
+                dvmCompilerAbort(cUnit);
+            }
+            if ((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumb2LdrPcRel12@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            } else if (delta > 1020) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumbLdrPcRel@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            }
+            if ((lir->opcode == kThumb2Vldrs) || (lir->opcode == kThumb2Vldrd)) {
+                lir->operands[2] = delta >> 2;
+            } else {
+                lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ?
+                                    delta : delta >> 2;
+            }
+        } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta > 126 || delta < 0) {
+                /* Convert to cmp rx,#0 / b[eq/ne] tgt pair */
+                ArmLIR *newInst =
+                    (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
+                /* Make new branch instruction and insert after */
+                newInst->opcode = kThumbBCond;
+                newInst->operands[0] = 0;
+                newInst->operands[1] = (lir->opcode == kThumb2Cbz) ?
+                                        kArmCondEq : kArmCondNe;
+                newInst->generic.target = lir->generic.target;
+                dvmCompilerSetupResourceMasks(newInst);
+                dvmCompilerInsertLIRAfter((LIR *)lir, (LIR *)newInst);
+                /* Convert the cb[n]z to a cmp rx, #0 ] */
+                lir->opcode = kThumbCmpRI8;
+                /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+                lir->operands[1] = 0;
+                lir->generic.target = 0;
+                dvmCompilerSetupResourceMasks(lir);
+                if (cUnit->printMe) {
+                    ALOGD("kThumb2Cbnz/kThumb2Cbz@%x: delta=%d",
+                         lir->generic.offset, delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryAll;
+            } else {
+                lir->operands[1] = delta >> 1;
+            }
+        } else if (lir->opcode == kThumbBCond ||
+                   lir->opcode == kThumb2BCond) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+                if (cUnit->printMe) {
+                    ALOGD("kThumbBCond@%x: delta=%d", lir->generic.offset,
+                         delta);
+                    dvmCompilerCodegenDump(cUnit);
+                }
+                return kRetryHalve;
+            }
+            lir->operands[0] = delta >> 1;
+        } else if (lir->opcode == kThumbBUncond) {
+            ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta > 2046 || delta < -2048) {
+                ALOGE("Unconditional branch distance out of range: %d", delta);
+                dvmCompilerAbort(cUnit);
+            }
+            lir->operands[0] = delta >> 1;
+        } else if (lir->opcode == kThumbBlx1) {
+            assert(NEXT_LIR(lir)->opcode == kThumbBlx2);
+            /* curPC is Thumb */
+            intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
+            intptr_t target = lir->operands[1];
+
+            /* Match bit[1] in target with base */
+            if (curPC & 0x2) {
+                target |= 0x2;
+            }
+            int delta = target - curPC;
+            assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+            lir->operands[0] = (delta >> 12) & 0x7ff;
+            NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+        } else if (lir->opcode == kThumbBl1) {
+            assert(NEXT_LIR(lir)->opcode == kThumbBl2);
+            /* Both curPC and target are Thumb */
+            intptr_t curPC = startAddr + lir->generic.offset + 4;
+            intptr_t target = lir->operands[1];
+
+            int delta = target - curPC;
+            assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+            lir->operands[0] = (delta >> 12) & 0x7ff;
+            NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+        }
+
+        ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
+        u4 bits = encoder->skeleton;
+        int i;
+        for (i = 0; i < 4; i++) {
+            u4 operand;
+            u4 value;
+            operand = lir->operands[i];
+            switch(encoder->fieldLoc[i].kind) {
+                case kFmtUnused:
+                    break;
+                case kFmtFPImm:
+                    value = ((operand & 0xF0) >> 4) << encoder->fieldLoc[i].end;
+                    value |= (operand & 0x0F) << encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                case kFmtBrOffset:
+                    value = ((operand  & 0x80000) >> 19) << 26;
+                    value |= ((operand & 0x40000) >> 18) << 11;
+                    value |= ((operand & 0x20000) >> 17) << 13;
+                    value |= ((operand & 0x1f800) >> 11) << 16;
+                    value |= (operand  & 0x007ff);
+                    bits |= value;
+                    break;
+                case kFmtShift5:
+                    value = ((operand & 0x1c) >> 2) << 12;
+                    value |= (operand & 0x03) << 6;
+                    bits |= value;
+                    break;
+                case kFmtShift:
+                    value = ((operand & 0x70) >> 4) << 12;
+                    value |= (operand & 0x0f) << 4;
+                    bits |= value;
+                    break;
+                case kFmtBWidth:
+                    value = operand - 1;
+                    bits |= value;
+                    break;
+                case kFmtLsb:
+                    value = ((operand & 0x1c) >> 2) << 12;
+                    value |= (operand & 0x03) << 6;
+                    bits |= value;
+                    break;
+                case kFmtImm6:
+                    value = ((operand & 0x20) >> 5) << 9;
+                    value |= (operand & 0x1f) << 3;
+                    bits |= value;
+                    break;
+                case kFmtBitBlt:
+                    value = (operand << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                case kFmtDfp: {
+                    assert(DOUBLEREG(operand));
+                    assert((operand & 0x1) == 0);
+                    int regName = (operand & FP_REG_MASK) >> 1;
+                    /* Snag the 1-bit slice and position it */
+                    value = ((regName & 0x10) >> 4) <<
+                            encoder->fieldLoc[i].end;
+                    /* Extract and position the 4-bit slice */
+                    value |= (regName & 0x0f) <<
+                            encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                }
+                case kFmtSfp:
+                    assert(SINGLEREG(operand));
+                    /* Snag the 1-bit slice and position it */
+                    value = (operand & 0x1) <<
+                            encoder->fieldLoc[i].end;
+                    /* Extract and position the 4-bit slice */
+                    value |= ((operand & 0x1e) >> 1) <<
+                            encoder->fieldLoc[i].start;
+                    bits |= value;
+                    break;
+                case kFmtImm12:
+                case kFmtModImm:
+                    value = ((operand & 0x800) >> 11) << 26;
+                    value |= ((operand & 0x700) >> 8) << 12;
+                    value |= operand & 0x0ff;
+                    bits |= value;
+                    break;
+                case kFmtImm16:
+                    value = ((operand & 0x0800) >> 11) << 26;
+                    value |= ((operand & 0xf000) >> 12) << 16;
+                    value |= ((operand & 0x0700) >> 8) << 12;
+                    value |= operand & 0x0ff;
+                    bits |= value;
+                    break;
+                default:
+                    assert(0);
+            }
+        }
+        if (encoder->size == 2) {
+            *bufferAddr++ = (bits >> 16) & 0xffff;
+        }
+        *bufferAddr++ = bits & 0xffff;
+    }
+    return kSuccess;
+}
+
+static int assignLiteralOffsetCommon(LIR *lir, int offset)
+{
+    for (;lir != NULL; lir = lir->next) {
+        lir->offset = offset;
+        offset += 4;
+    }
+    return offset;
+}
+
+/* Determine the offset of each literal field */
+static int assignLiteralOffset(CompilationUnit *cUnit, int offset)
+{
+    /* Reserved for the size field of class pointer pool */
+    offset += 4;
+    offset = assignLiteralOffsetCommon(cUnit->classPointerList, offset);
+    offset = assignLiteralOffsetCommon(cUnit->literalList, offset);
+    return offset;
+}
+
+/*
+ * Translation layout in the code cache.  Note that the codeAddress pointer
+ * in JitTable will point directly to the code body (field codeAddress).  The
+ * chain cell offset codeAddress - 2, and the address of the trace profile
+ * counter is at codeAddress - 6.
+ *
+ *      +----------------------------+
+ *      | Trace Profile Counter addr |  -> 4 bytes (PROF_COUNTER_ADDR_SIZE)
+ *      +----------------------------+
+ *   +--| Offset to chain cell counts|  -> 2 bytes (CHAIN_CELL_OFFSET_SIZE)
+ *   |  +----------------------------+
+ *   |  | Trace profile code         |  <- entry point when profiling
+ *   |  .  -   -   -   -   -   -   - .
+ *   |  | Code body                  |  <- entry point when not profiling
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Chaining Cells             |  -> 12/16 bytes, 4 byte aligned
+ *   |  .                            .
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Gap for large switch stmt  |  -> # cases >= MAX_CHAINED_SWITCH_CASES
+ *   |  +----------------------------+
+ *   +->| Chaining cell counts       |  -> 8 bytes, chain cell counts by type
+ *      +----------------------------+
+ *      | Trace description          |  -> variable sized
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | # Class pointer pool size  |  -> 4 bytes
+ *      +----------------------------+
+ *      | Class pointer pool         |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | Literal pool               |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *
+ */
+
+#define PROF_COUNTER_ADDR_SIZE 4
+#define CHAIN_CELL_OFFSET_SIZE 2
+
+/*
+ * Utility functions to navigate various parts in a trace. If we change the
+ * layout/offset in the future, we just modify these functions and we don't need
+ * to propagate the changes to all the use cases.
+ */
+static inline char *getTraceBase(const JitEntry *p)
+{
+    return (char*)p->codeAddress -
+        (PROF_COUNTER_ADDR_SIZE + CHAIN_CELL_OFFSET_SIZE +
+         (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
+}
+
+/* Handy function to retrieve the profile count */
+static inline JitTraceCounter_t getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return 0;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    return **p;
+}
+
+/* Handy function to reset the profile count */
+static inline void resetProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    **p = 0;
+}
+
+/* Get the pointer of the chain cell count */
+static inline ChainCellCounts* getChainCellCountsPointer(const char *base)
+{
+    /* 4 is the size of the profile count */
+    u2 *chainCellOffsetP = (u2 *) (base + PROF_COUNTER_ADDR_SIZE);
+    u2 chainCellOffset = *chainCellOffsetP;
+    return (ChainCellCounts *) ((char *) chainCellOffsetP + chainCellOffset);
+}
+
+/* Get the size of all chaining cells */
+static inline u4 getChainCellSize(const ChainCellCounts* pChainCellCounts)
+{
+    int cellSize = 0;
+    int i;
+
+    /* Get total count of chain cells */
+    for (i = 0; i < kChainingCellGap; i++) {
+        if (i != kChainingCellInvokePredicted) {
+            cellSize += pChainCellCounts->u.count[i] *
+                        (CHAIN_CELL_NORMAL_SIZE >> 2);
+        } else {
+            cellSize += pChainCellCounts->u.count[i] *
+                (CHAIN_CELL_PREDICTED_SIZE >> 2);
+        }
+    }
+    return cellSize;
+}
+
+/* Get the starting pointer of the trace description section */
+static JitTraceDescription* getTraceDescriptionPointer(const char *base)
+{
+    ChainCellCounts* pCellCounts = getChainCellCountsPointer(base);
+    return (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+}
+
+/* Get the size of a trace description */
+static int getTraceDescriptionSize(const JitTraceDescription *desc)
+{
+    int runCount;
+    /* Trace end is always of non-meta type (ie isCode == true) */
+    for (runCount = 0; ; runCount++) {
+        if (desc->trace[runCount].isCode &&
+            desc->trace[runCount].info.frag.runEnd)
+           break;
+    }
+    return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+#if defined(SIGNATURE_BREAKPOINT)
+/* Inspect the assembled instruction stream to find potential matches */
+static void matchSignatureBreakpoint(const CompilationUnit *cUnit,
+                                     unsigned int size)
+{
+    unsigned int i, j;
+    u4 *ptr = (u4 *) cUnit->codeBuffer;
+
+    for (i = 0; i < size - gDvmJit.signatureBreakpointSize + 1; i++) {
+        if (ptr[i] == gDvmJit.signatureBreakpoint[0]) {
+            for (j = 1; j < gDvmJit.signatureBreakpointSize; j++) {
+                if (ptr[i+j] != gDvmJit.signatureBreakpoint[j]) {
+                    break;
+                }
+            }
+            if (j == gDvmJit.signatureBreakpointSize) {
+                ALOGD("Signature match starting from offset %#x (%d words)",
+                     i*4, gDvmJit.signatureBreakpointSize);
+                int descSize = getTraceDescriptionSize(cUnit->traceDesc);
+                JitTraceDescription *newCopy =
+                    (JitTraceDescription *) malloc(descSize);
+                memcpy(newCopy, cUnit->traceDesc, descSize);
+                dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+                break;
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    ArmLIR *armLIR;
+    int offset = 0;
+    int i;
+    ChainCellCounts chainCellCounts;
+    int descSize = (cUnit->jitMode == kJitMethod) ?
+        0 : getTraceDescriptionSize(cUnit->traceDesc);
+    int chainingCellGap = 0;
+
+    info->instructionSet = cUnit->instructionSet;
+
+    /* Beginning offset needs to allow space for chain cell offset */
+    for (armLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         armLIR;
+         armLIR = NEXT_LIR(armLIR)) {
+        armLIR->generic.offset = offset;
+        if (armLIR->opcode >= 0 && !armLIR->flags.isNop) {
+            armLIR->flags.size = EncodingMap[armLIR->opcode].size * 2;
+            offset += armLIR->flags.size;
+        } else if (armLIR->opcode == kArmPseudoPseudoAlign4) {
+            if (offset & 0x2) {
+                offset += 2;
+                armLIR->operands[0] = 1;
+            } else {
+                armLIR->operands[0] = 0;
+            }
+        }
+        /* Pseudo opcodes don't consume space */
+    }
+
+    /* Const values have to be word aligned */
+    offset = (offset + 3) & ~3;
+
+    u4 chainCellOffset = offset;
+    ArmLIR *chainCellOffsetLIR = NULL;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /*
+         * Get the gap (# of u4) between the offset of chaining cell count and
+         * the bottom of real chaining cells. If the translation has chaining
+         * cells, the gap is guaranteed to be multiples of 4.
+         */
+        chainingCellGap = (offset - cUnit->chainingCellBottom->offset) >> 2;
+
+        /* Add space for chain cell counts & trace description */
+        chainCellOffsetLIR = (ArmLIR *) cUnit->chainCellOffsetLIR;
+        assert(chainCellOffsetLIR);
+        assert(chainCellOffset < 0x10000);
+        assert(chainCellOffsetLIR->opcode == kArm16BitData &&
+               chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+        /*
+         * Adjust the CHAIN_CELL_OFFSET_TAG LIR's offset to remove the
+         * space occupied by the pointer to the trace profiling counter.
+         */
+        chainCellOffsetLIR->operands[0] = chainCellOffset - 4;
+
+        offset += sizeof(chainCellCounts) + descSize;
+
+        assert((offset & 0x3) == 0);  /* Should still be word aligned */
+    }
+
+    /* Set up offsets for literals */
+    cUnit->dataOffset = offset;
+
+    /*
+     * Assign each class pointer/constant an offset from the beginning of the
+     * compilation unit.
+     */
+    offset = assignLiteralOffset(cUnit, offset);
+
+    cUnit->totalSize = offset;
+
+    if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+        gDvmJit.codeCacheFull = true;
+        info->discardResult = true;
+        return;
+    }
+
+    /* Allocate enough space for the code block */
+    cUnit->codeBuffer = (unsigned char *)dvmCompilerNew(chainCellOffset, true);
+    if (cUnit->codeBuffer == NULL) {
+        ALOGE("Code buffer allocation failure");
+        info->discardResult = true;
+        return;
+    }
+
+    /*
+     * Attempt to assemble the trace.  Note that assembleInstructions
+     * may rewrite the code sequence and request a retry.
+     */
+    cUnit->assemblerStatus = assembleInstructions(cUnit,
+          (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+    switch(cUnit->assemblerStatus) {
+        case kSuccess:
+            break;
+        case kRetryAll:
+            if (cUnit->assemblerRetries < MAX_ASSEMBLER_RETRIES) {
+                if (cUnit->jitMode != kJitMethod) {
+                    /* Restore pristine chain cell marker on retry */
+                    chainCellOffsetLIR->operands[0] = CHAIN_CELL_OFFSET_TAG;
+                }
+                return;
+            }
+            /* Too many retries - reset and try cutting the trace in half */
+            cUnit->assemblerRetries = 0;
+            cUnit->assemblerStatus = kRetryHalve;
+            return;
+        case kRetryHalve:
+            return;
+        default:
+             ALOGE("Unexpected assembler status: %d", cUnit->assemblerStatus);
+             dvmAbort();
+    }
+
+#if defined(SIGNATURE_BREAKPOINT)
+    if (info->discardResult == false && gDvmJit.signatureBreakpoint != NULL &&
+        chainCellOffset/4 >= gDvmJit.signatureBreakpointSize) {
+        matchSignatureBreakpoint(cUnit, chainCellOffset/4);
+    }
+#endif
+
+    /* Don't go all the way if the goal is just to get the verbose output */
+    if (info->discardResult) return;
+
+    /*
+     * The cache might disappear - acquire lock and check version
+     * Continue holding lock until translation cache update is complete.
+     * These actions are required here in the compiler thread because
+     * it is unaffected by suspend requests and doesn't know if a
+     * translation cache flush is in progress.
+     */
+    dvmLockMutex(&gDvmJit.compilerLock);
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        /* Cache changed - discard current translation */
+        info->discardResult = true;
+        info->codeAddress = NULL;
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return;
+    }
+
+    cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    gDvmJit.codeCacheByteUsed += offset;
+
+    UNPROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Install the code block */
+    memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset);
+    gDvmJit.numCompilations++;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /* Install the chaining cell counts */
+        for (i=0; i< kChainingCellGap; i++) {
+            chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+        }
+
+        /* Set the gap number in the chaining cell count structure */
+        chainCellCounts.u.count[kChainingCellGap] = chainingCellGap;
+
+        memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts,
+               sizeof(chainCellCounts));
+
+        /* Install the trace description */
+        memcpy((char*) cUnit->baseAddr + chainCellOffset +
+                       sizeof(chainCellCounts),
+               cUnit->traceDesc, descSize);
+    }
+
+    /* Write the literals directly into the code cache */
+    installLiteralPools(cUnit);
+
+    /* Flush dcache and invalidate the icache to maintain coherence */
+    dvmCompilerCacheFlush((long)cUnit->baseAddr,
+                          (long)((char *) cUnit->baseAddr + offset));
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Translation cache update complete - release lock */
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Record code entry point and instruction set */
+    info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+    /* If applicable, mark low bit to denote thumb */
+    if (info->instructionSet != DALVIK_JIT_ARM)
+        info->codeAddress = (char*)info->codeAddress + 1;
+    /* transfer the size of the profiling code */
+    info->profileCodeSize = cUnit->profileCodeSize;
+}
+
+/*
+ * Returns the skeleton bit pattern associated with an opcode.  All
+ * variable fields are zeroed.
+ */
+static u4 getSkeleton(ArmOpcode op)
+{
+    return EncodingMap[op].skeleton;
+}
+
+static u4 assembleChainingBranch(int branchOffset, bool thumbTarget)
+{
+    u4 thumb1, thumb2;
+
+    if (!thumbTarget) {
+        thumb1 =  (getSkeleton(kThumbBlx1) | ((branchOffset>>12) & 0x7ff));
+        thumb2 =  (getSkeleton(kThumbBlx2) | ((branchOffset>> 1) & 0x7ff));
+    } else if ((branchOffset < -2048) | (branchOffset > 2046)) {
+        thumb1 =  (getSkeleton(kThumbBl1) | ((branchOffset>>12) & 0x7ff));
+        thumb2 =  (getSkeleton(kThumbBl2) | ((branchOffset>> 1) & 0x7ff));
+    } else {
+        thumb1 =  (getSkeleton(kThumbBUncond) | ((branchOffset>> 1) & 0x7ff));
+        thumb2 =  getSkeleton(kThumbOrr);  /* nop -> or r0, r0 */
+    }
+
+    return thumb2<<16 | thumb1;
+}
+
+/*
+ * Perform translation chain operation.
+ * For ARM, we'll use a pair of thumb instructions to generate
+ * an unconditional chaining branch of up to 4MB in distance.
+ * Use a BL, because the generic "interpret" translation needs
+ * the link register to find the dalvik pc of teh target.
+ *     111HHooooooooooo
+ * Where HH is 10 for the 1st inst, and 11 for the second and
+ * the "o" field is each instruction's 11-bit contribution to the
+ * 22-bit branch offset.
+ * If the target is nearby, use a single-instruction bl.
+ * If one or more threads is suspended, don't chain.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+    int baseAddr = (u4) branchAddr + 4;
+    int branchOffset = (int) tgtAddr - baseAddr;
+    u4 newInst;
+    bool thumbTarget;
+
+    /*
+     * Only chain translations when there is no urge to ask all threads to
+     * suspend themselves via the interpreter.
+     */
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false)) {
+        assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2)));
+
+        gDvmJit.translationChains++;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: chaining %#x to %#x",
+                 (int) branchAddr, (int) tgtAddr & -2));
+
+        /*
+         * NOTE: normally, all translations are Thumb[2] mode, with
+         * a single exception: the default TEMPLATE_INTERPRET
+         * pseudo-translation.  If the need ever arises to
+         * mix Arm & Thumb[2] translations, the following code should be
+         * generalized.
+         */
+        thumbTarget = (tgtAddr != dvmCompilerGetInterpretTemplate());
+
+        newInst = assembleChainingBranch(branchOffset, thumbTarget);
+
+        /*
+         * The second half-word instruction of the chaining cell must
+         * either be a nop (which represents initial state), or is the
+         * same exact branch halfword that we are trying to install.
+         */
+        assert( ((*branchAddr >> 16) == getSkeleton(kThumbOrr)) ||
+                ((*branchAddr >> 16) == (newInst >> 16)));
+
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        *branchAddr = newInst;
+        dvmCompilerCacheFlush((long)branchAddr, (long)branchAddr + 4);
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        gDvmJit.hasNewChain = true;
+    }
+
+    return tgtAddr;
+}
+
+#if !defined(WITH_SELF_VERIFICATION)
+/*
+ * Attempt to enqueue a work order to patch an inline cache for a predicted
+ * chaining cell for virtual/interface calls.
+ */
+static void inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
+                                    PredictedChainingCell *newContent)
+{
+    /*
+     * Make sure only one thread gets here since updating the cell (ie fast
+     * path and queueing the request (ie the queued path) have to be done
+     * in an atomic fashion.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    /* Fast path for uninitialized chaining cell */
+    if (cellAddr->clazz == NULL &&
+        cellAddr->branch == PREDICTED_CHAIN_BX_PAIR_INIT) {
+
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->method = newContent->method;
+        cellAddr->branch = newContent->branch;
+        /*
+         * The update order matters - make sure clazz is updated last since it
+         * will bring the uninitialized chaining cell to life.
+         */
+        android_atomic_release_store((int32_t)newContent->clazz,
+            (volatile int32_t *)(void *)&cellAddr->clazz);
+        dvmCompilerCacheFlush((intptr_t) cellAddr, (intptr_t) (cellAddr+1));
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchInit++;
+#endif
+    /* Check if this is a frequently missed clazz */
+    } else if (cellAddr->stagedClazz != newContent->clazz) {
+        /* Not proven to be frequent yet - build up the filter cache */
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->stagedClazz = newContent->clazz;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchRejected++;
+#endif
+    /*
+     * Different classes but same method implementation - it is safe to just
+     * patch the class value without the need to stop the world.
+     */
+    } else if (cellAddr->method == newContent->method) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->clazz = newContent->clazz;
+        /* No need to flush the cache here since the branch is not patched */
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchLockFree++;
+#endif
+    /*
+     * Cannot patch the chaining cell inline - queue it until the next safe
+     * point.
+     */
+    } else if (gDvmJit.compilerICPatchIndex < COMPILER_IC_PATCH_QUEUE_SIZE) {
+        int index = gDvmJit.compilerICPatchIndex++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+#endif
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Enqueue the new content for the chaining cell which will be appled in
+ *      next safe point.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+    newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+    goto done;
+#else
+    PredictedChainingCell newCell;
+    int baseAddr, branchOffset, tgtAddr;
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        goto done;
+    }
+    tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to method %s%s delayed",
+                 cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    baseAddr = (int) cell + 4;   // PC is cur_addr + 4
+    branchOffset = tgtAddr - baseAddr;
+
+    newCell.branch = assembleChainingBranch(branchOffset, true);
+    newCell.clazz = clazz;
+    newCell.method = method;
+    newCell.stagedClazz = NULL;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+#endif
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Patch the inline cache content based on the content passed from the work
+ * order.
+ */
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                 "patched",
+                 cellAddr,
+                 cellAddr->clazz->descriptor,
+                 cellContent->clazz->descriptor,
+                 cellContent->method->name));
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    /* Then synchronize the I/D cache */
+    dvmCompilerCacheFlush((long) minAddr, (long) (maxAddr+1));
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    gDvmJit.compilerICPatchIndex = 0;
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+
+/*
+ * Unchain a trace given the starting address of the translation
+ * in the code cache.  Refer to the diagram in dvmCompilerAssembleLIR.
+ * Returns the address following the last cell unchained.  Note that
+ * the incoming codeAddr is a thumb code address, and therefore has
+ * the low bit set.
+ */
+static u4* unchainSingle(JitEntry *trace)
+{
+    const char *base = getTraceBase(trace);
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    u4* pChainCells;
+    u4 newInst;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+
+    if (cellSize == 0)
+        return (u4 *) pChainCellCounts;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+                  pChainCellCounts->u.count[kChainingCellGap];
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        int elemSize = CHAIN_CELL_NORMAL_SIZE >> 2;  /* In 32-bit words */
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = CHAIN_CELL_PREDICTED_SIZE >> 2;
+        }
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            switch(i) {
+                case kChainingCellNormal:
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                case kChainingCellBackwardBranch:
+                    /*
+                     * Replace the 1st half-word of the cell with an
+                     * unconditional branch, leaving the 2nd half-word
+                     * untouched.  This avoids problems with a thread
+                     * that is suspended between the two halves when
+                     * this unchaining takes place.
+                     */
+                    newInst = *pChainCells;
+                    newInst &= 0xFFFF0000;
+                    newInst |= getSkeleton(kThumbBUncond); /* b offset is 0 */
+                    *pChainCells = newInst;
+                    break;
+                case kChainingCellInvokePredicted:
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+                default:
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGD("Jit Runtime: unchaining %#x", (int)pChainCells));
+            pChainCells += elemSize;  /* Advance by a fixed number of words */
+        }
+    }
+    return pChainCells;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    u4* lowAddress = NULL;
+    u4* highAddress = NULL;
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(LOGD("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (size_t i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress &&
+                (gDvmJit.pJitEntryTable[i].codeAddress !=
+                 dvmCompilerGetInterpretTemplate())) {
+                u4* lastAddress;
+                lastAddress = unchainSingle(&gDvmJit.pJitEntryTable[i]);
+                if (lowAddress == NULL ||
+                      (u4*)gDvmJit.pJitEntryTable[i].codeAddress <
+                      lowAddress)
+                    lowAddress = lastAddress;
+                if (lastAddress > highAddress)
+                    highAddress = lastAddress;
+            }
+        }
+        dvmCompilerCacheFlush((long)lowAddress, (long)highAddress);
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        gDvmJit.translationChains = 0;
+    }
+    gDvmJit.hasNewChain = false;
+}
+
+typedef struct jitProfileAddrToLine {
+    u4 lineNum;
+    u4 bytecodeOffset;
+} jitProfileAddrToLine;
+
+
+/* Callback function to track the bytecode offset/line number relationiship */
+static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum)
+{
+    jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt;
+
+    /* Best match so far for this offset */
+    if (addrToLine->bytecodeOffset >= bytecodeOffset) {
+        addrToLine->lineNum = lineNum;
+    }
+    return 0;
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+                            unsigned long sum)
+{
+    int idx;
+
+    if (p->codeAddress == NULL) {
+        if (!silent)
+            ALOGD("TRACEPROFILE NULL");
+        return 0;
+    }
+    if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+        if (!silent)
+            ALOGD("TRACEPROFILE INTERPRET_ONLY");
+        return 0;
+    }
+    JitTraceCounter_t count = getProfileCount(p);
+    if (reset) {
+        resetProfileCount(p);
+    }
+    if (silent) {
+        return count;
+    }
+    JitTraceDescription *desc = getTraceDescriptionPointer(getTraceBase(p));
+    const Method *method = desc->method;
+    char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+    jitProfileAddrToLine addrToLine = {0, desc->trace[0].info.frag.startOffset};
+
+    /*
+     * We may end up decoding the debug information for the same method
+     * multiple times, but the tradeoff is we don't need to allocate extra
+     * space to store the addr/line mapping. Since this is a debugging feature
+     * and done infrequently so the slower but simpler mechanism should work
+     * just fine.
+     */
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+                       dvmGetMethodCode(method),
+                       method->clazz->descriptor,
+                       method->prototype.protoIdx,
+                       method->accessFlags,
+                       addrToLineCb, NULL, &addrToLine);
+
+    ALOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+         (int) getTraceBase(p),
+         count,
+         ((float ) count) / sum * 100.0,
+         desc->trace[0].info.frag.startOffset,
+         desc->trace[0].info.frag.numInsts,
+         addrToLine.lineNum,
+         method->clazz->descriptor, method->name, methodDesc);
+    free(methodDesc);
+
+    /* Find the last fragment (ie runEnd is set) */
+    for (idx = 0;
+         desc->trace[idx].isCode && !desc->trace[idx].info.frag.runEnd;
+         idx++) {
+    }
+
+    /*
+     * runEnd must comes with a JitCodeDesc frag. If isCode is false it must
+     * be a meta info field (only used by callsite info for now).
+     */
+    if (!desc->trace[idx].isCode) {
+        const Method *method = (const Method *)
+            desc->trace[idx+JIT_TRACE_CUR_METHOD-1].info.meta;
+        char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+        /* Print the callee info in the trace */
+        ALOGD("    -> %s%s;%s", method->clazz->descriptor, method->name,
+             methodDesc);
+    }
+
+    return count;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry)
+{
+    const JitEntry *jitEntry = knownEntry ? knownEntry
+                                          : dvmJitFindEntry(pc, false);
+    if ((jitEntry == NULL) || (jitEntry->codeAddress == 0))
+        return NULL;
+
+    JitTraceDescription *desc =
+        getTraceDescriptionPointer(getTraceBase(jitEntry));
+
+    /* Now make a copy and return */
+    int descSize = getTraceDescriptionSize(desc);
+    JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+    memcpy(newCopy, desc, descSize);
+    return newCopy;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long sum = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Analyze the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            sum += dumpTraceProfile(&sortedEntries[i],
+                                       true /* silent */,
+                                       false /* reset */,
+                                       0);
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    if (sum == 0) {
+        sum = 1;
+    }
+
+    ALOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+    // How efficiently are we using code cache memory?  Bigger is better.
+    ALOGD("JIT: CodeCache efficiency -> %.2f",(float)sum / (float)gDvmJit.codeCacheByteUsed);
+
+    /* Dump the sorted entries. The count of each trace will be reset to 0. */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            dumpTraceProfile(&sortedEntries[i],
+                             false /* silent */,
+                             true /* reset */,
+                             sum);
+        }
+    }
+
+    for (i=0; i < gDvmJit.jitTableSize && i < 10; i++) {
+        /* Stip interpreter stubs */
+        if (sortedEntries[i].codeAddress == dvmCompilerGetInterpretTemplate()) {
+            continue;
+        }
+        JitTraceDescription* desc =
+            dvmCopyTraceDescriptor(NULL, &sortedEntries[i]);
+        if (desc) {
+            dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+                                   kWorkOrderTraceDebug, desc);
+        }
+    }
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+static void findClassPointersSingleTrace(char *base, void (*callback)(void *))
+{
+    unsigned int chainTypeIdx, chainIdx;
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    /* Scan the chaining cells */
+    if (cellSize) {
+        /* Locate the beginning of the chain cell region */
+        u4 *pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+            pChainCellCounts->u.count[kChainingCellGap];
+        /* The cells are sorted in order - walk through them */
+        for (chainTypeIdx = 0; chainTypeIdx < kChainingCellGap;
+             chainTypeIdx++) {
+            if (chainTypeIdx != kChainingCellInvokePredicted) {
+                /* In 32-bit words */
+                pChainCells += (CHAIN_CELL_NORMAL_SIZE >> 2) *
+                    pChainCellCounts->u.count[chainTypeIdx];
+                continue;
+            }
+            for (chainIdx = 0;
+                 chainIdx < pChainCellCounts->u.count[chainTypeIdx];
+                 chainIdx++) {
+                PredictedChainingCell *cell =
+                    (PredictedChainingCell *) pChainCells;
+                /*
+                 * Report the cell if it contains a sane class
+                 * pointer.
+                 */
+                if (cell->clazz != NULL &&
+                    cell->clazz !=
+                      (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ) {
+                    callback(&cell->clazz);
+                }
+                pChainCells += CHAIN_CELL_PREDICTED_SIZE >> 2;
+            }
+        }
+    }
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    int *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *classPointerP++;
+    for (; numClassPointers; numClassPointers--, classPointerP++) {
+        callback(classPointerP);
+    }
+}
+
+/*
+ * Scan class pointers in each translation and pass its address to the callback
+ * function. Currently such a pointers can be found in the pointer pool and the
+ * clazz field in the predicted chaining cells.
+ */
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    /* Handle the inflight compilation first */
+    if (gDvmJit.inflightBaseAddr)
+        findClassPointersSingleTrace((char *) gDvmJit.inflightBaseAddr,
+                                     callback);
+
+    if (gDvmJit.pJitEntryTable != NULL) {
+        unsigned int traceIdx;
+        dvmLockMutex(&gDvmJit.tableLock);
+        for (traceIdx = 0; traceIdx < gDvmJit.jitTableSize; traceIdx++) {
+            const JitEntry *entry = &gDvmJit.pJitEntryTable[traceIdx];
+            if (entry->dPC &&
+                !entry->u.info.isMethodEntry &&
+                entry->codeAddress &&
+                (entry->codeAddress != dvmCompilerGetInterpretTemplate())) {
+                char *base = getTraceBase(entry);
+                findClassPointersSingleTrace(base, callback);
+            }
+        }
+        dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+}
+
+/*
+ * Provide the final touch on the class object pointer pool to install the
+ * actual pointers. The thread has to be in the running state.
+ */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+    char *base = codeAddress - cUnit->headerSize -
+                 (cUnit->instructionSet == DALVIK_JIT_ARM ? 0 : 1);
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    intptr_t *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *(int *)classPointerP++;
+    intptr_t *startClassPointerP = classPointerP;
+
+    /*
+     * Change the thread state to VM_RUNNING so that GC won't be happening
+     * when the assembler looks up the class pointers. May suspend the current
+     * thread if there is a pending request before the state is actually
+     * changed to RUNNING.
+     */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_RUNNING);
+
+    /*
+     * Unprotecting the code cache will need to acquire the code cache
+     * protection lock first. Doing so after the state change may increase the
+     * time spent in the RUNNING state (which may delay the next GC request
+     * should there be contention on codeCacheProtectionLock). In practice
+     * this is probably not going to happen often since a GC is just served.
+     * More importantly, acquiring the lock before the state change will
+     * cause deadlock (b/4192964).
+     */
+    UNPROTECT_CODE_CACHE(startClassPointerP,
+                         numClassPointers * sizeof(intptr_t));
+#if defined(WITH_JIT_TUNING)
+    u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+    for (;numClassPointers; numClassPointers--) {
+        CallsiteInfo *callsiteInfo = (CallsiteInfo *) *classPointerP;
+        ClassObject *clazz = dvmFindClassNoInit(
+            callsiteInfo->classDescriptor, callsiteInfo->classLoader);
+        assert(!strcmp(clazz->descriptor, callsiteInfo->classDescriptor));
+        *classPointerP++ = (intptr_t) clazz;
+    }
+
+    /*
+     * Register the base address so that if GC kicks in after the thread state
+     * has been changed to VMWAIT and before the compiled code is registered
+     * in the JIT table, its content can be patched if class objects are
+     * moved.
+     */
+    gDvmJit.inflightBaseAddr = base;
+
+#if defined(WITH_JIT_TUNING)
+    u8 blockTime = dvmGetRelativeTimeUsec() - startTime;
+    gDvmJit.compilerThreadBlockGCTime += blockTime;
+    if (blockTime > gDvmJit.maxCompilerThreadBlockGCTime)
+        gDvmJit.maxCompilerThreadBlockGCTime = blockTime;
+    gDvmJit.numCompilerThreadBlockGC++;
+#endif
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(startClassPointerP, numClassPointers * sizeof(intptr_t));
+
+    /* Change the thread state back to VMWAIT */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_VMWAIT);
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * The following are used to keep compiled loads and stores from modifying
+ * memory during self verification mode.
+ *
+ * Stores do not modify memory. Instead, the address and value pair are stored
+ * into heapSpace. Addresses within heapSpace are unique. For accesses smaller
+ * than a word, the word containing the address is loaded first before being
+ * updated.
+ *
+ * Loads check heapSpace first and return data from there if an entry exists.
+ * Otherwise, data is loaded from memory as usual.
+ */
+
+/* Used to specify sizes of memory operations */
+enum {
+    kSVByte,
+    kSVSignedByte,
+    kSVHalfword,
+    kSVSignedHalfword,
+    kSVWord,
+    kSVDoubleword,
+    kSVVariable,
+};
+
+/* Load the value of a decoded register from the stack */
+static int selfVerificationMemRegLoad(int* sp, int reg)
+{
+    return *(sp + reg);
+}
+
+/* Load the value of a decoded doubleword register from the stack */
+static s8 selfVerificationMemRegLoadDouble(int* sp, int reg)
+{
+    return *((s8*)(sp + reg));
+}
+
+/* Store the value of a decoded register out to the stack */
+static void selfVerificationMemRegStore(int* sp, int data, int reg)
+{
+    *(sp + reg) = data;
+}
+
+/* Store the value of a decoded doubleword register out to the stack */
+static void selfVerificationMemRegStoreDouble(int* sp, s8 data, int reg)
+{
+    *((s8*)(sp + reg)) = data;
+}
+
+/*
+ * Load the specified size of data from the specified address, checking
+ * heapSpace first if Self Verification mode wrote to it previously, and
+ * falling back to actual memory otherwise.
+ */
+static int selfVerificationLoad(int addr, int size)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int data;
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) {
+            addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+            break;
+        }
+    }
+
+    switch (size) {
+        case kSVByte:
+            data = *((u1*) addr);
+            break;
+        case kSVSignedByte:
+            data = *((s1*) addr);
+            break;
+        case kSVHalfword:
+            data = *((u2*) addr);
+            break;
+        case kSVSignedHalfword:
+            data = *((s2*) addr);
+            break;
+        case kSVWord:
+            data = *((u4*) addr);
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+            data = 0;
+            dvmAbort();
+    }
+
+    //ALOGD("*** HEAP LOAD: Addr: %#x Data: %#x Size: %d", addr, data, size);
+    return data;
+}
+
+/* Like selfVerificationLoad, but specifically for doublewords */
+static s8 selfVerificationLoadDoubleword(int addr)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    ShadowHeap* heapSpacePtr;
+
+    int addr2 = addr+4;
+    unsigned int data = *((unsigned int*) addr);
+    unsigned int data2 = *((unsigned int*) addr2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            data = heapSpacePtr->data;
+        } else if (heapSpacePtr->addr == addr2) {
+            data2 = heapSpacePtr->data;
+        }
+    }
+
+    //ALOGD("*** HEAP LOAD DOUBLEWORD: Addr: %#x Data: %#x Data2: %#x",
+    //    addr, data, data2);
+    return (((s8) data2) << 32) | data;
+}
+
+/*
+ * Handles a store of a specified size of data to a specified address.
+ * This gets logged as an addr/data pair in heapSpace instead of modifying
+ * memory.  Addresses in heapSpace are unique, and accesses smaller than a
+ * word pull the entire word from memory first before updating.
+ */
+static void selfVerificationStore(int addr, int data, int size)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    //ALOGD("*** HEAP STORE: Addr: %#x Data: %#x Size: %d", addr, data, size);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) break;
+    }
+
+    if (heapSpacePtr == shadowSpace->heapSpaceTail) {
+        heapSpacePtr->addr = maskedAddr;
+        heapSpacePtr->data = *((unsigned int*) maskedAddr);
+        shadowSpace->heapSpaceTail++;
+    }
+
+    addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+    switch (size) {
+        case kSVByte:
+            *((u1*) addr) = data;
+            break;
+        case kSVSignedByte:
+            *((s1*) addr) = data;
+            break;
+        case kSVHalfword:
+            *((u2*) addr) = data;
+            break;
+        case kSVSignedHalfword:
+            *((s2*) addr) = data;
+            break;
+        case kSVWord:
+            *((u4*) addr) = data;
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationSave: %d", size);
+            dvmAbort();
+    }
+}
+
+/* Like selfVerificationStore, but specifically for doublewords */
+static void selfVerificationStoreDoubleword(int addr, s8 double_data)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int addr2 = addr+4;
+    int data = double_data;
+    int data2 = double_data >> 32;
+    bool store1 = false, store2 = false;
+
+    //ALOGD("*** HEAP STORE DOUBLEWORD: Addr: %#x Data: %#x, Data2: %#x",
+    //    addr, data, data2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            heapSpacePtr->data = data;
+            store1 = true;
+        } else if (heapSpacePtr->addr == addr2) {
+            heapSpacePtr->data = data2;
+            store2 = true;
+        }
+    }
+
+    if (!store1) {
+        shadowSpace->heapSpaceTail->addr = addr;
+        shadowSpace->heapSpaceTail->data = data;
+        shadowSpace->heapSpaceTail++;
+    }
+    if (!store2) {
+        shadowSpace->heapSpaceTail->addr = addr2;
+        shadowSpace->heapSpaceTail->data = data2;
+        shadowSpace->heapSpaceTail++;
+    }
+}
+
+/*
+ * Decodes the memory instruction at the address specified in the link
+ * register. All registers (r0-r12,lr) and fp registers (d0-d15) are stored
+ * consecutively on the stack beginning at the specified stack pointer.
+ * Calls the proper Self Verification handler for the memory instruction and
+ * updates the link register to point past the decoded memory instruction.
+ */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp)
+{
+    enum {
+        kMemOpLdrPcRel = 0x09, // ldr(3)  [01001] rd[10..8] imm_8[7..0]
+        kMemOpRRR      = 0x0A, // Full opcode is 7 bits
+        kMemOp2Single  = 0x0A, // Used for Vstrs and Vldrs
+        kMemOpRRR2     = 0x0B, // Full opcode is 7 bits
+        kMemOp2Double  = 0x0B, // Used for Vstrd and Vldrd
+        kMemOpStrRRI5  = 0x0C, // str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRI5  = 0x0D, // ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRI5 = 0x0E, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRI5 = 0x0F, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRI5 = 0x10, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRI5 = 0x11, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrSpRel = 0x13, // ldr(4)  [10011] rd[10..8] imm_8[7..0]
+        kMemOpStmia    = 0x18, // stmia   [11000] rn[10..8] reglist [7..0]
+        kMemOpLdmia    = 0x19, // ldmia   [11001] rn[10..8] reglist [7..0]
+        kMemOpStrRRR   = 0x28, // str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRR  = 0x29, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRR  = 0x2A, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrsbRRR = 0x2B, // ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRR   = 0x2C, // ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRR  = 0x2D, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRR  = 0x2E, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrshRRR = 0x2F, // ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0]
+        kMemOp2Stmia   = 0xE88, // stmia  [111010001000[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia   = 0xE89, // ldmia  [111010001001[ rn[19..16] mask[15..0]
+        kMemOp2Stmia2  = 0xE8A, // stmia  [111010001010[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia2  = 0xE8B, // ldmia  [111010001011[ rn[19..16] mask[15..0]
+        kMemOp2Vstr    = 0xED8, // Used for Vstrs and Vstrd
+        kMemOp2Vldr    = 0xED9, // Used for Vldrs and Vldrd
+        kMemOp2Vstr2   = 0xEDC, // Used for Vstrs and Vstrd
+        kMemOp2Vldr2   = 0xEDD, // Used for Vstrs and Vstrd
+        kMemOp2StrbRRR = 0xF80, /* str rt,[rn,rm,LSL #imm] [111110000000]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrbRRR = 0xF81, /* ldrb rt,[rn,rm,LSL #imm] [111110000001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrhRRR = 0xF82, /* str rt,[rn,rm,LSL #imm] [111110000010]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrhRRR = 0xF83, /* ldrh rt,[rn,rm,LSL #imm] [111110000011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrRRR  = 0xF84, /* str rt,[rn,rm,LSL #imm] [111110000100]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrRRR  = 0xF85, /* ldr rt,[rn,rm,LSL #imm] [111110000101]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrbRRI12 = 0xF88, /* strb rt,[rn,#imm12] [111110001000]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrbRRI12 = 0xF89, /* ldrb rt,[rn,#imm12] [111110001001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrhRRI12 = 0xF8A, /* strh rt,[rn,#imm12] [111110001010]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrhRRI12 = 0xF8B, /* ldrh rt,[rn,#imm12] [111110001011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrRRI12 = 0xF8C, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrRRI12 = 0xF8D, /* ldr(Imm,T3) rd,[rn,#imm12] [111110001101]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrsbRRR = 0xF91, /* ldrsb rt,[rn,rm,LSL #imm] [111110010001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrshRRR = 0xF93, /* ldrsh rt,[rn,rm,LSL #imm] [111110010011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrsbRRI12 = 0xF99, /* ldrsb rt,[rn,#imm12] [111110011001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrshRRI12 = 0xF9B, /* ldrsh rt,[rn,#imm12] [111110011011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2        = 0xE000, // top 3 bits set indicates Thumb2
+    };
+
+    int addr, offset, data;
+    long long double_data;
+    int size = kSVWord;
+    bool store = false;
+    unsigned int *lr_masked = (unsigned int *) (lr & 0xFFFFFFFE);
+    unsigned int insn = *lr_masked;
+
+    int old_lr;
+    old_lr = selfVerificationMemRegLoad(sp, 13);
+
+    if ((insn & kMemOp2) == kMemOp2) {
+        insn = (insn << 16) | (insn >> 16);
+        //ALOGD("*** THUMB2 - Addr: %#x Insn: %#x", lr, insn);
+
+        int opcode12 = (insn >> 20) & 0xFFF;
+        int opcode4 = (insn >> 8) & 0xF;
+        int imm2 = (insn >> 4) & 0x3;
+        int imm8 = insn & 0xFF;
+        int imm12 = insn & 0xFFF;
+        int rd = (insn >> 12) & 0xF;
+        int rm = insn & 0xF;
+        int rn = (insn >> 16) & 0xF;
+        int rt = (insn >> 12) & 0xF;
+        bool wBack = true;
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+4, 13);
+
+        // Determine whether the mem op is a store or load
+        switch (opcode12) {
+            case kMemOp2Stmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2StrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2StrRRI12:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode12) {
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+                size = kSVByte;
+                break;
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrsbRRI12:
+                size = kSVSignedByte;
+                break;
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+                size = kSVHalfword;
+                break;
+            case kMemOp2LdrshRRR:
+            case kMemOp2LdrshRRI12:
+                size = kSVSignedHalfword;
+                break;
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                if (opcode4 == kMemOp2Double) size = kSVDoubleword;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode12) {
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                offset = imm8 << 2;
+                if (opcode4 == kMemOp2Single) {
+                    rt = rd << 1;
+                    if (insn & 0x400000) rt |= 0x1;
+                } else if (opcode4 == kMemOp2Double) {
+                    if (insn & 0x400000) rt |= 0x10;
+                    rt = rt << 1;
+                } else {
+                    ALOGE("*** ERROR: UNRECOGNIZED VECTOR MEM OP: %x", opcode4);
+                    dvmAbort();
+                }
+                rt += 14;
+                break;
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2LdrRRR:
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrshRRR:
+                offset = selfVerificationMemRegLoad(sp, rm) << imm2;
+                break;
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+            case kMemOp2StrRRI12:
+            case kMemOp2LdrRRI12:
+            case kMemOp2LdrsbRRI12:
+            case kMemOp2LdrshRRI12:
+                offset = imm12;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+                wBack = false;
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 STMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationMemRegLoadDouble(sp, rt);
+                selfVerificationStoreDoubleword(addr+offset, double_data);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 LDMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationLoadDoubleword(addr+offset);
+                selfVerificationMemRegStoreDouble(sp, double_data, rt);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    } else {
+        //ALOGD("*** THUMB - Addr: %#x Insn: %#x", lr, insn);
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+2, 13);
+
+        int opcode5 = (insn >> 11) & 0x1F;
+        int opcode7 = (insn >> 9) & 0x7F;
+        int imm = (insn >> 6) & 0x1F;
+        int rd = (insn >> 8) & 0x7;
+        int rm = (insn >> 6) & 0x7;
+        int rn = (insn >> 3) & 0x7;
+        int rt = insn & 0x7;
+
+        // Determine whether the mem op is a store or load
+        switch (opcode5) {
+            case kMemOpRRR:
+                switch (opcode7) {
+                    case kMemOpStrRRR:
+                    case kMemOpStrhRRR:
+                    case kMemOpStrbRRR:
+                        store = true;
+                }
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpStrbRRI5:
+            case kMemOpStrhRRI5:
+            case kMemOpStmia:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode5) {
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                switch (opcode7) {
+                    case kMemOpStrbRRR:
+                    case kMemOpLdrbRRR:
+                        size = kSVByte;
+                        break;
+                    case kMemOpLdrsbRRR:
+                        size = kSVSignedByte;
+                        break;
+                    case kMemOpStrhRRR:
+                    case kMemOpLdrhRRR:
+                        size = kSVHalfword;
+                        break;
+                    case kMemOpLdrshRRR:
+                        size = kSVSignedHalfword;
+                        break;
+                }
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                size = kSVByte;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                size = kSVHalfword;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        if (opcode5 == kMemOpLdrPcRel)
+            addr = selfVerificationMemRegLoad(sp, 4);
+        else if (opcode5 == kMemOpStmia || opcode5 == kMemOpLdmia)
+            addr = selfVerificationMemRegLoad(sp, rd);
+        else
+            addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode5) {
+            case kMemOpLdrPcRel:
+                offset = (insn & 0xFF) << 2;
+                rt = rd;
+                break;
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                offset = selfVerificationMemRegLoad(sp, rm);
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpLdrRRI5:
+                offset = imm << 2;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                offset = imm << 1;
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                offset = imm;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB MEM OP: %x", opcode5);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                bool wBack = true;
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        if (i == rd) wBack = false;
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    }
+}
+#endif
diff --git a/vm/compiler/codegen/arm/CalloutHelper.h b/vm/compiler/codegen/arm/CalloutHelper.h
new file mode 100644
index 0000000..079c5f6
--- /dev/null
+++ b/vm/compiler/codegen/arm/CalloutHelper.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
+
+#include "Dalvik.h"
+
+/*
+ * Declare/comment prototypes of all native callout functions invoked by the
+ * JIT'ed code here and use the LOAD_FUNC_ADDR macro to load the address into
+ * a register. In this way we have a centralized place to find out all native
+ * helper functions and we can grep for LOAD_FUNC_ADDR to find out all the
+ * callsites.
+ */
+
+/* Load a statically compiled function address as a constant */
+#define LOAD_FUNC_ADDR(cUnit, reg, addr) loadConstant(cUnit, reg, addr)
+
+/* Conversions */
+extern "C" float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
+extern "C" int __aeabi_f2iz(float op1);            // OP_FLOAT_TO_INT
+extern "C" float __aeabi_d2f(double op1);          // OP_DOUBLE_TO_FLOAT
+extern "C" double __aeabi_f2d(float op1);          // OP_FLOAT_TO_DOUBLE
+extern "C" double __aeabi_i2d(int op1);            // OP_INT_TO_DOUBLE
+extern "C" int __aeabi_d2iz(double op1);           // OP_DOUBLE_TO_INT
+extern "C" float __aeabi_l2f(long op1);            // OP_LONG_TO_FLOAT
+extern "C" double __aeabi_l2d(long op1);           // OP_LONG_TO_DOUBLE
+s8 dvmJitf2l(float op1);                // OP_FLOAT_TO_LONG
+s8 dvmJitd2l(double op1);               // OP_DOUBLE_TO_LONG
+
+/* Single-precision FP arithmetics */
+extern "C" float __aeabi_fadd(float a, float b);   // OP_ADD_FLOAT[_2ADDR]
+extern "C" float __aeabi_fsub(float a, float b);   // OP_SUB_FLOAT[_2ADDR]
+extern "C" float __aeabi_fdiv(float a, float b);   // OP_DIV_FLOAT[_2ADDR]
+extern "C" float __aeabi_fmul(float a, float b);   // OP_MUL_FLOAT[_2ADDR]
+extern "C" float fmodf(float a, float b);          // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+extern "C" double __aeabi_dadd(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+extern "C" double __aeabi_dsub(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+extern "C" double __aeabi_ddiv(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+extern "C" double __aeabi_dmul(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+extern "C" double fmod(double a, double b);         // OP_REM_DOUBLE[_2ADDR]
+
+/* Integer arithmetics */
+extern "C" int __aeabi_idivmod(int op1, int op2);  // OP_REM_INT[_2ADDR|_LIT8|_LIT16]
+extern "C" int __aeabi_idiv(int op1, int op2);     // OP_DIV_INT[_2ADDR|_LIT8|_LIT16]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+extern "C" long long __aeabi_ldivmod(long long op1, long long op2);
+
+/* Originally declared in Sync.h */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj); //OP_MONITOR_EXIT
+
+/* Originally declared in oo/TypeCheck.h */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,   // OP_APUT_OBJECT
+                           const ClassObject* arrayClass);
+int dvmInstanceofNonTrivial(const ClassObject* instance,   // OP_CHECK_CAST &&
+                            const ClassObject* clazz);     // OP_INSTANCE_OF
+
+/* Originally declared in oo/Array.h */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, // OP_NEW_ARRAY
+                                  size_t length, int allocFlags);
+
+/* Originally declared in interp/InterpDefs.h */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,// OP_FILL_ARRAY_DATA
+                                  const u2* arrayData);
+
+/* Originally declared in compiler/codegen/arm/Assemble.c */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+
+/*
+ * Resolve interface callsites - OP_INVOKE_INTERFACE & OP_INVOKE_INTERFACE_RANGE
+ *
+ * Originally declared in mterp/common/FindInterface.h and only comment it here
+ * due to the INLINE attribute.
+ *
+ * INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ *  u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+ */
+
+/* Originally declared in alloc/Alloc.h */
+Object* dvmAllocObject(ClassObject* clazz, int flags);  // OP_NEW_INSTANCE
+
+/*
+ * Functions declared in gDvmInlineOpsTable[] are used for
+ * OP_EXECUTE_INLINE & OP_EXECUTE_INLINE_RANGE.
+ */
+extern "C" double sqrt(double x);  // INLINE_MATH_SQRT
+
+/*
+ * The following functions are invoked through the compiler templates (declared
+ * in compiler/template/armv5te/footer.S:
+ *
+ *      __aeabi_cdcmple         // CMPG_DOUBLE
+ *      __aeabi_cfcmple         // CMPG_FLOAT
+ *      dvmLockObject           // MONITOR_ENTER
+ */
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H_
diff --git a/vm/compiler/codegen/arm/Codegen.h b/vm/compiler/codegen/arm/Codegen.h
new file mode 100644
index 0000000..e67f3d8
--- /dev/null
+++ b/vm/compiler/codegen/arm/Codegen.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerIR.h"
+#include "CalloutHelper.h"
+
+#if defined(_CODEGEN_C)
+/*
+ * loadConstant() sometimes needs to add a small imm to a pre-existing constant
+ */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value);
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2);
+
+/* Forward decalraton the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+#if defined(__ARM_ARCH_5__)
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir);
+#endif
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir);
+
+#endif
+
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Self Verification memory instruction decoder */
+extern "C" void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+extern void dvmCompilerSetupResourceMasks(ArmLIR *lir);
+
+extern ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+                                          int rSrc);
diff --git a/vm/compiler/codegen/arm/CodegenCommon.cpp b/vm/compiler/codegen/arm/CodegenCommon.cpp
new file mode 100644
index 0000000..5c02678
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenCommon.cpp
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+#include "compiler/Loop.h"
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[kNumPackedOpcodes];
+
+static void setMemRefType(ArmLIR *lir, bool isLoad, int memType)
+{
+    u8 *maskPtr;
+    u8 mask = ENCODE_MEM;;
+    assert(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE));
+    if (isLoad) {
+        maskPtr = &lir->useMask;
+    } else {
+        maskPtr = &lir->defMask;
+    }
+    /* Clear out the memref flags */
+    *maskPtr &= ~mask;
+    /* ..and then add back the one we need */
+    switch(memType) {
+        case kLiteral:
+            assert(isLoad);
+            *maskPtr |= ENCODE_LITERAL;
+            break;
+        case kDalvikReg:
+            *maskPtr |= ENCODE_DALVIK_REG;
+            break;
+        case kHeapRef:
+            *maskPtr |= ENCODE_HEAP_REF;
+            break;
+        case kMustNotAlias:
+            /* Currently only loads can be marked as kMustNotAlias */
+            assert(!(EncodingMap[lir->opcode].flags & IS_STORE));
+            *maskPtr |= ENCODE_MUST_NOT_ALIAS;
+            break;
+        default:
+            ALOGE("Jit: invalid memref kind - %d", memType);
+            assert(0);  // Bail if debug build, set worst-case in the field
+            *maskPtr |= ENCODE_ALL;
+    }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through r5FP +
+ * offset.
+ */
+static void annotateDalvikRegAccess(ArmLIR *lir, int regId, bool isLoad)
+{
+    setMemRefType(lir, isLoad, kDalvikReg);
+
+    /*
+     * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit
+     * access.
+     */
+    lir->aliasInfo = regId;
+    if (DOUBLEREG(lir->operands[0])) {
+        lir->aliasInfo |= 0x80000000;
+    }
+}
+
+/*
+ * Decode the register id.
+ */
+static inline u8 getRegMaskCommon(int reg)
+{
+    u8 seed;
+    int shift;
+    int regId = reg & 0x1f;
+
+    /*
+     * Each double register is equal to a pair of single-precision FP registers
+     */
+    seed = DOUBLEREG(reg) ? 3 : 1;
+    /* FP register starts at bit position 16 */
+    shift = FPREG(reg) ? kFPReg0 : 0;
+    /* Expand the double register id into single offset */
+    shift += regId;
+    return (seed << shift);
+}
+
+/* External version of getRegMaskCommon */
+u8 dvmGetRegResourceMask(int reg)
+{
+    return getRegMaskCommon(reg);
+}
+
+/*
+ * Mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, int reg)
+{
+    *mask |= getRegMaskCommon(reg);
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+static void setupResourceMasks(ArmLIR *lir)
+{
+    int opcode = lir->opcode;
+    int flags;
+
+    if (opcode <= 0) {
+        lir->useMask = lir->defMask = 0;
+        return;
+    }
+
+    flags = EncodingMap[lir->opcode].flags;
+
+    /* Set up the mask for resources that are updated */
+    if (flags & (IS_LOAD | IS_STORE)) {
+        /* Default to heap - will catch specialized classes later */
+        setMemRefType(lir, flags & IS_LOAD, kHeapRef);
+    }
+
+    /*
+     * Conservatively assume the branch here will call out a function that in
+     * turn will trash everything.
+     */
+    if (flags & IS_BRANCH) {
+        lir->defMask = lir->useMask = ENCODE_ALL;
+        return;
+    }
+
+    if (flags & REG_DEF0) {
+        setupRegMask(&lir->defMask, lir->operands[0]);
+    }
+
+    if (flags & REG_DEF1) {
+        setupRegMask(&lir->defMask, lir->operands[1]);
+    }
+
+    if (flags & REG_DEF_SP) {
+        lir->defMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & REG_DEF_LIST0) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_DEF_LIST1) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & SETS_CCODES) {
+        lir->defMask |= ENCODE_CCODE;
+    }
+
+    /* Conservatively treat the IT block */
+    if (flags & IS_IT) {
+        lir->defMask = ENCODE_ALL;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & REG_USE_PC) {
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    if (flags & REG_USE_SP) {
+        lir->useMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_USE_LIST0) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_USE_LIST1) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+
+    /* Fixup for kThumbPush/lr and kThumbPop/pc */
+    if (opcode == kThumbPush || opcode == kThumbPop) {
+        u8 r8Mask = getRegMaskCommon(r8);
+        if ((opcode == kThumbPush) && (lir->useMask & r8Mask)) {
+            lir->useMask &= ~r8Mask;
+            lir->useMask |= ENCODE_REG_LR;
+        } else if ((opcode == kThumbPop) && (lir->defMask & r8Mask)) {
+            lir->defMask &= ~r8Mask;
+            lir->defMask |= ENCODE_REG_PC;
+        }
+    }
+}
+
+/*
+ * Set up the accurate resource mask for branch instructions
+ */
+static void relaxBranchMasks(ArmLIR *lir)
+{
+    int flags = EncodingMap[lir->opcode].flags;
+
+    /* Make sure only branch instructions are passed here */
+    assert(flags & IS_BRANCH);
+
+    lir->useMask = lir->defMask = ENCODE_REG_PC;
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpcode opcode)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND));
+    insn->opcode = opcode;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_BINARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1, int src2)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) {
+        ALOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode);
+    }
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_TERTIARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+#if defined(_ARMV7_A) || defined(_ARMV7_A_NEON)
+static ArmLIR *newLIR4(CompilationUnit *cUnit, ArmOpcode opcode,
+                           int dest, int src1, int src2, int info)
+{
+    ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    assert(isPseudoOpcode(opcode) ||
+           (EncodingMap[opcode].flags & IS_QUAD_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    insn->operands[3] = info;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+#endif
+
+/*
+ * If the next instruction is a move-result or move-result-long,
+ * return the target Dalvik sReg[s] and convert the next to a
+ * nop.  Otherwise, return INVALID_SREG.  Used to optimize method inlining.
+ */
+static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir,
+                                  bool fpHint)
+{
+    if (mir->next &&
+        ((mir->next->dalvikInsn.opcode == OP_MOVE_RESULT) ||
+         (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_OBJECT))) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDest(cUnit, mir->next, 0);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+/*
+ * Search the existing constants in the literal pool for an exact or close match
+ * within specified delta (greater or equal to 0).
+ */
+static ArmLIR *scanLiteralPool(LIR *dataTarget, int value, unsigned int delta)
+{
+    while (dataTarget) {
+        if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <=
+            delta)
+            return (ArmLIR *) dataTarget;
+        dataTarget = dataTarget->next;
+    }
+    return NULL;
+}
+
+/* Search the existing constants in the literal pool for an exact wide match */
+ArmLIR* scanLiteralPoolWide(LIR* dataTarget, int valLo, int valHi)
+{
+  bool lowMatch = false;
+  ArmLIR* lowTarget = NULL;
+  while (dataTarget) {
+    if (lowMatch && (((ArmLIR *)dataTarget)->operands[0] == valHi)) {
+      return lowTarget;
+    }
+    lowMatch = false;
+    if (((ArmLIR *) dataTarget)->operands[0] == valLo) {
+      lowMatch = true;
+      lowTarget = (ArmLIR *) dataTarget;
+    }
+    dataTarget = dataTarget->next;
+  }
+  return NULL;
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static ArmLIR *addWordData(CompilationUnit *cUnit, LIR **constantListP,
+                           int value)
+{
+    /* Add the constant to the literal pool */
+    if (constantListP) {
+        ArmLIR *newValue = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        newValue->operands[0] = value;
+        newValue->generic.next = *constantListP;
+        *constantListP = (LIR *) newValue;
+        return newValue;
+    } else {
+        /* Add the constant in the middle of code stream */
+        newLIR1(cUnit, kArm16BitData, (value & 0xffff));
+        newLIR1(cUnit, kArm16BitData, (value >> 16));
+    }
+    return NULL;
+}
+
+/* Add a 64-bit constant to the literal pool or mixed with code */
+ArmLIR* addWideData(CompilationUnit* cUnit, LIR** constantListP,
+                 int valLo, int valHi)
+{
+    addWordData(cUnit, constantListP, valHi);
+    return addWordData(cUnit, constantListP, valLo);
+}
+
+static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir,
+                                      bool fpHint)
+{
+    if (mir->next &&
+        (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_WIDE)) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+
+/*
+ * Generate an kArmPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+static void genBarrier(CompilationUnit *cUnit)
+{
+    ArmLIR *barrier = newLIR0(cUnit, kArmPseudoBarrier);
+    /* Mark all resources as being clobbered */
+    barrier->defMask = -1;
+}
+
+/* Create the PC reconstruction slot if not already done */
+static ArmLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+                              ArmLIR *branch,
+                              ArmLIR *pcrLabel)
+{
+    /* Forget all def info (because we might rollback here.  Bug #2367397 */
+    dvmCompilerResetDefTracking(cUnit);
+
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + dOffset);
+        pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = dOffset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+
+    /* Clear the conservative flags for branches that punt to the interpreter */
+    relaxBranchMasks(branch);
+
+    return pcrLabel;
+}
diff --git a/vm/compiler/codegen/arm/CodegenDriver.cpp b/vm/compiler/codegen/arm/CodegenDriver.cpp
new file mode 100644
index 0000000..499ac49
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenDriver.cpp
@@ -0,0 +1,4796 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg)
+{
+    int regCardBase = dvmCompilerAllocTemp(cUnit);
+    int regCardNo = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, cardTable),
+                 regCardBase);
+    opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
+    storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
+                     kUnsignedByte);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *)target;
+    dvmCompilerFreeTemp(cUnit, regCardBase);
+    dvmCompilerFreeTemp(cUnit, regCardNo);
+}
+
+static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
+                                     int srcSize, int tgtSize)
+{
+    /*
+     * Don't optimize the register usage since it calls out to template
+     * functions
+     */
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    if (srcSize == 1) {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        loadValueDirectFixed(cUnit, rlSrc, r0);
+    } else {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
+    }
+    LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+    if (tgtSize == 1) {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerGetReturn(cUnit);
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            funct = (void*) __aeabi_fadd;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            funct = (void*) __aeabi_fsub;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            funct = (void*) __aeabi_fdiv;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            funct = (void*) __aeabi_fmul;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+            funct = (void*) fmodf;
+            break;
+        case OP_NEG_FLOAT: {
+            genNegFloat(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    loadValueDirectFixed(cUnit, rlSrc1, r0);
+    loadValueDirectFixed(cUnit, rlSrc2, r1);
+    LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            funct = (void*) __aeabi_dadd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            funct = (void*) __aeabi_dsub;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            funct = (void*) __aeabi_ddiv;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            funct = (void*) __aeabi_dmul;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+            funct = (void*) (double (*)(double, double)) fmod;
+            break;
+        case OP_NEG_DOUBLE: {
+            genNegDouble(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    LOAD_FUNC_ADDR(cUnit, r14lr, (int)funct);
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    opReg(cUnit, kOpBlx, r14lr);
+    dvmCompilerClobberCallRegs(cUnit);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+    cUnit->usesLinkRegister = true;
+#endif
+    return false;
+}
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1);
+        case OP_FLOAT_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1);
+        case OP_DOUBLE_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1);
+        case OP_FLOAT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2);
+        case OP_INT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2);
+        case OP_DOUBLE_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1);
+        case OP_FLOAT_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2);
+        case OP_LONG_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1);
+        case OP_DOUBLE_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2);
+        case OP_LONG_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2);
+        default:
+            return true;
+    }
+    return false;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpcode opcode,
+                          int dest, int src1)
+{
+     ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+     insn->opcode = opcode;
+     insn->operands[0] = dest;
+     insn->operands[1] = src1;
+     setupResourceMasks(insn);
+     dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+/*
+ * Example where r14 (LR) is preserved around a heap access under
+ * self-verification mode in Thumb2:
+ *
+ * D/dalvikvm( 1538): 0x59414c5e (0026): ldr     r14, [r15pc, #220] <-hoisted
+ * D/dalvikvm( 1538): 0x59414c62 (002a): mla     r4, r0, r8, r4
+ * D/dalvikvm( 1538): 0x59414c66 (002e): adds    r3, r4, r3
+ * D/dalvikvm( 1538): 0x59414c6a (0032): push    <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c6c (0034): blx_1   0x5940f494      |
+ * D/dalvikvm( 1538): 0x59414c6e (0036): blx_2   see above       <-MEM_OP_DECODE
+ * D/dalvikvm( 1538): 0x59414c70 (0038): ldr     r10, [r9, #0]   |
+ * D/dalvikvm( 1538): 0x59414c74 (003c): pop     <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c78 (0040): mov     r11, r10
+ * D/dalvikvm( 1538): 0x59414c7a (0042): asr     r12, r11, #31
+ * D/dalvikvm( 1538): 0x59414c7e (0046): movs    r0, r2
+ * D/dalvikvm( 1538): 0x59414c80 (0048): movs    r1, r3
+ * D/dalvikvm( 1538): 0x59414c82 (004a): str     r2, [r5, #16]
+ * D/dalvikvm( 1538): 0x59414c84 (004c): mov     r2, r11
+ * D/dalvikvm( 1538): 0x59414c86 (004e): str     r3, [r5, #20]
+ * D/dalvikvm( 1538): 0x59414c88 (0050): mov     r3, r12
+ * D/dalvikvm( 1538): 0x59414c8a (0052): str     r11, [r5, #24]
+ * D/dalvikvm( 1538): 0x59414c8e (0056): str     r12, [r5, #28]
+ * D/dalvikvm( 1538): 0x59414c92 (005a): blx     r14             <-use of LR
+ *
+ */
+static void selfVerificationBranchInsertPass(CompilationUnit *cUnit)
+{
+    ArmLIR *thisLIR;
+    TemplateOpcode opcode = TEMPLATE_MEM_OP_DECODE;
+
+    for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        if (!thisLIR->flags.isNop && thisLIR->flags.insertWrapper) {
+            /*
+             * Push r5(FP) and r14(LR) onto stack. We need to make sure that
+             * SP is 8-byte aligned, and we use r5 as a temp to restore LR
+             * for Thumb-only target since LR cannot be directly accessed in
+             * Thumb mode. Another reason to choose r5 here is it is the Dalvik
+             * frame pointer and cannot be the target of the emulated heap
+             * load.
+             */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPreBranch(cUnit, thisLIR);
+            }
+
+            /* Branch to mem op decode template */
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+
+            /* Restore LR */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPostBranch(cUnit, thisLIR);
+            }
+        }
+    }
+}
+#endif
+
+/* Generate conditional branch instructions */
+static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
+                                    ArmConditionCode cond,
+                                    ArmLIR *target)
+{
+    ArmLIR *branch = opCondBranch(cUnit, cond);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+                                  ArmLIR *pcrLabel)
+{
+    ArmLIR *branch = opNone(cUnit, kOpUncondBr);
+    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(rlDest.wide);
+
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    HEAP_ACCESS_SHADOW(true);
+    loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr;
+    rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    regPtr = dvmCompilerAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+    HEAP_ACCESS_SHADOW(true);
+    storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+}
+
+/*
+ * Load a field from an object instance
+ *
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isVolatile)
+{
+    RegLocation rlResult;
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    HEAP_ACCESS_SHADOW(true);
+    loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                 size, rlObj.sRegLow);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kISH);
+    }
+
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+/*
+ * Store a field to an object instance
+ *
+ */
+static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isObject, bool isVolatile)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlSrc = loadValue(cUnit, rlSrc, regClass);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kISHST);
+    }
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kISH);
+    }
+    if (isObject) {
+        /* NOTE: marking card based on object head */
+        markCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
+    }
+}
+
+
+/*
+ * Generate array load
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlDest, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlResult;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+    int regPtr;
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
+                                rlArray.lowReg, mir->offset, NULL);
+    }
+
+    regPtr = dvmCompilerAllocTemp(cUnit);
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+    }
+    if ((size == kLong) || (size == kDouble)) {
+        if (scale) {
+            int rNewIndex = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+            opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+            dvmCompilerFreeTemp(cUnit, rNewIndex);
+        } else {
+            opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+        }
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+        HEAP_ACCESS_SHADOW(true);
+        loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
+                        scale, size);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+}
+
+/*
+ * Generate array store
+ *
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlSrc, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regPtr;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+
+    if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) {
+        dvmCompilerClobber(cUnit, rlArray.lowReg);
+        regPtr = rlArray.lowReg;
+    } else {
+        regPtr = dvmCompilerAllocTemp(cUnit);
+        genRegCopy(cUnit, regPtr, rlArray.lowReg);
+    }
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        //NOTE: max live temps(4) here.
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    } else {
+        /* regPtr -> array data */
+        opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+    }
+    /* at this point, regPtr points to array, 2 live temps */
+    if ((size == kLong) || (size == kDouble)) {
+        //TODO: need specific wide routine that can handle fp regs
+        if (scale) {
+            int rNewIndex = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+            opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+            dvmCompilerFreeTemp(cUnit, rNewIndex);
+        } else {
+            opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+        }
+        rlSrc = loadValueWide(cUnit, rlSrc, regClass);
+
+        HEAP_ACCESS_SHADOW(true);
+        storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+        HEAP_ACCESS_SHADOW(false);
+
+        dvmCompilerFreeTemp(cUnit, regPtr);
+    } else {
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
+                         scale, size);
+        HEAP_ACCESS_SHADOW(false);
+    }
+}
+
+/*
+ * Generate array object store
+ * Must use explicit register allocation here because of
+ * call-out to dvmCanPutArrayElement
+ */
+static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir,
+                              RegLocation rlArray, RegLocation rlIndex,
+                              RegLocation rlSrc, int scale)
+{
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    dvmCompilerFlushAllRegs(cUnit);
+
+    int regLen = r0;
+    int regPtr = r4PC;  /* Preserved across call */
+    int regArray = r1;
+    int regIndex = r7;  /* Preserved across call */
+
+    loadValueDirectFixed(cUnit, rlArray, regArray);
+    loadValueDirectFixed(cUnit, rlIndex, regIndex);
+
+    /* null object? */
+    ArmLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        /* Get len */
+        loadWordDisp(cUnit, regArray, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+        genBoundsCheck(cUnit, regIndex, regLen, mir->offset,
+                       pcrLabel);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+    }
+
+    /* Get object to store */
+    loadValueDirectFixed(cUnit, rlSrc, r0);
+    LOAD_FUNC_ADDR(cUnit, r2, (int)dvmCanPutArrayElement);
+
+    /* Are we storing null?  If so, avoid check */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
+
+    /* Make sure the types are compatible */
+    loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r1);
+    loadWordDisp(cUnit, r0, offsetof(Object, clazz), r0);
+    opReg(cUnit, kOpBlx, r2);
+    dvmCompilerClobberCallRegs(cUnit);
+
+    /*
+     * Using fixed registers here, and counting on r4 and r7 being
+     * preserved across the above call.  Tell the register allocation
+     * utilities about the regs we are using directly
+     */
+    dvmCompilerLockTemp(cUnit, regPtr);   // r4PC
+    dvmCompilerLockTemp(cUnit, regIndex); // r7
+    dvmCompilerLockTemp(cUnit, r0);
+    dvmCompilerLockTemp(cUnit, r1);
+
+    /* Bad? - roll back and re-execute if so */
+    genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel);
+
+    /* Resume here - must reload element & array, regPtr & index preserved */
+    loadValueDirectFixed(cUnit, rlSrc, r0);
+    loadValueDirectFixed(cUnit, rlArray, r1);
+
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseIndexed(cUnit, regPtr, regIndex, r0,
+                     scale, kWord);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    dvmCompilerFreeTemp(cUnit, regIndex);
+
+    /* NOTE: marking card here based on object head */
+    markCard(cUnit, r0, r1);
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlShift)
+{
+    /*
+     * Don't mess with the regsiters here as there is a particular calling
+     * convention to the out-of-line handler.
+     */
+    RegLocation rlResult;
+
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirect(cUnit, rlShift, r2);
+    switch( mir->dalvikInsn.opcode) {
+        case OP_SHL_LONG:
+        case OP_SHL_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+            break;
+        case OP_SHR_LONG:
+        case OP_SHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+            break;
+        case OP_USHR_LONG:
+        case OP_USHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+            break;
+        default:
+            return true;
+    }
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    OpKind firstOp = kOpBkpt;
+    OpKind secondOp = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    void *callTgt;
+    int retReg = r0;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NOT_LONG:
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        case OP_ADD_LONG:
+        case OP_ADD_LONG_2ADDR:
+            firstOp = kOpAdd;
+            secondOp = kOpAdc;
+            break;
+        case OP_SUB_LONG:
+        case OP_SUB_LONG_2ADDR:
+            firstOp = kOpSub;
+            secondOp = kOpSbc;
+            break;
+        case OP_MUL_LONG:
+        case OP_MUL_LONG_2ADDR:
+            genMulLong(cUnit, rlDest, rlSrc1, rlSrc2);
+            return false;
+        case OP_DIV_LONG:
+        case OP_DIV_LONG_2ADDR:
+            callOut = true;
+            retReg = r0;
+            checkZero = true;
+            callTgt = (void*)__aeabi_ldivmod;
+            break;
+        /* NOTE - result is in r2/r3 instead of r0/r1 */
+        case OP_REM_LONG:
+        case OP_REM_LONG_2ADDR:
+            callOut = true;
+            callTgt = (void*)__aeabi_ldivmod;
+            retReg = r2;
+            checkZero = true;
+            break;
+        case OP_AND_LONG_2ADDR:
+        case OP_AND_LONG:
+            firstOp = kOpAnd;
+            secondOp = kOpAnd;
+            break;
+        case OP_OR_LONG:
+        case OP_OR_LONG_2ADDR:
+            firstOp = kOpOr;
+            secondOp = kOpOr;
+            break;
+        case OP_XOR_LONG:
+        case OP_XOR_LONG_2ADDR:
+            firstOp = kOpXor;
+            secondOp = kOpXor;
+            break;
+        case OP_NEG_LONG: {
+            //TUNING: can improve this using Thumb2 code
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, tReg, 0);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+                        tReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg);
+            genRegCopy(cUnit, rlResult.highReg, tReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+        }
+        default:
+            ALOGE("Invalid long arith op");
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
+    } else {
+        // Adjust return regs in to handle case of rem returning r2/r3
+        dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+        loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+        loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+        LOAD_FUNC_ADDR(cUnit, r14lr, (int) callTgt);
+        if (checkZero) {
+            int tReg = r12; // Using fixed registers during call sequence
+            opRegRegReg(cUnit, kOpOr, tReg, r2, r3);
+            genRegImmCheck(cUnit, kArmCondEq, tReg, 0, mir->offset, NULL);
+        }
+        opReg(cUnit, kOpBlx, r14lr);
+        dvmCompilerClobberCallRegs(cUnit);
+        if (retReg == r0)
+            rlResult = dvmCompilerGetReturnWide(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+        cUnit->usesLinkRegister = true;
+#endif
+    }
+    return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir,
+                          RegLocation rlDest, RegLocation rlSrc1,
+                          RegLocation rlSrc2)
+{
+    OpKind op = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    bool unary = false;
+    int retReg = r0;
+    int (*callTgt)(int, int) = NULL;
+    RegLocation rlResult;
+    bool shiftOp = false;
+    bool remOp = false;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NEG_INT:
+            op = kOpNeg;
+            unary = true;
+            break;
+        case OP_NOT_INT:
+            op = kOpMvn;
+            unary = true;
+            break;
+        case OP_ADD_INT:
+        case OP_ADD_INT_2ADDR:
+            op = kOpAdd;
+            break;
+        case OP_SUB_INT:
+        case OP_SUB_INT_2ADDR:
+            op = kOpSub;
+            break;
+        case OP_MUL_INT:
+        case OP_MUL_INT_2ADDR:
+            op = kOpMul;
+            break;
+        case OP_DIV_INT:
+        case OP_DIV_INT_2ADDR:
+#ifdef __ARM_ARCH_EXT_IDIV__
+            op = kOpDiv;
+#else
+            callOut = true;
+            callTgt = __aeabi_idiv;
+            retReg = r0;
+#endif
+            checkZero = true;
+            break;
+        /* NOTE: returns in r1 */
+        case OP_REM_INT:
+        case OP_REM_INT_2ADDR:
+#ifdef __ARM_ARCH_EXT_IDIV__
+            op = kOpRem;
+            remOp = true;
+#else
+            callOut = true;
+            callTgt = __aeabi_idivmod;
+            retReg = r1;
+#endif
+            checkZero = true;
+            break;
+        case OP_AND_INT:
+        case OP_AND_INT_2ADDR:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT:
+        case OP_OR_INT_2ADDR:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT:
+        case OP_XOR_INT_2ADDR:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT:
+        case OP_SHL_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT:
+        case OP_SHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT:
+        case OP_USHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+        default:
+            ALOGE("Invalid word arith op: %#x(%d)",
+                 mir->dalvikInsn.opcode, mir->dalvikInsn.opcode);
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+        if (unary) {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, op, rlResult.lowReg,
+                     rlSrc1.lowReg);
+        } else {
+            rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+#ifdef __ARM_ARCH_EXT_IDIV__
+            if (checkZero) {
+                genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL);
+            }
+#endif
+            if (shiftOp) {
+                int tReg = dvmCompilerAllocTemp(cUnit);
+                opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
+                rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+                opRegRegReg(cUnit, op, rlResult.lowReg,
+                            rlSrc1.lowReg, tReg);
+                dvmCompilerFreeTemp(cUnit, tReg);
+            } else if(remOp) {
+                int tReg = dvmCompilerAllocTemp(cUnit);
+                opRegRegReg(cUnit, kOpDiv, tReg,
+                            rlSrc1.lowReg, rlSrc2.lowReg);
+                rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+                opRegRegRegReg(cUnit, op, rlResult.lowReg,
+                               rlSrc2.lowReg, tReg, rlSrc1.lowReg);
+                dvmCompilerFreeTemp(cUnit, tReg);
+            } else {
+                rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+                opRegRegReg(cUnit, op, rlResult.lowReg,
+                            rlSrc1.lowReg, rlSrc2.lowReg);
+            }
+        }
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        RegLocation rlResult;
+        dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+        loadValueDirectFixed(cUnit, rlSrc2, r1);
+        LOAD_FUNC_ADDR(cUnit, r2, (int) callTgt);
+        loadValueDirectFixed(cUnit, rlSrc1, r0);
+        if (checkZero) {
+            genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL);
+        }
+        opReg(cUnit, kOpBlx, r2);
+        dvmCompilerClobberCallRegs(cUnit);
+        if (retReg == r0)
+            rlResult = dvmCompilerGetReturn(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnAlt(cUnit);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+static bool genArithOp(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    /* Deduce sizes of operands */
+    if (mir->ssaRep->numUses == 2) {
+        rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    } else if (mir->ssaRep->numUses == 3) {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+    } else {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        assert(mir->ssaRep->numUses == 4);
+    }
+    if (mir->ssaRep->numDefs == 1) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    } else {
+        assert(mir->ssaRep->numDefs == 2);
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    }
+
+    if ((opcode >= OP_ADD_LONG_2ADDR) && (opcode <= OP_XOR_LONG_2ADDR)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_LONG) && (opcode <= OP_XOR_LONG)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG_2ADDR) && (opcode <= OP_USHR_LONG_2ADDR)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG) && (opcode <= OP_USHR_LONG)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_USHR_INT_2ADDR)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT) && (opcode <= OP_USHR_INT)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT_2ADDR) && (opcode <= OP_REM_FLOAT_2ADDR)) {
+        return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT) && (opcode <= OP_REM_FLOAT)) {
+        return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    return true;
+}
+
+/* Generate unconditional branch instructions */
+static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target)
+{
+    ArmLIR *branch = opNone(cUnit, kOpUncondBr);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Perform the actual operation for OP_RETURN_* */
+static void genReturnCommon(CompilationUnit *cUnit, MIR *mir)
+{
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                         TEMPLATE_RETURN_PROF : TEMPLATE_RETURN);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.returnOp++;
+#endif
+    int dPC = (int) (cUnit->method->insns + mir->offset);
+    /* Insert branch, but defer setting of target */
+    ArmLIR *branch = genUnconditionalBranch(cUnit, NULL);
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+    pcrLabel->operands[0] = dPC;
+    pcrLabel->operands[1] = mir->offset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+                                  DecodedInstruction *dInsn,
+                                  ArmLIR **pcrLabel)
+{
+    unsigned int i;
+    unsigned int regMask = 0;
+    RegLocation rlArg;
+    int numDone = 0;
+
+    /*
+     * Load arguments to r0..r4.  Note that these registers may contain
+     * live values, so we clobber them immediately after loading to prevent
+     * them from being used as sources for subsequent loads.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    for (i = 0; i < dInsn->vA; i++) {
+        regMask |= 1 << i;
+        rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++);
+        loadValueDirectFixed(cUnit, rlArg, i);
+    }
+    if (regMask) {
+        /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+        opRegRegImm(cUnit, kOpSub, r7, r5FP,
+                    sizeof(StackSaveArea) + (dInsn->vA << 2));
+        /* generate null check */
+        if (pcrLabel) {
+            *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+                                     mir->offset, NULL);
+        }
+        storeMultiple(cUnit, r7, regMask);
+    }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+                                DecodedInstruction *dInsn,
+                                ArmLIR **pcrLabel)
+{
+    int srcOffset = dInsn->vC << 2;
+    int numArgs = dInsn->vA;
+    int regMask;
+
+    /*
+     * Note: here, all promoted registers will have been flushed
+     * back to the Dalvik base locations, so register usage restrictins
+     * are lifted.  All parms loaded from original Dalvik register
+     * region - even though some might conceivably have valid copies
+     * cached in a preserved register.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * r4PC     : &r5FP[vC]
+     * r7: &newFP[0]
+     */
+    opRegRegImm(cUnit, kOpAdd, r4PC, r5FP, srcOffset);
+    /* load [r0 .. min(numArgs,4)] */
+    regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+    /*
+     * Protect the loadMultiple instruction from being reordered with other
+     * Dalvik stack accesses.
+     */
+    if (numArgs != 0) loadMultiple(cUnit, r4PC, regMask);
+
+    opRegRegImm(cUnit, kOpSub, r7, r5FP,
+                sizeof(StackSaveArea) + (numArgs << 2));
+    /* generate null check */
+    if (pcrLabel) {
+        *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+                                 mir->offset, NULL);
+    }
+
+    /*
+     * Handle remaining 4n arguments:
+     * store previously loaded 4 values and load the next 4 values
+     */
+    if (numArgs >= 8) {
+        ArmLIR *loopLabel = NULL;
+        /*
+         * r0 contains "this" and it will be used later, so push it to the stack
+         * first. Pushing r5FP is just for stack alignment purposes.
+         */
+        opImm(cUnit, kOpPush, (1 << r0 | 1 << r5FP));
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2);
+            loopLabel = newLIR0(cUnit, kArmPseudoTargetLabel);
+            loopLabel->defMask = ENCODE_ALL;
+        }
+        storeMultiple(cUnit, r7, regMask);
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            opRegImm(cUnit, kOpSub, r5FP, 4);
+            genConditionalBranch(cUnit, kArmCondNe, loopLabel);
+        }
+    }
+
+    /* Save the last batch of loaded values */
+    if (numArgs != 0) storeMultiple(cUnit, r7, regMask);
+
+    /* Generate the loop epilogue - don't use r0 */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+    }
+    if (numArgs >= 8)
+        opImm(cUnit, kOpPop, (1 << r0 | 1 << r5FP));
+
+    /* Save the modulo 4 arguments */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        storeMultiple(cUnit, r7, regMask);
+    }
+}
+
+/*
+ * Generate code to setup the call stack then jump to the chaining cell if it
+ * is not a native method.
+ */
+static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, ArmLIR *labelList,
+                                     ArmLIR *pcrLabel,
+                                     const Method *calleeMethod)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  All temp & preserved registers may be used.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+    /* r1 = &retChainingCell */
+    ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r7 = calleeMethod->registersSize */
+    loadConstant(cUnit, r7, calleeMethod->registersSize);
+    /*
+     * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+     * r1 = &ChainingCell
+     * r2 = calleeMethod->outsSize (to be loaded later for Java callees)
+     * r4PC = callsiteDPC
+     * r7 = calleeMethod->registersSize
+     */
+    if (dvmIsNativeMethod(calleeMethod)) {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_NATIVE_PROF :
+            TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeNative++;
+#endif
+    } else {
+        /* For Java callees, set up r2 to be calleeMethod->outsSize */
+        loadConstant(cUnit, r2, calleeMethod->outsSize);
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_CHAIN_PROF :
+            TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonomorphic++;
+#endif
+        /* Branch to the chaining cell */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/*
+ * Generate code to check the validity of a predicted chain and take actions
+ * based on the result.
+ *
+ * 0x426a99aa : ldr     r4, [pc, #72] --> r4 <- dalvikPC of this invoke
+ * 0x426a99ac : add     r1, pc, #32   --> r1 <- &retChainingCell
+ * 0x426a99ae : add     r2, pc, #40   --> r2 <- &predictedChainingCell
+ * 0x426a99b0 : blx_1   0x426a918c    --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+ * 0x426a99b2 : blx_2   see above     --+
+ * 0x426a99b4 : b       0x426a99d8    --> off to the predicted chain
+ * 0x426a99b6 : b       0x426a99c8    --> punt to the interpreter
+ * 0x426a99b8 : ldr     r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx]
+ * 0x426a99ba : cmp     r1, #0        --> compare r1 (rechain count) against 0
+ * 0x426a99bc : bgt     0x426a99c2    --> >=0? don't rechain
+ * 0x426a99be : ldr     r7, [pc, #off]--+ dvmJitToPatchPredictedChain
+ * 0x426a99c0 : blx     r7            --+
+ * 0x426a99c2 : add     r1, pc, #12   --> r1 <- &retChainingCell
+ * 0x426a99c4 : blx_1   0x426a9098    --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x426a99c6 : blx_2   see above     --+
+ */
+static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir,
+                                   int methodIndex,
+                                   ArmLIR *retChainingCell,
+                                   ArmLIR *predChainingCell,
+                                   ArmLIR *pcrLabel)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  Lock temps to prevent them from being
+     * allocated by utility routines.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * For verbose printing, store the method pointer in operands[1] first as
+     * operands[0] will be clobbered in dvmCompilerMIR2LIR.
+     */
+    predChainingCell->operands[1] = (int) mir->meta.callsiteInfo->method;
+
+    /* "this" is already left in r0 by genProcessArgs* */
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+
+    /* r1 = &retChainingCell */
+    ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r2 = &predictedChainingCell */
+    ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+    /* return through lr - jump to the chaining cell */
+    genUnconditionalBranch(cUnit, predChainingCell);
+
+    /*
+     * null-check on "this" may have been eliminated, but we still need a PC-
+     * reconstruction label for stack overflow bailout.
+     */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + mir->offset);
+        pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+        pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = mir->offset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+
+    /* return through lr+2 - punt to the interpreter */
+    genUnconditionalBranch(cUnit, pcrLabel);
+
+    /*
+     * return through lr+4 - fully resolve the callee method.
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+
+    /* r0 <- calleeMethod */
+    loadWordDisp(cUnit, r7, methodIndex * 4, r0);
+
+    /* Check if rechain limit is reached */
+    ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt, r1, 0);
+
+    LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain);
+
+    genRegCopy(cUnit, r1, r6SELF);
+
+    /*
+     * r0 = calleeMethod
+     * r2 = &predictedChainingCell
+     * r3 = class
+     *
+     * &returnChainingCell has been loaded into r1 but is not needed
+     * when patching the chaining cell and will be clobbered upon
+     * returning so it will be reconstructed again.
+     */
+    opReg(cUnit, kOpBlx, r7);
+
+    /* r1 = &retChainingCell */
+    addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    bypassRechaining->generic.target = (LIR *) addrRetChain;
+    /*
+     * r0 = calleeMethod,
+     * r1 = &ChainingCell,
+     * r4PC = callsiteDPC,
+     */
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+        TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.invokePolymorphic++;
+#endif
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* "this" pointer is already in r0 */
+static void genInvokeVirtualWholeMethod(CompilationUnit *cUnit,
+                                        MIR *mir,
+                                        void *calleeAddr,
+                                        ArmLIR *retChainingCell)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    dvmCompilerLockAllTemps(cUnit);
+
+    loadClassPointer(cUnit, r1, (int) callsiteInfo);
+
+    loadWordDisp(cUnit, r0, offsetof(Object, clazz), r2);
+    /* Branch to the slow path if classes are not equal */
+    opRegReg(cUnit, kOpCmp, r1, r2);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    ArmLIR *classCheck = opCondBranch(cUnit, kArmCondNe);
+
+    /* r0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr);
+    newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    /* Target of slow path */
+    ArmLIR *slowPathLabel = newLIR0(cUnit, kArmPseudoTargetLabel);
+
+    slowPathLabel->defMask = ENCODE_ALL;
+    classCheck->generic.target = (LIR *) slowPathLabel;
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+static void genInvokeSingletonWholeMethod(CompilationUnit *cUnit,
+                                          MIR *mir,
+                                          void *calleeAddr,
+                                          ArmLIR *retChainingCell)
+{
+    /* r0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr);
+    newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+    /* r0 = dalvik pc */
+    dvmCompilerFlushAllRegs(cUnit);
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset));
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpPunt), r1);
+    opReg(cUnit, kOpBlx, r1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+    int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+    int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn;
+
+    // Single stepping is considered loop mode breaker
+    if (cUnit->jitMode == kJitLoop) {
+        cUnit->quitLoopMode = true;
+        return;
+    }
+
+    //If already optimized out, just ignore
+    if (mir->dalvikInsn.opcode == OP_NOP)
+        return;
+
+    //Ugly, but necessary.  Flush all Dalvik regs so Interp can find them
+    dvmCompilerFlushAllRegs(cUnit);
+
+    if ((mir->next == NULL) || (flags & flagsToCheck)) {
+       genPuntToInterp(cUnit, mir->offset);
+       return;
+    }
+    int entryAddr = offsetof(Thread,
+                             jitToInterpEntries.dvmJitToInterpSingleStep);
+    loadWordDisp(cUnit, r6SELF, entryAddr, r2);
+    /* r0 = dalvik pc */
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+    /* r1 = dalvik pc of following instruction */
+    loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset));
+    opReg(cUnit, kOpBlx, r2);
+}
+
+#if defined(_ARMV5TE) || defined(_ARMV5TE_VFP)
+/*
+ * To prevent a thread in a monitor wait from blocking the Jit from
+ * resetting the code cache, heavyweight monitor lock will not
+ * be allowed to return to an existing translation.  Instead, we will
+ * handle them by branching to a handler, which will in turn call the
+ * runtime lock routine and then branch directly back to the
+ * interpreter main loop.  Given the high cost of the heavyweight
+ * lock operation, this additional cost should be slight (especially when
+ * considering that we expect the vast majority of lock operations to
+ * use the fast-path thin lock bypass).
+ */
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    bool isEnter = (mir->dalvikInsn.opcode == OP_MONITOR_ENTER);
+    genExportPC(cUnit, mir);
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);
+    genRegCopy(cUnit, r0, r6SELF);
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    if (isEnter) {
+        /* Get dPC of next insn */
+        loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    } else {
+        LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject);
+        /* Do the call */
+        opReg(cUnit, kOpBlx, r2);
+        /* Did we throw? */
+        ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+        loadConstant(cUnit, r0,
+                     (int) (cUnit->method->insns + mir->offset +
+                     dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+        genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+        ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+        target->defMask = ENCODE_ALL;
+        branchOver->generic.target = (LIR *) target;
+        dvmCompilerClobberCallRegs(cUnit);
+    }
+}
+#endif
+
+/*
+ * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
+ * punt to the interpreter.
+ */
+static void genSuspendPoll(CompilationUnit *cUnit, MIR *mir)
+{
+    int rTemp = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *ld;
+    ld = loadBaseDisp(cUnit, NULL, r6SELF,
+                      offsetof(Thread, interpBreak.ctl.breakFlags),
+                      rTemp, kUnsignedByte, INVALID_SREG);
+    setMemRefType(ld, true /* isLoad */, kMustNotAlias);
+    genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, ArmLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    int numPredecessors = dvmCountSetBits(bb->taken->predecessors);
+    /*
+     * Things could be hoisted out of the taken block into the predecessor, so
+     * make sure it is dominated by the predecessor.
+     */
+    if (numPredecessors == 1 && bb->taken->visited == false &&
+        bb->taken->blockType == kDalvikByteCode) {
+        cUnit->nextCodegenBlock = bb->taken;
+    } else {
+        /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    if ((dalvikOpcode >= OP_UNUSED_3E) && (dalvikOpcode <= OP_UNUSED_43)) {
+        ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+        return true;
+    }
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID_BARRIER:
+            dvmCompilerGenMemBarrier(cUnit, kISHST);
+            // Intentional fallthrough
+        case OP_RETURN_VOID:
+            genReturnCommon(cUnit,mir);
+            break;
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_FF:
+            ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+            return true;
+        case OP_NOP:
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST:
+        case OP_CONST_4: {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_32: {
+            //TUNING: single routine to load constant pair for support doubles
+            //TUNING: load 0/-1 separately to avoid load dependency
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_HIGH16: {
+            loadConstantNoClobber(cUnit, rlResult.lowReg,
+                                  mir->dalvikInsn.vB << 16);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_HIGH16: {
+            loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg,
+                                  0, mir->dalvikInsn.vB << 16);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    /* For OP_THROW_VERIFICATION_ERROR */
+    genInterpSingleStep(cUnit, mir);
+    return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlResult;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+
+            if (strPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null string");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET:
+        case OP_SGET_VOLATILE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            bool isVolatile;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            /*
+             * On SMP systems, Dalvik opcodes found to be referencing
+             * volatile fields are rewritten to their _VOLATILE variant.
+             * However, this does not happen on non-SMP systems. The JIT
+             * still needs to know about volatility to avoid unsafe
+             * optimizations so we determine volatility based on either
+             * the opcode or the field access flags.
+             */
+#if ANDROID_SMP != 0
+            Opcode opcode = mir->dalvikInsn.opcode;
+            isVolatile = (opcode == OP_SGET_VOLATILE) ||
+                         (opcode == OP_SGET_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kISH);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET_WIDE: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SPUT:
+        case OP_SPUT_VOLATILE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int objHead;
+            bool isVolatile;
+            bool isSputObject;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+            Opcode opcode = mir->dalvikInsn.opcode;
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            isVolatile = (opcode == OP_SPUT_VOLATILE) ||
+                         (opcode == OP_SPUT_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            isSputObject = (opcode == OP_SPUT_OBJECT) ||
+                           (opcode == OP_SPUT_OBJECT_VOLATILE);
+
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr);
+            if (isSputObject) {
+                objHead = dvmCompilerAllocTemp(cUnit);
+                loadWordDisp(cUnit, tReg, OFFSETOF_MEMBER(Field, clazz), objHead);
+            }
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kISHST);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kISH);
+            }
+            if (isSputObject) {
+                /* NOTE: marking card based sfield->clazz */
+                markCard(cUnit, rlSrc.lowReg, objHead);
+                dvmCompilerFreeTemp(cUnit, objHead);
+            }
+
+            break;
+        }
+        case OP_SPUT_WIDE: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg);
+            HEAP_ACCESS_SHADOW(false);
+            break;
+        }
+        case OP_NEW_INSTANCE: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr = (ClassObject *)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            /*
+             * If it is going to throw, it should not make to the trace to begin
+             * with.  However, Alloc might throw, so we need to genExportPC()
+             */
+            assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmAllocObject);
+            loadConstant(cUnit, r0, (int) classPtr);
+            loadConstant(cUnit, r1, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CHECK_CAST: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadConstant(cUnit, r1, (int) classPtr );
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            /* Null? */
+            ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq,
+                                              rlSrc.lowReg, 0);
+            /*
+             *  rlSrc.lowReg now contains object->clazz.  Note that
+             *  it could have been allocated r0, but we're okay so long
+             *  as we don't do anything desctructive until r0 is loaded
+             *  with clazz.
+             */
+            /* r0 now contains object->clazz */
+            loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r0);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInstanceofNonTrivial);
+            opRegReg(cUnit, kOpCmp, r0, r1);
+            ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /*
+             * If null, check cast failed - punt to the interpreter.  Because
+             * interpreter will be the one throwing, we don't need to
+             * genExportPC() here.
+             */
+            genZeroCheck(cUnit, r0, mir->offset, NULL);
+            /* check cast passed - branch target here */
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * A typical example of inlined getter/setter from a monomorphic callsite:
+ *
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0000 @ invoke-static (I)
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0000 @ sget-object (C) v0, ...
+ * D/dalvikvm(  289): 0x4427fc22 (0002): ldr     r0, [pc, #56]
+ * D/dalvikvm(  289): 0x4427fc24 (0004): ldr     r1, [r0, #0]
+ * D/dalvikvm(  289): 0x4427fc26 (0006): str     r1, [r5, #0]
+ * D/dalvikvm(  289): 0x4427fc28 (0008): .align4
+ * D/dalvikvm(  289): L0x0003:
+ * D/dalvikvm(  289): -------- dalvik offset: 0x0003 @ move-result-object (I) v0
+ *
+ * Note the invoke-static and move-result-object with the (I) notation are
+ * turned into no-op.
+ */
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlResult;
+    switch (dalvikOpcode) {
+        case OP_MOVE_EXCEPTION: {
+            int exOffset = offsetof(Thread, exception);
+            int resetReg = dvmCompilerAllocTemp(cUnit);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, r6SELF, exOffset, rlResult.lowReg);
+            loadConstant(cUnit, resetReg, 0);
+            storeWordDisp(cUnit, r6SELF, exOffset, resetReg);
+            storeValue(cUnit, rlDest, rlResult);
+           break;
+        }
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL;
+            rlSrc.fp = rlDest.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_MOVE_RESULT_WIDE: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlSrc.fp = rlDest.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_RETURN_WIDE: {
+            RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlDest.fp = rlSrc.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit,mir);
+            break;
+        }
+        case OP_RETURN:
+        case OP_RETURN_OBJECT: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL;
+            rlDest.fp = rlSrc.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit, mir);
+            break;
+        }
+        case OP_MONITOR_EXIT:
+        case OP_MONITOR_ENTER:
+            genMonitor(cUnit, mir);
+            break;
+        case OP_THROW:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+    RegLocation rlResult;
+
+    if ( (opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    if (mir->ssaRep->numUses == 2)
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    else
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    if (mir->ssaRep->numDefs == 2)
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    else
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+
+    switch (opcode) {
+        case OP_DOUBLE_TO_INT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+            return genConversion(cUnit, mir);
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+            return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+            return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_FLOAT:
+            return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_DOUBLE:
+            return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_MOVE_WIDE:
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_LONG:
+            rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            //TUNING: shouldn't loadValueDirect already check for phys reg?
+            if (rlSrc.location == kLocPhysReg) {
+                genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+            } else {
+                loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
+            }
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        case OP_LONG_TO_INT:
+            rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+            rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc);
+            // Intentional fallthrough
+        case OP_MOVE:
+        case OP_MOVE_OBJECT:
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_BYTE:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_SHORT:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_CHAR:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_ARRAY_LENGTH: {
+            int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg,
+                         mir->offset, NULL);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rlSrc.lowReg, lenOffset,
+                         rlResult.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlResult;
+    int BBBB = mir->dalvikInsn.vB;
+    if (dalvikOpcode == OP_CONST_WIDE_16) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        //TUNING: do high separately to avoid load dependency
+        opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (dalvikOpcode == OP_CONST_16) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        storeValue(cUnit, rlDest, rlResult);
+    } else
+        return true;
+    return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    ArmConditionCode cond;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQZ:
+            cond = kArmCondEq;
+            break;
+        case OP_IF_NEZ:
+            cond = kArmCondNe;
+            break;
+        case OP_IF_LTZ:
+            cond = kArmCondLt;
+            break;
+        case OP_IF_GEZ:
+            cond = kArmCondGe;
+            break;
+        case OP_IF_GTZ:
+            cond = kArmCondGt;
+            break;
+        case OP_IF_LEZ:
+            cond = kArmCondLe;
+            break;
+        default:
+            cond = (ArmConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt21t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+    ArmLIR* branch = genCmpImmBranch(cUnit, cond, rlSrc.lowReg, 0);
+    branch->generic.target = (LIR*)&labelList[bb->taken->id];
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool isPowerOfTwo(int x)
+{
+    return (x & (x - 1)) == 0;
+}
+
+// Returns true if no more than two bits are set in 'x'.
+static bool isPopCountLE2(unsigned int x)
+{
+    x &= x - 1;
+    return (x & (x - 1)) == 0;
+}
+
+// Returns the index of the lowest set bit in 'x'.
+static int lowestSetBit(unsigned int x) {
+    int bit_posn = 0;
+    while ((x & 0xf) == 0) {
+        bit_posn += 4;
+        x >>= 4;
+    }
+    while ((x & 1) == 0) {
+        bit_posn++;
+        x >>= 1;
+    }
+    return bit_posn;
+}
+
+// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyDivide(CompilationUnit *cUnit, Opcode dalvikOpcode,
+                             RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    if (lit < 2 || !isPowerOfTwo(lit)) {
+        return false;
+    }
+    int k = lowestSetBit(lit);
+    if (k >= 30) {
+        // Avoid special cases.
+        return false;
+    }
+    bool div = (dalvikOpcode == OP_DIV_INT_LIT8 || dalvikOpcode == OP_DIV_INT_LIT16);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (div) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            // Division by 2 is by far the most common division by constant.
+            opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        }
+    } else {
+        int cReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, cReg, lit - 1);
+        int tReg1 = dvmCompilerAllocTemp(cUnit);
+        int tReg2 = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        }
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyMultiply(CompilationUnit *cUnit,
+                               RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    // Can we simplify this multiplication?
+    bool powerOfTwo = false;
+    bool popCountLE2 = false;
+    bool powerOfTwoMinusOne = false;
+    if (lit < 2) {
+        // Avoid special cases.
+        return false;
+    } else if (isPowerOfTwo(lit)) {
+        powerOfTwo = true;
+    } else if (isPopCountLE2(lit)) {
+        popCountLE2 = true;
+    } else if (isPowerOfTwo(lit + 1)) {
+        powerOfTwoMinusOne = true;
+    } else {
+        return false;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (powerOfTwo) {
+        // Shift.
+        opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
+                    lowestSetBit(lit));
+    } else if (popCountLE2) {
+        // Shift and add and shift.
+        int firstBit = lowestSetBit(lit);
+        int secondBit = lowestSetBit(lit ^ (1 << firstBit));
+        genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
+                                      firstBit, secondBit);
+    } else {
+        // Reverse subtract: (src << (shift + 1)) - src.
+        assert(powerOfTwoMinusOne);
+        genMultiplyByShiftAndReverseSubtract(cUnit, rlSrc, rlResult, lowestSetBit(lit + 1));
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    RegLocation rlResult;
+    int lit = mir->dalvikInsn.vC;
+    OpKind op = (OpKind)0;      /* Make gcc happy */
+    int shiftOp = false;
+    bool isDiv = false;
+    bool isRem = false;
+
+    switch (dalvikOpcode) {
+        case OP_RSUB_INT_LIT8:
+        case OP_RSUB_INT: {
+            int tReg;
+            //TUNING: add support for use of Arm rsub op
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            tReg = dvmCompilerAllocTemp(cUnit);
+            loadConstant(cUnit, tReg, lit);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+                        tReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+
+        case OP_ADD_INT_LIT8:
+        case OP_ADD_INT_LIT16:
+            op = kOpAdd;
+            break;
+        case OP_MUL_INT_LIT8:
+        case OP_MUL_INT_LIT16: {
+            if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
+                return false;
+            }
+            op = kOpMul;
+            break;
+        }
+        case OP_AND_INT_LIT8:
+        case OP_AND_INT_LIT16:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT_LIT8:
+        case OP_OR_INT_LIT16:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT_LIT8:
+        case OP_XOR_INT_LIT16:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+
+        case OP_DIV_INT_LIT8:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT8:
+        case OP_REM_INT_LIT16:
+            if (lit == 0) {
+                /* Let the interpreter deal with div by 0 */
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
+                return false;
+            }
+#ifdef __ARM_ARCH_EXT_IDIV__
+            if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
+                (dalvikOpcode == OP_DIV_INT_LIT16)) {
+                op = kOpDiv;
+            }
+            else {
+                isRem = true;
+                op = kOpRem;
+            }
+            break;
+#endif
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r0);
+            dvmCompilerClobber(cUnit, r0);
+            if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
+                (dalvikOpcode == OP_DIV_INT_LIT16)) {
+                LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idiv);
+                isDiv = true;
+            } else {
+                LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idivmod);
+                isDiv = false;
+            }
+            loadConstant(cUnit, r1, lit);
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            if (isDiv)
+                rlResult = dvmCompilerGetReturn(cUnit);
+            else
+                rlResult = dvmCompilerGetReturnAlt(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        default:
+            return true;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    // Avoid shifts by literal 0 - no support in Thumb.  Change to copy
+    if (shiftOp && (lit == 0)) {
+        genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    } else if(isRem) {
+        int tReg1 = dvmCompilerAllocTemp(cUnit);
+        int tReg2 = dvmCompilerAllocTemp(cUnit);
+
+        loadConstant(cUnit, tReg2, lit);
+        opRegRegReg(cUnit, kOpDiv, tReg1, rlSrc.lowReg, tReg2);
+        opRegRegRegReg(cUnit, op, rlResult.lowReg, tReg2, tReg1, rlSrc.lowReg);
+        dvmCompilerFreeTemp(cUnit, tReg1);
+        dvmCompilerFreeTemp(cUnit, tReg2);
+    } else {
+        opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset = -1;
+    bool isVolatile = false;
+    switch (dalvikOpcode) {
+        /*
+         * Wide volatiles currently handled via single step.
+         * Add them here if generating in-line code.
+         *     case OP_IGET_WIDE_VOLATILE:
+         *     case OP_IPUT_WIDE_VOLATILE:
+         */
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+#if ANDROID_SMP != 0
+            isVolatile = true;
+        // NOTE: intentional fallthrough
+#endif
+        case OP_IGET:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT: {
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            Field *fieldPtr =
+                method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null instance field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+            fieldOffset = ((InstField *)fieldPtr)->byteOffset;
+            break;
+        }
+        default:
+            break;
+    }
+
+    switch (dalvikOpcode) {
+        case OP_NEW_ARRAY: {
+            // Generates a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r1);   /* Len */
+            loadConstant(cUnit, r0, (int) classPtr );
+            LOAD_FUNC_ADDR(cUnit, r3, (int)dvmAllocArrayByClass);
+            /*
+             * "len < 0": bail to the interpreter to re-execute the
+             * instruction
+             */
+            genRegImmCheck(cUnit, kArmCondMi, r1, 0, mir->offset, NULL);
+            loadConstant(cUnit, r2, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r3);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_INSTANCE_OF: {
+            // May generate a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                break;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */
+            loadConstant(cUnit, r2, (int) classPtr );
+            /* When taken r0 has NULL which can be used for store directly */
+            ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
+            /* r1 now contains object->clazz */
+            loadWordDisp(cUnit, r0, offsetof(Object, clazz), r1);
+            /* r1 now contains object->clazz */
+            LOAD_FUNC_ADDR(cUnit, r3, (int)dvmInstanceofNonTrivial);
+            loadConstant(cUnit, r0, 1);                /* Assume true */
+            opRegReg(cUnit, kOpCmp, r1, r2);
+            ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+            genRegCopy(cUnit, r0, r1);
+            genRegCopy(cUnit, r1, r2);
+            opReg(cUnit, kOpBlx, r3);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* branch target here */
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_IGET_WIDE:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
+            break;
+        case OP_IPUT_WIDE:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+            break;
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
+            break;
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset =  mir->dalvikInsn.vC;
+    switch (dalvikOpcode) {
+        case OP_IGET_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+            genIGet(cUnit, mir, kWord, fieldOffset, false);
+            break;
+        case OP_IPUT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, false);
+            break;
+        case OP_IPUT_OBJECT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, false);
+            break;
+        case OP_IGET_WIDE_QUICK:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_WIDE_QUICK:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        default:
+            return true;
+    }
+    return false;
+
+}
+
+/* Compare agaist zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    ArmConditionCode cond;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+
+    opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQ:
+            cond = kArmCondEq;
+            break;
+        case OP_IF_NE:
+            cond = kArmCondNe;
+            break;
+        case OP_IF_LT:
+            cond = kArmCondLt;
+            break;
+        case OP_IF_GE:
+            cond = kArmCondGe;
+            break;
+        case OP_IF_GT:
+            cond = kArmCondGt;
+            break;
+        case OP_IF_LE:
+            cond = kArmCondLe;
+            break;
+        default:
+            cond = (ArmConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt22t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+    genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_MOVE_16:
+        case OP_MOVE_OBJECT_16:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_OBJECT_FROM16: {
+            storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0),
+                       dvmCompilerGetSrc(cUnit, mir, 0));
+            break;
+        }
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_WIDE_FROM16: {
+            storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1),
+                           dvmCompilerGetSrcWide(cUnit, mir, 0, 1));
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    RegLocation rlDest;
+
+    if ( (opcode >= OP_ADD_INT) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    /* APUTs have 3 sources and no targets */
+    if (mir->ssaRep->numDefs == 0) {
+        if (mir->ssaRep->numUses == 3) {
+            rlDest = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+        } else {
+            assert(mir->ssaRep->numUses == 4);
+            rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3);
+        }
+    } else {
+        /* Two sources and 1 dest.  Deduce the operand sizes */
+        if (mir->ssaRep->numUses == 4) {
+            rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        } else {
+            assert(mir->ssaRep->numUses == 2);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+        }
+        if (mir->ssaRep->numDefs == 2) {
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        } else {
+            assert(mir->ssaRep->numDefs == 1);
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        }
+    }
+
+
+    switch (opcode) {
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+            return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        case OP_CMP_LONG:
+            genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+            break;
+        case OP_AGET_WIDE:
+            genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+            genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_AGET_BOOLEAN:
+            genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_BYTE:
+            genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_CHAR:
+            genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_AGET_SHORT:
+            genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_WIDE:
+            genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_APUT:
+            genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_OBJECT:
+            genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_SHORT:
+        case OP_APUT_CHAR:
+            genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_BYTE:
+        case OP_APUT_BOOLEAN:
+            genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * Find the matching case.
+ *
+ * return values:
+ * r0 (low 32-bit): pc of the chaining cell corresponding to the resolved case,
+ *    including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES).
+ * r1 (high 32-bit): the branch offset of the matching case (only for indexes
+ *    above MAX_CHAINED_SWITCH_CASES).
+ *
+ * Instructions around the call are:
+ *
+ * mov r2, pc
+ * blx &findPackedSwitchIndex
+ * mov pc, r0
+ * .align4
+ * chaining cell for case 0 [12 bytes]
+ * chaining cell for case 1 [12 bytes]
+ *               :
+ * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [12 bytes]
+ * chaining cell for case default [8 bytes]
+ * noChain exit
+ */
+static u8 findPackedSwitchIndex(const u2* switchData, int testVal, uintptr_t pc)
+{
+    int size;
+    int firstKey;
+    const int *entries;
+    int index;
+    int jumpIndex;
+    uintptr_t caseDPCOffset = 0;
+    /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+    uintptr_t chainingPC = (pc + 4) & ~3;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    size = switchData[1];
+    assert(size > 0);
+
+    firstKey = switchData[2];
+    firstKey |= switchData[3] << 16;
+
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = (const int*) &switchData[4];
+    assert(((u4)entries & 0x3) == 0);
+
+    index = testVal - firstKey;
+
+    /* Jump to the default cell */
+    if (index < 0 || index >= size) {
+        jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES);
+    /* Jump to the non-chaining exit point */
+    } else if (index >= MAX_CHAINED_SWITCH_CASES) {
+        jumpIndex = MAX_CHAINED_SWITCH_CASES + 1;
+        caseDPCOffset = entries[index];
+    /* Jump to the inline chaining cell */
+    } else {
+        jumpIndex = index;
+    }
+
+    chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+    return (((u8) caseDPCOffset) << 32) | (u8) chainingPC;
+}
+
+/* See comments for findPackedSwitchIndex */
+static u8 findSparseSwitchIndex(const u2* switchData, int testVal, uintptr_t pc)
+{
+    int size;
+    const int *keys;
+    const int *entries;
+    uintptr_t chainingPC = (pc + 4) & ~3;
+    int i;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    size = switchData[1];
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const int*) &switchData[2];
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Run through the list of keys, which are guaranteed to
+     * be sorted low-to-high.
+     *
+     * Most tables have 3-4 entries.  Few have more than 10.  A binary
+     * search here is probably not useful.
+     */
+    for (i = 0; i < size; i++) {
+        int k = keys[i];
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+            return (((u8) entries[i]) << 32) | (u8) chainingPC;
+        } else if (k > testVal) {
+            break;
+        }
+    }
+    return chainingPC + MIN(size, MAX_CHAINED_SWITCH_CASES) *
+           CHAIN_CELL_NORMAL_SIZE;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_FILL_ARRAY_DATA: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            // Making a call - use explicit registers
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r0);
+            LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInterpHandleFillArrayData);
+            loadConstant(cUnit, r1,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r2);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if successful */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            break;
+        }
+        /*
+         * Compute the goto target of up to
+         * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells.
+         * See the comment before findPackedSwitchIndex for the code layout.
+         */
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r1);
+            dvmCompilerLockAllTemps(cUnit);
+            if (dalvikOpcode == OP_PACKED_SWITCH) {
+                LOAD_FUNC_ADDR(cUnit, r4PC, (int)findPackedSwitchIndex);
+            } else {
+                LOAD_FUNC_ADDR(cUnit, r4PC, (int)findSparseSwitchIndex);
+            }
+            /* r0 <- Addr of the switch data */
+            loadConstant(cUnit, r0,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            /* r2 <- pc of the instruction following the blx */
+            opRegReg(cUnit, kOpMov, r2, r15pc);
+            opReg(cUnit, kOpBlx, r4PC);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* pc <- computed goto target */
+            opRegReg(cUnit, kOpMov, r15pc, r0);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * See the example of predicted inlining listed before the
+ * genValidationForPredictedInline function. The function here takes care the
+ * branch over at 0x4858de78 and the misprediction target at 0x4858de7a.
+ */
+static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir,
+                                               BasicBlock *bb,
+                                               ArmLIR *labelList)
+{
+    BasicBlock *fallThrough = bb->fallThrough;
+
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    genUnconditionalBranch(cUnit, &labelList[fallThrough->id]);
+
+    /* Reset the register state */
+    dvmCompilerResetRegPool(cUnit);
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    /* Target for the slow invoke path */
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    /* Hook up the target to the verification branch */
+    mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                             BasicBlock *bb, ArmLIR *labelList)
+{
+    ArmLIR *retChainingCell = NULL;
+    ArmLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    if (bb->fallThrough != NULL)
+        retChainingCell = &labelList[bb->fallThrough->id];
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /*
+         * calleeMethod = this->clazz->vtable[
+         *     method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+         * ]
+         */
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE: {
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+            int methodIndex =
+                cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+                methodIndex;
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /*
+         * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+         *                ->pResMethods[BBBB]->methodIndex]
+         */
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod == cUnit->method->clazz->super->vtable[
+                                     cUnit->method->clazz->pDvmDex->
+                                       pResMethods[dInsn->vB]->methodIndex]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r0 = calleeMethod */
+                loadConstant(cUnit, r0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r0 = calleeMethod */
+            loadConstant(cUnit, r0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC)
+                genProcessArgsNoRange(cUnit, mir, dInsn,
+                                      NULL /* no null check */);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn,
+                                    NULL /* no null check */);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r0 = calleeMethod */
+                loadConstant(cUnit, r0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /*
+         * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+         *                    BBBB, method, method->clazz->pDvmDex)
+         *
+         * The following is an example of generated code for
+         *      "invoke-interface v0"
+         *
+         * -------- dalvik offset: 0x0008 @ invoke-interface v0
+         * 0x47357e36 : ldr     r0, [r5, #0]   --+
+         * 0x47357e38 : sub     r7,r5,#24        |
+         * 0x47357e3c : cmp     r0, #0           | genProcessArgsNoRange
+         * 0x47357e3e : beq     0x47357e82       |
+         * 0x47357e40 : stmia   r7, <r0>       --+
+         * 0x47357e42 : ldr     r4, [pc, #120] --> r4 <- dalvikPC of this invoke
+         * 0x47357e44 : add     r1, pc, #64    --> r1 <- &retChainingCell
+         * 0x47357e46 : add     r2, pc, #72    --> r2 <- &predictedChainingCell
+         * 0x47357e48 : blx_1   0x47348190     --+ TEMPLATE_INVOKE_METHOD_
+         * 0x47357e4a : blx_2   see above      --+     PREDICTED_CHAIN
+         * 0x47357e4c : b       0x47357e90     --> off to the predicted chain
+         * 0x47357e4e : b       0x47357e82     --> punt to the interpreter
+         * 0x47357e50 : mov     r8, r1         --+
+         * 0x47357e52 : mov     r9, r2           |
+         * 0x47357e54 : ldr     r2, [pc, #96]    |
+         * 0x47357e56 : mov     r10, r3          |
+         * 0x47357e58 : movs    r0, r3           | dvmFindInterfaceMethodInCache
+         * 0x47357e5a : ldr     r3, [pc, #88]    |
+         * 0x47357e5c : ldr     r7, [pc, #80]    |
+         * 0x47357e5e : mov     r1, #1452        |
+         * 0x47357e62 : blx     r7             --+
+         * 0x47357e64 : cmp     r0, #0         --> calleeMethod == NULL?
+         * 0x47357e66 : bne     0x47357e6e     --> branch over the throw if !r0
+         * 0x47357e68 : ldr     r0, [pc, #80]  --> load Dalvik PC of the invoke
+         * 0x47357e6a : blx_1   0x47348494     --+ TEMPLATE_THROW_EXCEPTION_
+         * 0x47357e6c : blx_2   see above      --+     COMMON
+         * 0x47357e6e : mov     r1, r8         --> r1 <- &retChainingCell
+         * 0x47357e70 : cmp     r1, #0         --> compare against 0
+         * 0x47357e72 : bgt     0x47357e7c     --> >=0? don't rechain
+         * 0x47357e74 : ldr     r7, [pc, #off] --+
+         * 0x47357e76 : mov     r2, r9           | dvmJitToPatchPredictedChain
+         * 0x47357e78 : mov     r3, r10          |
+         * 0x47357e7a : blx     r7             --+
+         * 0x47357e7c : add     r1, pc, #8     --> r1 <- &retChainingCell
+         * 0x47357e7e : blx_1   0x4734809c     --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+         * 0x47357e80 : blx_2   see above      --+
+         * -------- reconstruct dalvik PC : 0x425719dc @ +0x0008
+         * 0x47357e82 : ldr     r0, [pc, #56]
+         * Exception_Handling:
+         * 0x47357e84 : ldr     r1, [r6, #92]
+         * 0x47357e86 : blx     r1
+         * 0x47357e88 : .align4
+         * -------- chaining cell (hot): 0x000b
+         * 0x47357e88 : ldr     r0, [r6, #104]
+         * 0x47357e8a : blx     r0
+         * 0x47357e8c : data    0x19e2(6626)
+         * 0x47357e8e : data    0x4257(16983)
+         * 0x47357e90 : .align4
+         * -------- chaining cell (predicted)
+         * 0x47357e90 : data    0xe7fe(59390)  --> will be patched into bx
+         * 0x47357e92 : data    0x0000(0)
+         * 0x47357e94 : data    0x0000(0)      --> class
+         * 0x47357e96 : data    0x0000(0)
+         * 0x47357e98 : data    0x0000(0)      --> method
+         * 0x47357e9a : data    0x0000(0)
+         * 0x47357e9c : data    0x0000(0)      --> rechain count
+         * 0x47357e9e : data    0x0000(0)
+         * -------- end of chaining cells (0x006c)
+         * 0x47357eb0 : .word (0xad03e369)
+         * 0x47357eb4 : .word (0x28a90)
+         * 0x47357eb8 : .word (0x41a63394)
+         * 0x47357ebc : .word (0x425719dc)
+         */
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE: {
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* "this" is already left in r0 by genProcessArgs* */
+
+            /* r4PC = dalvikCallsite */
+            loadConstant(cUnit, r4PC,
+                         (int) (cUnit->method->insns + mir->offset));
+
+            /* r1 = &retChainingCell */
+            ArmLIR *addrRetChain =
+                opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            /* r2 = &predictedChainingCell */
+            ArmLIR *predictedChainingCell =
+                opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+            /* return through lr - jump to the chaining cell */
+            genUnconditionalBranch(cUnit, predChainingCell);
+
+            /*
+             * null-check on "this" may have been eliminated, but we still need
+             * a PC-reconstruction label for stack overflow bailout.
+             */
+            if (pcrLabel == NULL) {
+                int dPC = (int) (cUnit->method->insns + mir->offset);
+                pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+                pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+                pcrLabel->operands[0] = dPC;
+                pcrLabel->operands[1] = mir->offset;
+                /* Insert the place holder to the growable list */
+                dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                                      (intptr_t) pcrLabel);
+            }
+
+            /* return through lr+2 - punt to the interpreter */
+            genUnconditionalBranch(cUnit, pcrLabel);
+
+            /*
+             * return through lr+4 - fully resolve the callee method.
+             * r1 <- count
+             * r2 <- &predictedChainCell
+             * r3 <- this->class
+             * r4 <- dPC
+             * r7 <- this->class->vtable
+             */
+
+            /* Save count, &predictedChainCell, and class to high regs first */
+            genRegCopy(cUnit, r8, r1);
+            genRegCopy(cUnit, r9, r2);
+            genRegCopy(cUnit, r10, r3);
+
+            /* r0 now contains this->clazz */
+            genRegCopy(cUnit, r0, r3);
+
+            /* r1 = BBBB */
+            loadConstant(cUnit, r1, dInsn->vB);
+
+            /* r2 = method (caller) */
+            loadConstant(cUnit, r2, (int) cUnit->method);
+
+            /* r3 = pDvmDex */
+            loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex);
+
+            LOAD_FUNC_ADDR(cUnit, r7,
+                           (intptr_t) dvmFindInterfaceMethodInCache);
+            opReg(cUnit, kOpBlx, r7);
+            /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if the interface method is resolved */
+            ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+            /*
+             * calleeMethod == NULL -> throw
+             */
+            loadConstant(cUnit, r0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+
+            genRegCopy(cUnit, r1, r8);
+
+            /* Check if rechain limit is reached */
+            ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt,
+                                                       r1, 0);
+
+            LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain);
+
+            genRegCopy(cUnit, r1, r6SELF);
+            genRegCopy(cUnit, r2, r9);
+            genRegCopy(cUnit, r3, r10);
+
+            /*
+             * r0 = calleeMethod
+             * r2 = &predictedChainingCell
+             * r3 = class
+             *
+             * &returnChainingCell has been loaded into r1 but is not needed
+             * when patching the chaining cell and will be clobbered upon
+             * returning so it will be reconstructed again.
+             */
+            opReg(cUnit, kOpBlx, r7);
+
+            /* r1 = &retChainingCell */
+            addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            bypassRechaining->generic.target = (LIR *) addrRetChain;
+
+            /*
+             * r0 = this, r1 = calleeMethod,
+             * r1 = &ChainingCell,
+             * r4PC = callsiteDPC,
+             */
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+                TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.invokePolymorphic++;
+#endif
+            /* Handle exceptions using the interpreter */
+            genTrap(cUnit, mir->offset, pcrLabel);
+            break;
+        }
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE: {
+            /* Just let the interpreter deal with these */
+            genInterpSingleStep(cUnit, mir);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                               BasicBlock *bb, ArmLIR *labelList)
+{
+    ArmLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /* calleeMethod = this->clazz->vtable[BBBB] */
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK: {
+            int methodIndex = dInsn->vB;
+            ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+            ArmLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeVirtualWholeMethod(cUnit, mir, calleeAddr,
+                                            retChainingCell);
+            }
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /* calleeMethod = method->clazz->super->vtable[BBBB] */
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->super->vtable[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r0 = calleeMethod */
+            loadConstant(cUnit, r0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * This operation is complex enough that we'll do it partly inline
+ * and partly with a handler.  NOTE: the handler uses hardcoded
+ * values for string object offsets and must be revisitied if the
+ * layout changes.
+ */
+static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    ArmLIR *rollback;
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r0);
+    loadValueDirectFixed(cUnit, rlComp, r1);
+    /* Test objects for NULL */
+    rollback = genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+    genNullCheck(cUnit, rlComp.sRegLow, r1, mir->offset, rollback);
+    /*
+     * TUNING: we could check for object pointer equality before invoking
+     * handler. Unclear whether the gain would be worth the added code size
+     * expansion.
+     */
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r0);
+    loadValueDirectFixed(cUnit, rlChar, r1);
+    RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2);
+    loadValueDirectFixed(cUnit, rlStart, r2);
+    /* Test objects for NULL */
+    genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir,
+                                            bool isEmpty)
+{
+    // dst = src.length();
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
+                 rlResult.lowReg);
+    if (isEmpty) {
+        // dst = (dst == 0);
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegReg(cUnit, kOpNeg, tReg, rlResult.lowReg);
+        opRegRegReg(cUnit, kOpAdc, rlResult.lowReg, rlResult.lowReg, tReg);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, false);
+}
+
+static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, true);
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+    int contents = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+    int regMax = dvmCompilerAllocTemp(cUnit);
+    int regOff = dvmCompilerAllocTemp(cUnit);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+    ArmLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
+                                    mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
+    genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
+    dvmCompilerFreeTemp(cUnit, regMax);
+    opRegImm(cUnit, kOpAdd, regPtr, contents);
+    opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2's IT block also yields 3 instructions, but imposes
+     * scheduling constraints.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2 IT block allows slightly shorter sequence,
+     * but introduces a scheduling barrier.  Stick with this
+     * mechanism for now.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    storeValue(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    storeValueWide(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+/*
+ * JITs a call to a C function.
+ * TODO: use this for faster native method invocation for simple native
+ * methods (http://b/3069458).
+ */
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int operation = dInsn->vB;
+    unsigned int i;
+    const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+    uintptr_t fn = (int) inLineTable[operation].func;
+    if (fn == 0) {
+        dvmCompilerAbort(cUnit);
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, r7);
+    int offset = offsetof(Thread, interpSave.retval);
+    opRegRegImm(cUnit, kOpAdd, r4PC, r6SELF, offset);
+    opImm(cUnit, kOpPush, (1<<r4PC) | (1<<r7));
+    LOAD_FUNC_ADDR(cUnit, r4PC, fn);
+    genExportPC(cUnit, mir);
+    for (i=0; i < dInsn->vA; i++) {
+        loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i);
+    }
+    opReg(cUnit, kOpBlx, r4PC);
+    opRegImm(cUnit, kOpAdd, r13sp, 8);
+    /* NULL? */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+    loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+    return false;
+}
+
+/*
+ * NOTE: Handles both range and non-range versions (arguments
+ * have already been normalized by this point).
+ */
+static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    assert(dInsn->opcode == OP_EXECUTE_INLINE_RANGE ||
+           dInsn->opcode == OP_EXECUTE_INLINE);
+    switch (dInsn->vB) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return false;  /* Nop */
+
+        /* These ones we potentially JIT inline. */
+
+        case INLINE_STRING_CHARAT:
+            return genInlinedStringCharAt(cUnit, mir);
+        case INLINE_STRING_LENGTH:
+            return genInlinedStringLength(cUnit, mir);
+        case INLINE_STRING_IS_EMPTY:
+            return genInlinedStringIsEmpty(cUnit, mir);
+        case INLINE_STRING_COMPARETO:
+            return genInlinedCompareTo(cUnit, mir);
+        case INLINE_STRING_FASTINDEXOF_II:
+            return genInlinedFastIndexOf(cUnit, mir);
+
+        case INLINE_MATH_ABS_INT:
+        case INLINE_STRICT_MATH_ABS_INT:
+            return genInlinedAbsInt(cUnit, mir);
+        case INLINE_MATH_ABS_LONG:
+        case INLINE_STRICT_MATH_ABS_LONG:
+            return genInlinedAbsLong(cUnit, mir);
+        case INLINE_MATH_MIN_INT:
+        case INLINE_STRICT_MATH_MIN_INT:
+            return genInlinedMinMaxInt(cUnit, mir, true);
+        case INLINE_MATH_MAX_INT:
+        case INLINE_STRICT_MATH_MAX_INT:
+            return genInlinedMinMaxInt(cUnit, mir, false);
+        case INLINE_MATH_SQRT:
+        case INLINE_STRICT_MATH_SQRT:
+            return genInlineSqrt(cUnit, mir);
+        case INLINE_MATH_ABS_FLOAT:
+        case INLINE_STRICT_MATH_ABS_FLOAT:
+            return genInlinedAbsFloat(cUnit, mir);
+        case INLINE_MATH_ABS_DOUBLE:
+        case INLINE_STRICT_MATH_ABS_DOUBLE:
+            return genInlinedAbsDouble(cUnit, mir);
+
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+        case INLINE_INT_BITS_TO_FLOAT:
+            return genInlinedIntFloatConversion(cUnit, mir);
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            return genInlinedLongDoubleConversion(cUnit, mir);
+
+        /*
+         * These ones we just JIT a call to a C function for.
+         * TODO: special-case these in the other "invoke" call paths.
+         */
+        case INLINE_STRING_EQUALS:
+        case INLINE_MATH_COS:
+        case INLINE_MATH_SIN:
+        case INLINE_FLOAT_TO_INT_BITS:
+        case INLINE_DOUBLE_TO_LONG_BITS:
+            return handleExecuteInlineC(cUnit, mir);
+    }
+    dvmCompilerAbort(cUnit);
+    return false; // Not reachable; keeps compiler happy.
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    //TUNING: We're using core regs here - not optimal when target is a double
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadConstantNoClobber(cUnit, rlResult.lowReg,
+                          mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+    loadConstantNoClobber(cUnit, rlResult.highReg,
+                          (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/*
+ * Insert a
+ *    b   .+4
+ *    nop
+ * pair at the beginning of a chaining cell.  This serves as the
+ * switch branch that selects between reverting to the interpreter or
+ * not.  Once the cell is chained to a translation, the cell will
+ * contain a 32-bit branch.  Subsequent chain/unchain operations will
+ * then only alter that first 16-bits - the "b .+4" for unchaining,
+ * and the restoration of the first half of the 32-bit branch for
+ * rechaining.
+ */
+static void insertChainingSwitch(CompilationUnit *cUnit)
+{
+    ArmLIR *branch = newLIR0(cUnit, kThumbBUncond);
+    newLIR2(cUnit, kThumbOrr, r0, r0);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *) target;
+}
+
+/* Chaining cell for code that may need warmup. */
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                             unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+#if defined(WITH_SELF_VERIFICATION)
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+        offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2);
+#else
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+#endif
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+    insertChainingSwitch(cUnit);
+    newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF,
+            offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, NULL, (int) (callee->insns));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+
+    /* Should not be executed in the initial state */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_BX_PAIR_INIT);
+    /* To be filled: class */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_CLAZZ_INIT);
+    /* To be filled: method */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_METHOD_INIT);
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_COUNTER_INIT);
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   ArmLIR *targetLabel)
+{
+    ArmLIR **pcrLabel =
+        (ArmLIR **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+
+    /*
+     * We should never reach here through fall-through code, so insert
+     * a bomb to signal troubles immediately.
+     */
+    if (numElems) {
+        newLIR0(cUnit, kThumbUndefined);
+    }
+
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* r0 = dalvik PC */
+        loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+}
+
+static const char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = {
+    "kMirOpPhi",
+    "kMirOpNullNRangeUpCheck",
+    "kMirOpNullNRangeDownCheck",
+    "kMirOpLowerBound",
+    "kMirOpPunt",
+    "kMirOpCheckInlinePrediction",
+};
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int maxC = dInsn->arg[0];
+    int regLength;
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg);
+    genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    regLength = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta);
+        rlIdxEnd.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+    /* Punt if "regIdxEnd < len(Array)" is false */
+    genRegRegCheck(cUnit, kArmCondGe, rlIdxEnd.lowReg, regLength, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int regLength = dvmCompilerAllocTemp(cUnit);
+    const int maxC = dInsn->arg[0];
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg);
+    genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    if (maxC) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC);
+        rlIdxInit.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+
+    /* Punt if "regIdxInit < len(Array)" is false */
+    genRegRegCheck(cUnit, kArmCondGe, rlIdxInit.lowReg, regLength, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA];
+
+    /* regIdx <- initial index value */
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+
+    /* Punt if "regIdxInit + minC >= 0" is false */
+    genRegImmCheck(cUnit, kArmCondLt, rlIdx.lowReg, -minC, 0,
+                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vC = this
+ *
+ * A predicted inlining target looks like the following, where instructions
+ * between 0x4858de66 and 0x4858de72 are checking if the predicted class
+ * matches "this", and the verificaion code is generated by this routine.
+ *
+ * (C) means the instruction is inlined from the callee, and (PI) means the
+ * instruction is the predicted inlined invoke, whose corresponding
+ * instructions are still generated to handle the mispredicted case.
+ *
+ * D/dalvikvm(   86): -------- kMirOpCheckInlinePrediction
+ * D/dalvikvm(   86): 0x4858de66 (0002): ldr     r0, [r5, #68]
+ * D/dalvikvm(   86): 0x4858de68 (0004): ldr     r1, [pc, #140]
+ * D/dalvikvm(   86): 0x4858de6a (0006): cmp     r0, #0
+ * D/dalvikvm(   86): 0x4858de6c (0008): beq     0x4858deb2
+ * D/dalvikvm(   86): 0x4858de6e (000a): ldr     r2, [r0, #0]
+ * D/dalvikvm(   86): 0x4858de70 (000c): cmp     r1, r2
+ * D/dalvikvm(   86): 0x4858de72 (000e): bne     0x4858de7a
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004c @ +iget-object-quick (C)
+ * v4, v17, (#8)
+ * D/dalvikvm(   86): 0x4858de74 (0010): ldr     r3, [r0, #8]
+ * D/dalvikvm(   86): 0x4858de76 (0012): str     r3, [r5, #16]
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004c @
+ * +invoke-virtual-quick/range (PI) v17..v17
+ * D/dalvikvm(   86): 0x4858de78 (0014): b       0x4858debc
+ * D/dalvikvm(   86): 0x4858de7a (0016): add     r4,r5,#68
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de7e (001a): ldmia   r4, <r0>
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de80 (001c): sub     r7,r5,#24
+ * D/dalvikvm(   86): 0x4858de84 (0020): cmp     r0, #0
+ * D/dalvikvm(   86): 0x4858de86 (0022): beq     0x4858deb6
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de88 (0024): stmia   r7, <r0>
+ * D/dalvikvm(   86): -------- BARRIER
+ * D/dalvikvm(   86): 0x4858de8a (0026): ldr     r4, [pc, #104]
+ * D/dalvikvm(   86): 0x4858de8c (0028): add     r1, pc, #28
+ * D/dalvikvm(   86): 0x4858de8e (002a): add     r2, pc, #56
+ * D/dalvikvm(   86): 0x4858de90 (002c): blx_1   0x48589198
+ * D/dalvikvm(   86): 0x4858de92 (002e): blx_2   see above
+ * D/dalvikvm(   86): 0x4858de94 (0030): b       0x4858dec8
+ * D/dalvikvm(   86): 0x4858de96 (0032): b       0x4858deb6
+ * D/dalvikvm(   86): 0x4858de98 (0034): ldr     r0, [r7, #72]
+ * D/dalvikvm(   86): 0x4858de9a (0036): cmp     r1, #0
+ * D/dalvikvm(   86): 0x4858de9c (0038): bgt     0x4858dea4
+ * D/dalvikvm(   86): 0x4858de9e (003a): ldr     r7, [r6, #116]
+ * D/dalvikvm(   86): 0x4858dea0 (003c): movs    r1, r6
+ * D/dalvikvm(   86): 0x4858dea2 (003e): blx     r7
+ * D/dalvikvm(   86): 0x4858dea4 (0040): add     r1, pc, #4
+ * D/dalvikvm(   86): 0x4858dea6 (0042): blx_1   0x485890a0
+ * D/dalvikvm(   86): 0x4858dea8 (0044): blx_2   see above
+ * D/dalvikvm(   86): 0x4858deaa (0046): b       0x4858deb6
+ * D/dalvikvm(   86): 0x4858deac (0048): .align4
+ * D/dalvikvm(   86): L0x004f:
+ * D/dalvikvm(   86): -------- dalvik offset: 0x004f @ move-result-object (PI)
+ * v4, (#0), (#0)
+ * D/dalvikvm(   86): 0x4858deac (0048): ldr     r4, [r6, #8]
+ * D/dalvikvm(   86): 0x4858deae (004a): str     r4, [r5, #16]
+ * D/dalvikvm(   86): 0x4858deb0 (004c): b       0x4858debc
+ * D/dalvikvm(   86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm(   86): 0x4858deb2 (004e): ldr     r0, [pc, #64]
+ * D/dalvikvm(   86): 0x4858deb4 (0050): b       0x4858deb8
+ * D/dalvikvm(   86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm(   86): 0x4858deb6 (0052): ldr     r0, [pc, #60]
+ * D/dalvikvm(   86): Exception_Handling:
+ * D/dalvikvm(   86): 0x4858deb8 (0054): ldr     r1, [r6, #100]
+ * D/dalvikvm(   86): 0x4858deba (0056): blx     r1
+ * D/dalvikvm(   86): 0x4858debc (0058): .align4
+ * D/dalvikvm(   86): -------- chaining cell (hot): 0x0050
+ * D/dalvikvm(   86): 0x4858debc (0058): b       0x4858dec0
+ * D/dalvikvm(   86): 0x4858debe (005a): orrs    r0, r0
+ * D/dalvikvm(   86): 0x4858dec0 (005c): ldr     r0, [r6, #112]
+ * D/dalvikvm(   86): 0x4858dec2 (005e): blx     r0
+ * D/dalvikvm(   86): 0x4858dec4 (0060): data    0xefd4(61396)
+ * D/dalvikvm(   86): 0x4858dec6 (0062): data    0x42be(17086)
+ * D/dalvikvm(   86): 0x4858dec8 (0064): .align4
+ * D/dalvikvm(   86): -------- chaining cell (predicted)
+ * D/dalvikvm(   86): 0x4858dec8 (0064): data    0xe7fe(59390)
+ * D/dalvikvm(   86): 0x4858deca (0066): data    0x0000(0)
+ * D/dalvikvm(   86): 0x4858decc (0068): data    0x0000(0)
+ * D/dalvikvm(   86): 0x4858dece (006a): data    0x0000(0)
+ * :
+ */
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    rlThis = loadValue(cUnit, rlThis, kCoreReg);
+    int regPredictedClass = dvmCompilerAllocTemp(cUnit);
+    loadClassPointer(cUnit, regPredictedClass, (int) callsiteInfo);
+    genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset,
+                 NULL);/* null object? */
+    int regActualClass = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass);
+    opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    callsiteInfo->misPredBranchOver = (LIR *) opCondBranch(cUnit, kArmCondNe);
+}
+
+/* Extended MIR instructions like PHI */
+static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    int opOffset = mir->dalvikInsn.opcode - kMirOpFirst;
+    char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+                                        false);
+    strcpy(msg, extendedMIROpNames[opOffset]);
+    newLIR1(cUnit, kArmPseudoExtended, (int) msg);
+
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            genUnconditionalBranch(cUnit,
+                                   (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+            break;
+        }
+        case kMirOpCheckInlinePrediction: {
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*
+ * Create a PC-reconstruction cell for the starting offset of this trace.
+ * Since the PCR cell is placed near the end of the compiled code which is
+ * usually out of range for a conditional branch, we put two branches (one
+ * branch over to the loop body and one layover branch to the actual PCR) at the
+ * end of the entry block.
+ */
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                ArmLIR *bodyLabel)
+{
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pcrLabel->opcode = kArmPseudoPCReconstructionCell;
+    pcrLabel->operands[0] =
+        (int) (cUnit->method->insns + entry->startOffset);
+    pcrLabel->operands[1] = entry->startOffset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    ArmLIR *branchToBody = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToBody->opcode = kThumbBUncond;
+    branchToBody->generic.target = (LIR *) bodyLabel;
+    setupResourceMasks(branchToBody);
+    cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+    ArmLIR *branchToPCR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToPCR->opcode = kThumbBUncond;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static bool selfVerificationPuntOps(MIR *mir)
+{
+    DecodedInstruction *decInsn = &mir->dalvikInsn;
+
+    /*
+     * All opcodes that can throw exceptions and use the
+     * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+     * under self-verification mode.
+     */
+    switch (decInsn->opcode) {
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_CHECK_CAST:
+        case OP_MOVE_EXCEPTION:
+        case OP_FILL_ARRAY_DATA:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+            return true;
+        default:
+            return false;
+    }
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+    /* Used to hold the labels of each block */
+    ArmLIR *labelList =
+        (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+    ArmLIR *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellGap];
+    int i;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellGap; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].operands[0] = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellGap) {
+            if (bb->isFallThroughFromInvoke == true) {
+                /* Align this block first since it is a return chaining cell */
+                newLIR0(cUnit, kArmPseudoPseudoAlign4);
+            }
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].opcode = kArmPseudoEntryBlock;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb,
+                                  &labelList[bb->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].opcode = kArmPseudoExitBlock;
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].opcode = kArmPseudoNormalBlockLabel;
+            /* Reset the register state */
+            dvmCompilerResetRegPool(cUnit);
+            dvmCompilerClobberAllRegs(cUnit);
+            dvmCompilerResetNullCheck(cUnit);
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].opcode = kArmPseudoChainingCellNormal;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellInvokeSingleton;
+                    labelList[i].operands[0] =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellInvokePredicted;
+                    /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    labelList[i].operands[0] = labelList[i].operands[1];
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellHot;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].opcode =
+                        kArmPseudoPCReconstructionBlockLabel;
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].opcode = kArmPseudoEHBlockLabel;
+                    if (cUnit->pcReconstructionList.numUsed) {
+                        loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                                     jitToInterpEntries.dvmJitToInterpPunt),
+                                     r1);
+                        opReg(cUnit, kOpBlx, r1);
+                    }
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].opcode =
+                        kArmPseudoChainingCellBackwardBranch;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+
+        /*
+         * Try to build a longer optimization unit. Currently if the previous
+         * block ends with a goto, we continue adding instructions and don't
+         * reset the register allocation pool.
+         */
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+                dvmCompilerResetRegPool(cUnit);
+                if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+                    dvmCompilerClobberAllRegs(cUnit);
+                }
+
+                if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+                    dvmCompilerResetDefTracking(cUnit);
+                }
+
+                if ((int)mir->dalvikInsn.opcode >= (int)kMirOpFirst) {
+                    handleExtendedMIR(cUnit, mir);
+                    continue;
+                }
+
+                Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+                InstructionFormat dalvikFormat =
+                    dexGetFormatFromOpcode(dalvikOpcode);
+                const char *note;
+                if (mir->OptimizationFlags & MIR_INLINED) {
+                    note = " (I)";
+                } else if (mir->OptimizationFlags & MIR_INLINED_PRED) {
+                    note = " (PI)";
+                } else if (mir->OptimizationFlags & MIR_CALLEE) {
+                    note = " (C)";
+                } else {
+                    note = NULL;
+                }
+
+                ArmLIR *boundaryLIR;
+
+                /*
+                 * Don't generate the boundary LIR unless we are debugging this
+                 * trace or we need a scheduling barrier.
+                 */
+                if (headLIR == NULL || cUnit->printMe == true) {
+                    boundaryLIR =
+                        newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+                                mir->offset,
+                                (int) dvmCompilerGetDalvikDisassembly(
+                                    &mir->dalvikInsn, note));
+                    /* Remember the first LIR for this block */
+                    if (headLIR == NULL) {
+                        headLIR = boundaryLIR;
+                        /* Set the first boundaryLIR as a scheduling barrier */
+                        headLIR->defMask = ENCODE_ALL;
+                    }
+                }
+
+                /*
+                 * Don't generate the SSA annotation unless verbose mode is on
+                 */
+                if (cUnit->printMe && mir->ssaRep) {
+                    char *ssaString = dvmCompilerGetSSAString(cUnit,
+                                                              mir->ssaRep);
+                    newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+                }
+
+                bool notHandled;
+                /*
+                 * Debugging: screen the opcode first to see if it is in the
+                 * do[-not]-compile list
+                 */
+                bool singleStepMe = SINGLE_STEP_OP(dalvikOpcode);
+#if defined(WITH_SELF_VERIFICATION)
+              if (singleStepMe == false) {
+                  singleStepMe = selfVerificationPuntOps(mir);
+              }
+#endif
+                if (singleStepMe || cUnit->allSingleStep) {
+                    notHandled = false;
+                    genInterpSingleStep(cUnit, mir);
+                } else {
+                    opcodeCoverage[dalvikOpcode]++;
+                    switch (dalvikFormat) {
+                        case kFmt10t:
+                        case kFmt20t:
+                        case kFmt30t:
+                            notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+                                      mir, bb, labelList);
+                            break;
+                        case kFmt10x:
+                            notHandled = handleFmt10x(cUnit, mir);
+                            break;
+                        case kFmt11n:
+                        case kFmt31i:
+                            notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+                            break;
+                        case kFmt11x:
+                            notHandled = handleFmt11x(cUnit, mir);
+                            break;
+                        case kFmt12x:
+                            notHandled = handleFmt12x(cUnit, mir);
+                            break;
+                        case kFmt20bc:
+                            notHandled = handleFmt20bc(cUnit, mir);
+                            break;
+                        case kFmt21c:
+                        case kFmt31c:
+                            notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+                            break;
+                        case kFmt21h:
+                            notHandled = handleFmt21h(cUnit, mir);
+                            break;
+                        case kFmt21s:
+                            notHandled = handleFmt21s(cUnit, mir);
+                            break;
+                        case kFmt21t:
+                            notHandled = handleFmt21t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22b:
+                        case kFmt22s:
+                            notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+                            break;
+                        case kFmt22c:
+                            notHandled = handleFmt22c(cUnit, mir);
+                            break;
+                        case kFmt22cs:
+                            notHandled = handleFmt22cs(cUnit, mir);
+                            break;
+                        case kFmt22t:
+                            notHandled = handleFmt22t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22x:
+                        case kFmt32x:
+                            notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+                            break;
+                        case kFmt23x:
+                            notHandled = handleFmt23x(cUnit, mir);
+                            break;
+                        case kFmt31t:
+                            notHandled = handleFmt31t(cUnit, mir);
+                            break;
+                        case kFmt3rc:
+                        case kFmt35c:
+                            notHandled = handleFmt35c_3rc(cUnit, mir, bb,
+                                                          labelList);
+                            break;
+                        case kFmt3rms:
+                        case kFmt35ms:
+                            notHandled = handleFmt35ms_3rms(cUnit, mir, bb,
+                                                            labelList);
+                            break;
+                        case kFmt35mi:
+                        case kFmt3rmi:
+                            notHandled = handleExecuteInline(cUnit, mir);
+                            break;
+                        case kFmt51l:
+                            notHandled = handleFmt51l(cUnit, mir);
+                            break;
+                        default:
+                            notHandled = true;
+                            break;
+                    }
+                }
+                if (notHandled) {
+                    ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                         mir->offset,
+                         dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                         dalvikFormat);
+                    dvmCompilerAbort(cUnit);
+                    break;
+                }
+            }
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToBody);
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToPCR);
+        }
+
+        if (headLIR) {
+            /*
+             * Eliminate redundant loads/stores and delay stores into later
+             * slots
+             */
+            dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                               cUnit->lastLIRInsn);
+            /* Reset headLIR which is also the optimization boundary */
+            headLIR = NULL;
+        }
+
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+        }
+    }
+
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            /* Align this chaining cell first */
+            newLIR0(cUnit, kArmPseudoPseudoAlign4);
+
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+
+    /* Mark the bottom of chaining cells */
+    cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kArmChainingCellBottom);
+
+    /*
+     * Generate the branch to the dvmJitToInterpNoChain entry point at the end
+     * of all chaining cells for the overflow cases.
+     */
+    if (cUnit->switchOverflowPad) {
+        loadConstant(cUnit, r0, (int) cUnit->switchOverflowPad);
+        loadWordDisp(cUnit, r6SELF, offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNoChain), r2);
+        opRegReg(cUnit, kOpAdd, r1, r1);
+        opRegRegReg(cUnit, kOpAdd, r4PC, r0, r1);
+#if defined(WITH_JIT_TUNING)
+        loadConstant(cUnit, r0, kSwitchOverflow);
+#endif
+        opReg(cUnit, kOpBlx, r2);
+    }
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+    /* Print compiled opcode in this VM instance */
+    int i, start, streak;
+    char buf[1024];
+
+    streak = i = 0;
+    buf[0] = 0;
+    while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
+        i++;
+    }
+    if (i == kNumPackedOpcodes) {
+        return;
+    }
+    for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
+        if (opcodeCoverage[i]) {
+            streak++;
+        } else {
+            if (streak == 1) {
+                sprintf(buf+strlen(buf), "%x,", start);
+            } else {
+                sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+            }
+            streak = 0;
+            while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
+                i++;
+            }
+            if (i < kNumPackedOpcodes) {
+                streak = 1;
+                start = i;
+            }
+        }
+    }
+    if (streak) {
+        if (streak == 1) {
+            sprintf(buf+strlen(buf), "%x", start);
+        } else {
+            sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+        }
+    }
+    if (strlen(buf)) {
+        ALOGD("dalvik.vm.jit.op = %s", buf);
+    }
+}
+
+/* Common initialization routine for an architecture family */
+bool dvmCompilerArchInit()
+{
+    int i;
+
+    for (i = 0; i < kArmLast; i++) {
+        if (EncodingMap[i].opcode != i) {
+            ALOGE("Encoding order for %s is wrong: expecting %d, seeing %d",
+                 EncodingMap[i].name, i, EncodingMap[i].opcode);
+            dvmAbort();  // OK to dvmAbort - build error
+        }
+    }
+
+    return dvmCompilerArchVariantInit();
+}
+
+void *dvmCompilerGetInterpretTemplate()
+{
+      return (void*) ((int)gDvmJit.codeCache +
+                      templateEntryOffsets[TEMPLATE_INTERPRET]);
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_ARM;
+}
+
+/* Needed by the Assembler */
+void dvmCompilerSetupResourceMasks(ArmLIR *lir)
+{
+    setupResourceMasks(lir);
+}
+
+/* Needed by the ld/st optmizatons */
+ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopyNoInsert(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopy(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                            int srcLo, int srcHi)
+{
+    genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi);
+}
+
+void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    storeBaseDisp(cUnit, rBase, displacement, rSrc, size);
+}
+
+void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi);
+}
diff --git a/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp b/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp
new file mode 100644
index 0000000..3ecb381
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/Thumb2VFP.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    int op = kThumbBkpt;
+    RegLocation rlResult;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            op = kThumb2Vadds;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            op = kThumb2Vsubs;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            op = kThumb2Vdivs;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            op = kThumb2Vmuls;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1,
+                                              rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR3(cUnit, (ArmOpcode)op, rlResult.lowReg, rlSrc1.lowReg,
+            rlSrc2.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    int op = kThumbBkpt;
+    RegLocation rlResult;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            op = kThumb2Vaddd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            op = kThumb2Vsubd;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            op = kThumb2Vdivd;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            op = kThumb2Vmuld;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+    assert(rlSrc1.wide);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+    assert(rlSrc2.wide);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    assert(rlDest.wide);
+    assert(rlResult.wide);
+    newLIR3(cUnit, (ArmOpcode)op, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc1.lowReg, rlSrc1.highReg),
+            S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genConversionL2D(CompilationUnit *cUnit, MIR *mir)
+{
+    int srcReg, tmp1, tmp2;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    RegLocation rlResult;
+
+    rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
+    rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    tmp1 = dvmCompilerAllocTypedTempPair(cUnit, true, kFPReg);
+    tmp2 = dvmCompilerAllocTypedTempPair(cUnit, true, kFPReg);
+    newLIR2(cUnit, (ArmOpcode)kThumb2VcvtF64S32, tmp1, ((srcReg & 0xff)+1));
+    newLIR2(cUnit, (ArmOpcode)kThumb2VcvtF64U32, S2D(rlResult.lowReg, rlResult.highReg),
+            (srcReg & 0xff));
+    loadConstantValueWide(cUnit, (tmp2 & 0xff), ((tmp2 >> 8) & 0xff), 0x0, 0x41f00000);
+    newLIR3(cUnit, (ArmOpcode)kThumb2VmlaF64, S2D(rlResult.lowReg, rlResult.highReg),
+                    tmp1, tmp2);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    int op = kThumbBkpt;
+    bool longSrc = false;
+    bool longDest = false;
+    int srcReg;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    RegLocation rlResult;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            op = kThumb2VcvtIF;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            op = kThumb2VcvtFI;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            op = kThumb2VcvtDF;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kThumb2VcvtFd;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kThumb2VcvtID;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            op = kThumb2VcvtDI;
+            break;
+        case OP_LONG_TO_DOUBLE:
+            return genConversionL2D(cUnit, mir);
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+        srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+        srcReg = rlSrc.lowReg;
+    }
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (ArmOpcode)op, S2D(rlResult.lowReg, rlResult.highReg),
+                srcReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (ArmOpcode)op, rlResult.lowReg, srcReg);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *branch;
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vsqrtd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    newLIR2(cUnit, kThumb2Vcmpd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlResult.lowReg, rlResult.highReg));
+    newLIR0(cUnit, kThumb2Fmstat);
+    branch = newLIR2(cUnit, kThumbBCond, 0, kArmCondEq);
+    dvmCompilerClobberCallRegs(cUnit);
+    LOAD_FUNC_ADDR(cUnit, r2, (int) (double (*)(double)) sqrt);
+    newLIR3(cUnit, kThumb2Fmrrd, r0, r1, S2D(rlSrc.lowReg, rlSrc.highReg));
+    newLIR1(cUnit, kThumbBlxR, r2);
+    newLIR3(cUnit, kThumb2Fmdrr, S2D(rlResult.lowReg, rlResult.highReg),
+            r0, r1);
+    ArmLIR *label = newLIR0(cUnit, kArmPseudoTargetLabel);
+    label->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)label;
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    bool isDouble;
+    int defaultResult;
+    RegLocation rlResult;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            isDouble = false;
+            defaultResult = -1;
+            break;
+        case OP_CMPG_FLOAT:
+            isDouble = false;
+            defaultResult = 1;
+            break;
+        case OP_CMPL_DOUBLE:
+            isDouble = true;
+            defaultResult = -1;
+            break;
+        case OP_CMPG_DOUBLE:
+            isDouble = true;
+            defaultResult = 1;
+            break;
+        default:
+            return true;
+    }
+    if (isDouble) {
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+        dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstant(cUnit, rlResult.lowReg, defaultResult);
+        newLIR2(cUnit, kThumb2Vcmpd, S2D(rlSrc1.lowReg, r1Src2.highReg),
+                S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    } else {
+        rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+        rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+        dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstant(cUnit, rlResult.lowReg, defaultResult);
+        newLIR2(cUnit, kThumb2Vcmps, rlSrc1.lowReg, rlSrc2.lowReg);
+    }
+    assert(!FPREG(rlResult.lowReg));
+    newLIR0(cUnit, kThumb2Fmstat);
+
+    genIT(cUnit, (defaultResult == -1) ? kArmCondGt : kArmCondMi, "");
+    newLIR2(cUnit, kThumb2MovImmShift, rlResult.lowReg,
+            modifiedImmediate(-defaultResult)); // Must not alter ccodes
+    genBarrier(cUnit);
+
+    genIT(cUnit, kArmCondEq, "");
+    loadConstant(cUnit, rlResult.lowReg, 0);
+    genBarrier(cUnit);
+
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp b/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp
new file mode 100644
index 0000000..7aac8e6
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbPortableFP.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/* Forward-declare the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir);
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    return genConversionPortable(cUnit, mir);
+}
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleExecuteInlineC(cUnit, mir);
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult = LOC_C_RETURN;
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            loadValueDirectFixed(cUnit, rlSrc1, r0);
+            loadValueDirectFixed(cUnit, rlSrc2, r1);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPG_FLOAT:
+            loadValueDirectFixed(cUnit, rlSrc1, r0);
+            loadValueDirectFixed(cUnit, rlSrc2, r1);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPL_DOUBLE:
+            loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+            loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_CMPG_DOUBLE:
+            loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+            loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+            genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbVFP.cpp b/vm/compiler/codegen/arm/FP/ThumbVFP.cpp
new file mode 100644
index 0000000..f685f24
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbVFP.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Take the address of a Dalvik register and store it into rDest.
+ * Clobber any live values associated either with the Dalvik value
+ * or the target register and lock the target fixed register.
+ */
+static void loadValueAddressDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                                   int rDest)
+{
+     rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
+                          dvmCompilerUpdateLoc(cUnit, rlSrc);
+     if (rlSrc.location == kLocPhysReg) {
+         if (rlSrc.wide) {
+             dvmCompilerFlushRegWide(cUnit, rlSrc.lowReg, rlSrc.highReg);
+         } else {
+             dvmCompilerFlushReg(cUnit, rlSrc.lowReg);
+         }
+     }
+     dvmCompilerClobber(cUnit, rDest);
+     dvmCompilerLockTemp(cUnit, rDest);
+     opRegRegImm(cUnit, kOpAdd, rDest, r5FP,
+                 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = LOC_C_RETURN_WIDE;
+    RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+    loadValueAddressDirect(cUnit, rlSrc, r2);
+    genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * TUNING: On some implementations, it is quicker to pass addresses
+ * to the handlers rather than load the operands into core registers
+ * and then move the values to FP regs in the handlers.  Other implementations
+ * may prefer passing data in registers (and the latter approach would
+ * yield cleaner register handling - avoiding the requirement that operands
+ * be flushed to memory prior to the call).
+ */
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+    TemplateOpcode opcode;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            opcode = TEMPLATE_ADD_FLOAT_VFP;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            opcode = TEMPLATE_SUB_FLOAT_VFP;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            opcode = TEMPLATE_DIV_FLOAT_VFP;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            opcode = TEMPLATE_MUL_FLOAT_VFP;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc1, r1);
+    loadValueAddressDirect(cUnit, rlSrc2, r2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    }
+    return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+    TemplateOpcode opcode;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            opcode = TEMPLATE_ADD_DOUBLE_VFP;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            opcode = TEMPLATE_SUB_DOUBLE_VFP;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            opcode = TEMPLATE_DIV_DOUBLE_VFP;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            opcode = TEMPLATE_MUL_DOUBLE_VFP;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc1, r1);
+    loadValueAddressDirect(cUnit, rlSrc2, r2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    }
+    return false;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    bool longSrc = false;
+    bool longDest = false;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    TemplateOpcode templateOpcode;
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_INT_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_FLOAT_TO_INT_VFP;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_INT_TO_DOUBLE_VFP;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_INT_VFP;
+            break;
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    }
+
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    loadValueAddressDirect(cUnit, rlDest, r0);
+    loadValueAddressDirect(cUnit, rlSrc, r1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    if (rlDest.wide) {
+        rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    }
+    dvmCompilerClobber(cUnit, rlDest.lowReg);
+    return false;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    TemplateOpcode templateOpcode;
+    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+    bool wide = true;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            templateOpcode = TEMPLATE_CMPL_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPG_FLOAT:
+            templateOpcode = TEMPLATE_CMPG_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPL_DOUBLE:
+            templateOpcode = TEMPLATE_CMPL_DOUBLE_VFP;
+            break;
+        case OP_CMPG_DOUBLE:
+            templateOpcode = TEMPLATE_CMPG_DOUBLE_VFP;
+            break;
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlSrc1, r0);
+    loadValueAddressDirect(cUnit, rlSrc2, r1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/GlobalOptimizations.cpp b/vm/compiler/codegen/arm/GlobalOptimizations.cpp
new file mode 100644
index 0000000..e52bd8a
--- /dev/null
+++ b/vm/compiler/codegen/arm/GlobalOptimizations.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+
+/*
+ * Identify unconditional branches that jump to the immediate successor of the
+ * branch itself.
+ */
+static void applyRedundantBranchElimination(CompilationUnit *cUnit)
+{
+    ArmLIR *thisLIR;
+
+    for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+         thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Branch to the next instruction */
+        if (thisLIR->opcode == kThumbBUncond) {
+            ArmLIR *nextLIR = thisLIR;
+
+            while (true) {
+                nextLIR = NEXT_LIR(nextLIR);
+
+                /*
+                 * Is the branch target the next instruction?
+                 */
+                if (nextLIR == (ArmLIR *) thisLIR->generic.target) {
+                    thisLIR->flags.isNop = true;
+                    break;
+                }
+
+                /*
+                 * Found real useful stuff between the branch and the target.
+                 * Need to explicitly check the lastLIRInsn here since with
+                 * method-based JIT the branch might be the last real
+                 * instruction.
+                 */
+                if (!isPseudoOpcode(nextLIR->opcode) ||
+                    (nextLIR = (ArmLIR *) cUnit->lastLIRInsn))
+                    break;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+    applyRedundantBranchElimination(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/LocalOptimizations.cpp b/vm/compiler/codegen/arm/LocalOptimizations.cpp
new file mode 100644
index 0000000..8013d00
--- /dev/null
+++ b/vm/compiler/codegen/arm/LocalOptimizations.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+
+#define DEBUG_OPT(X)
+
+/* Check RAW, WAR, and WAR dependency on the register operands */
+#define CHECK_REG_DEP(use, def, check) ((def & check->useMask) || \
+                                        ((use | def) & check->defMask))
+
+/* Scheduler heuristics */
+#define MAX_HOIST_DISTANCE 20
+#define LDLD_DISTANCE 4
+#define LD_LATENCY 2
+
+static inline bool isDalvikRegisterClobbered(ArmLIR *lir1, ArmLIR *lir2)
+{
+    int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
+    int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
+    int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
+    int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
+
+    return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+#if 0
+/* Debugging utility routine */
+static void dumpDependentInsnPair(ArmLIR *thisLIR, ArmLIR *checkLIR,
+                                  const char *optimization)
+{
+    ALOGD("************ %s ************", optimization);
+    dvmDumpLIRInsn((LIR *) thisLIR, 0);
+    dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/* Convert a more expensive instruction (ie load) into a move */
+static void convertMemOpIntoMove(CompilationUnit *cUnit, ArmLIR *origLIR,
+                                 int dest, int src)
+{
+    /* Insert a move to replace the load */
+    ArmLIR *moveLIR;
+    moveLIR = dvmCompilerRegCopyNoInsert( cUnit, dest, src);
+    /*
+     * Insert the converted instruction after the original since the
+     * optimization is scannng in the top-down order and the new instruction
+     * will need to be re-checked (eg the new dest clobbers the src used in
+     * thisLIR).
+     */
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) moveLIR);
+}
+
+/*
+ * Perform a pass of top-down walk, from the second-last instruction in the
+ * superblock, to eliminate redundant loads and stores.
+ *
+ * An earlier load can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * An earlier store can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * A later store can be eliminated by an earlier store iff
+ *   1) They are must-aliases
+ *   2) The memory location is not written to in between
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+                                      ArmLIR *headLIR,
+                                      ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR;
+
+    if (headLIR == tailLIR) return;
+
+    for (thisLIR = PREV_LIR(tailLIR);
+         thisLIR != headLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        int sinkDistance = 0;
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpcode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & (IS_LOAD | IS_STORE))) {
+            continue;
+        }
+
+        int nativeRegId = thisLIR->operands[0];
+        bool isThisLIRLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        ArmLIR *checkLIR;
+        /* Use the mem mask to determine the rough memory location */
+        u8 thisMemMask = (thisLIR->useMask | thisLIR->defMask) & ENCODE_MEM;
+
+        /*
+         * Currently only eliminate redundant ld/st for constant and Dalvik
+         * register accesses.
+         */
+        if (!(thisMemMask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue;
+
+        /*
+         * Add r15 (pc) to the resource mask to prevent this instruction
+         * from sinking past branch instructions. Also take out the memory
+         * region bits since stopMask is used to check data/control
+         * dependencies.
+         */
+        u8 stopUseRegMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                            ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        for (checkLIR = NEXT_LIR(thisLIR);
+             checkLIR != tailLIR;
+             checkLIR = NEXT_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = (checkLIR->useMask | checkLIR->defMask) &
+                              ENCODE_MEM;
+            u8 aliasCondition = thisMemMask & checkMemMask;
+            bool stopHere = false;
+
+            /*
+             * Potential aliases seen - check the alias relations
+             */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                bool isCheckLIRLoad = EncodingMap[checkLIR->opcode].flags &
+                                      IS_LOAD;
+                if  (aliasCondition == ENCODE_LITERAL) {
+                    /*
+                     * Should only see literal loads in the instruction
+                     * stream.
+                     */
+                    assert(!(EncodingMap[checkLIR->opcode].flags &
+                             IS_STORE));
+                    /* Same value && same register type */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo &&
+                        REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId)){
+                        /*
+                         * Different destination register - insert
+                         * a move
+                         */
+                        if (checkLIR->operands[0] != nativeRegId) {
+                            convertMemOpIntoMove(cUnit, checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                        }
+                        checkLIR->flags.isNop = true;
+                    }
+                } else if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo) {
+                        /* Only optimize compatible registers */
+                        bool regCompatible =
+                            REGTYPE(checkLIR->operands[0]) ==
+                            REGTYPE(nativeRegId);
+                        if ((isThisLIRLoad && isCheckLIRLoad) ||
+                            (!isThisLIRLoad && isCheckLIRLoad)) {
+                            /* RAR or RAW */
+                            if (regCompatible) {
+                                /*
+                                 * Different destination register -
+                                 * insert a move
+                                 */
+                                if (checkLIR->operands[0] !=
+                                    nativeRegId) {
+                                    convertMemOpIntoMove(cUnit,
+                                                 checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                                }
+                                checkLIR->flags.isNop = true;
+                            } else {
+                                /*
+                                 * Destinaions are of different types -
+                                 * something complicated going on so
+                                 * stop looking now.
+                                 */
+                                stopHere = true;
+                            }
+                        } else if (isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAR - register value is killed */
+                            stopHere = true;
+                        } else if (!isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAW - nuke the earlier store */
+                            thisLIR->flags.isNop = true;
+                            stopHere = true;
+                        }
+                    /* Partial overlap */
+                    } else if (isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        /*
+                         * It is actually ok to continue if checkLIR
+                         * is a read. But it is hard to make a test
+                         * case for this so we just stop here to be
+                         * conservative.
+                         */
+                        stopHere = true;
+                    }
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    break;
+                /* The checkLIR has been transformed - check the next one */
+                } else if (checkLIR->flags.isNop) {
+                    continue;
+                }
+            }
+
+
+            /*
+             * this and check LIRs have no memory dependency. Now check if
+             * their register operands have any RAW, WAR, and WAW
+             * dependencies. If so, stop looking.
+             */
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                "REG CLOBBERED"));
+                /* Only sink store instructions */
+                if (sinkDistance && !isThisLIRLoad) {
+                    ArmLIR *newStoreLIR =
+                        (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+                    *newStoreLIR = *thisLIR;
+                    /*
+                     * Stop point found - insert *before* the checkLIR
+                     * since the instruction list is scanned in the
+                     * top-down order.
+                     */
+                    dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+                                               (LIR *) newStoreLIR);
+                    thisLIR->flags.isNop = true;
+                }
+                break;
+            } else if (!checkLIR->flags.isNop) {
+                sinkDistance++;
+            }
+        }
+    }
+}
+
+/*
+ * Perform a pass of bottom-up walk, from the second instruction in the
+ * superblock, to try to hoist loads to earlier slots.
+ */
+static void applyLoadHoisting(CompilationUnit *cUnit,
+                              ArmLIR *headLIR,
+                              ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR, *checkLIR;
+    /*
+     * Store the list of independent instructions that can be hoisted past.
+     * Will decide the best place to insert later.
+     */
+    ArmLIR *prevInstList[MAX_HOIST_DISTANCE];
+
+    /* Empty block */
+    if (headLIR == tailLIR) return;
+
+    /* Start from the second instruction */
+    for (thisLIR = NEXT_LIR(headLIR);
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpcode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_LOAD)) {
+            continue;
+        }
+
+        u8 stopUseAllMask = thisLIR->useMask;
+
+        /*
+         * Branches for null/range checks are marked with the true resource
+         * bits, and loads to Dalvik registers, constant pools, and non-alias
+         * locations are safe to be hoisted. So only mark the heap references
+         * conservatively here.
+         */
+        if (stopUseAllMask & ENCODE_HEAP_REF) {
+            stopUseAllMask |= ENCODE_REG_PC;
+        }
+
+        /* Similar as above, but just check for pure register dependency */
+        u8 stopUseRegMask = stopUseAllMask & ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        int nextSlot = 0;
+        bool stopHere = false;
+
+        /* Try to hoist the load to a good spot */
+        for (checkLIR = PREV_LIR(thisLIR);
+             checkLIR != headLIR;
+             checkLIR = PREV_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = checkLIR->defMask & ENCODE_MEM;
+            u8 aliasCondition = stopUseAllMask & checkMemMask;
+            stopHere = false;
+
+            /* Potential WAR alias seen - check the exact relation */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                /* We can fully disambiguate Dalvik references */
+                if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias or partually overlap */
+                    if ((checkLIR->aliasInfo == thisLIR->aliasInfo) ||
+                        isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        stopHere = true;
+                    }
+                /* Conservatively treat all heap refs as may-alias */
+                } else {
+                    assert(aliasCondition == ENCODE_HEAP_REF);
+                    stopHere = true;
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    prevInstList[nextSlot++] = checkLIR;
+                    break;
+                }
+            }
+
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            /*
+             * Store the dependent or non-pseudo/indepedent instruction to the
+             * list.
+             */
+            if (stopHere || !isPseudoOpcode(checkLIR->opcode)) {
+                prevInstList[nextSlot++] = checkLIR;
+                if (nextSlot == MAX_HOIST_DISTANCE) break;
+            }
+
+            /* Found a new place to put the load - move it here */
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(checkLIR, thisLIR
+                                                "HOIST STOP"));
+                break;
+            }
+        }
+
+        /*
+         * Reached the top - use headLIR as the dependent marker as all labels
+         * are barriers.
+         */
+        if (stopHere == false && nextSlot < MAX_HOIST_DISTANCE) {
+            prevInstList[nextSlot++] = headLIR;
+        }
+
+        /*
+         * At least one independent instruction is found. Scan in the reversed
+         * direction to find a beneficial slot.
+         */
+        if (nextSlot >= 2) {
+            int firstSlot = nextSlot - 2;
+            int slot;
+            ArmLIR *depLIR = prevInstList[nextSlot-1];
+            /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
+            if (!isPseudoOpcode(depLIR->opcode) &&
+                (EncodingMap[depLIR->opcode].flags & IS_LOAD)) {
+                firstSlot -= LDLD_DISTANCE;
+            }
+            /*
+             * Make sure we check slot >= 0 since firstSlot may be negative
+             * when the loop is first entered.
+             */
+            for (slot = firstSlot; slot >= 0; slot--) {
+                ArmLIR *curLIR = prevInstList[slot];
+                ArmLIR *prevLIR = prevInstList[slot+1];
+
+                /*
+                 * Check the highest instruction.
+                 * ENCODE_ALL represents a scheduling barrier.
+                 */
+                if (prevLIR->defMask == ENCODE_ALL) {
+                    /*
+                     * If the first instruction is a load, don't hoist anything
+                     * above it since it is unlikely to be beneficial.
+                     */
+                    if (EncodingMap[curLIR->opcode].flags & IS_LOAD) continue;
+                    /*
+                     * Need to unconditionally break here even if the hoisted
+                     * distance is greater than LD_LATENCY (ie more than enough
+                     * cycles are inserted to hide the load latency) since theu
+                     * subsequent code doesn't expect to compare against a
+                     * pseudo opcode (whose opcode value is negative).
+                     */
+                    break;
+                }
+
+                /*
+                 * NOTE: now prevLIR is guaranteed to be a non-pseudo
+                 * instruction (ie accessing EncodingMap[prevLIR->opcode] is
+                 * safe).
+                 *
+                 * Try to find two instructions with load/use dependency until
+                 * the remaining instructions are less than LD_LATENCY.
+                 */
+                if (((curLIR->useMask & prevLIR->defMask) &&
+                     (EncodingMap[prevLIR->opcode].flags & IS_LOAD)) ||
+                    (slot < LD_LATENCY)) {
+                    break;
+                }
+            }
+
+            /* Found a slot to hoist to */
+            if (slot >= 0) {
+                ArmLIR *curLIR = prevInstList[slot];
+                ArmLIR *newLoadLIR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR),
+                                                               true);
+                *newLoadLIR = *thisLIR;
+                /*
+                 * Insertion is guaranteed to succeed since checkLIR
+                 * is never the first LIR on the list
+                 */
+                dvmCompilerInsertLIRBefore((LIR *) curLIR,
+                                           (LIR *) newLoadLIR);
+                thisLIR->flags.isNop = true;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
+                                        LIR *tailLIR)
+{
+    if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
+        applyLoadStoreElimination(cUnit, (ArmLIR *) headLIR,
+                                  (ArmLIR *) tailLIR);
+    }
+    if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
+        applyLoadHoisting(cUnit, (ArmLIR *) headLIR, (ArmLIR *) tailLIR);
+    }
+}
diff --git a/vm/compiler/codegen/arm/README.txt b/vm/compiler/codegen/arm/README.txt
new file mode 100644
index 0000000..1bb4603
--- /dev/null
+++ b/vm/compiler/codegen/arm/README.txt
@@ -0,0 +1,48 @@
+The codegen file for the ARM-based JIT is composed by files broken by
+functionality hierarchies. The goal is to separate architectural dependent
+and independent components to facilitate maintenance and future extension.
+
+For example, the codegen file for armv7-a is assembled by the following
+components:
+
+--
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
+
+--
+
+For the Thumb/Thumb2 directories, each contain the followin three files:
+
+- Factory.c (low-level routines for instruction selections)
+- Gen.c     (invoke the ISA-specific instruction selection routines)
+- Ralloc.c  (arch-dependent register pools)
+
+The FP directory contains FP-specific codegen routines depending on
+Thumb/Thumb2/VFP/PortableFP:
+
+- Thumb2VFP.c
+- ThumbVFP.c
+- ThumbPortableFP.c
+
+In this way the dependency between generic and specific code tied to
+particular architectures can be explicitly represented.
diff --git a/vm/compiler/codegen/arm/Thumb/Factory.cpp b/vm/compiler/codegen/arm/Thumb/Factory.cpp
new file mode 100644
index 0000000..1b65a03
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Factory.cpp
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7};
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg,
+                      int highReg);
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest);
+static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc);
+static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              ArmConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              ArmLIR *pcrLabel);
+
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.  If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    ArmLIR *res;
+    int tDest = LOWREG(rDest) ? rDest : dvmCompilerAllocTemp(cUnit);
+    /* See if the value can be constructed cheaply */
+    if ((value >= 0) && (value <= 255)) {
+        res = newLIR2(cUnit, kThumbMovImm, tDest, value);
+        if (rDest != tDest) {
+           opRegReg(cUnit, kOpMov, rDest, tDest);
+           dvmCompilerFreeTemp(cUnit, tDest);
+        }
+        return res;
+    } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
+        res = newLIR2(cUnit, kThumbMovImm, tDest, ~value);
+        newLIR2(cUnit, kThumbMvn, tDest, tDest);
+        if (rDest != tDest) {
+           opRegReg(cUnit, kOpMov, rDest, tDest);
+           dvmCompilerFreeTemp(cUnit, tDest);
+        }
+        return res;
+    }
+    /* No shortcut - go ahead and use literal pool */
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 255);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumbLdrPcRel;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = tDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+    /*
+     * To save space in the constant pool, we use the ADD_RRI8 instruction to
+     * add up to 255 to an existing constant value.
+     */
+    if (dataTarget->operands[0] != value) {
+        newLIR2(cUnit, kThumbAddRI8, tDest, value - dataTarget->operands[0]);
+    }
+    if (rDest != tDest) {
+       opRegReg(cUnit, kOpMov, rDest, tDest);
+       dvmCompilerFreeTemp(cUnit, tDest);
+    }
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    ArmLIR *res;
+    cUnit->hasClassLiterals = true;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->classPointerList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->classPointerList, value);
+        /* Counts the number of class pointers in this translation */
+        cUnit->numClassPointers++;
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumbLdrPcRel;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return res;
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kThumbBUncond;
+            break;
+        default:
+            ALOGE("Jit: bad case in opNone");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR0(cUnit, opcode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+    return newLIR2(cUnit, kThumbBCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpPush:
+            opcode = kThumbPush;
+            break;
+        case kOpPop:
+            opcode = kThumbPop;
+            break;
+        default:
+            ALOGE("Jit: bad case in opCondBranch");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR1(cUnit, opcode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpBlx:
+            opcode = kThumbBlxR;
+            break;
+        default:
+            ALOGE("Jit: bad case in opReg");
+            dvmCompilerAbort(cUnit);
+    }
+    return newLIR1(cUnit, opcode, rDestSrc);
+}
+
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (absValue & 0xff) == absValue;
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            if ( !neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+            } else
+                opcode = kThumbAddRRR;
+            break;
+        case kOpSub:
+            if (!neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+            } else
+                opcode = kThumbSubRRR;
+            break;
+        case kOpCmp:
+            if (neg)
+               shortForm = false;
+            if (LOWREG(rDestSrc1) && shortForm) {
+                opcode = kThumbCmpRI8;
+            } else if (LOWREG(rDestSrc1)) {
+                opcode = kThumbCmpRR;
+            } else {
+                shortForm = false;
+                opcode = kThumbCmpHL;
+            }
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, value);
+        if (op == kOpCmp)
+            newLIR2(cUnit, opcode, rDestSrc1, rScratch);
+        else
+            newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
+    }
+    return res;
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            opcode = kThumbAddRRR;
+            break;
+        case kOpSub:
+            opcode = kThumbSubRRR;
+            break;
+        default:
+            if (rDest == rSrc1) {
+                return opRegReg(cUnit, op, rDest, rSrc2);
+            } else if (rDest == rSrc2) {
+                assert(dvmCompilerIsTemp(cUnit, rSrc1));
+                dvmCompilerClobber(cUnit, rSrc1);
+                opRegReg(cUnit, op, rSrc1, rSrc2);
+                return opRegReg(cUnit, kOpMov, rDest, rSrc1);
+            } else {
+                opRegReg(cUnit, kOpMov, rDest, rSrc1);
+                return opRegReg(cUnit, op, rDest, rSrc2);
+            }
+            break;
+    }
+    return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = (absValue & 0x7) == absValue;
+    switch(op) {
+        case kOpAdd:
+            if (rDest == rSrc1)
+                return opRegImm(cUnit, op, rDest, value);
+            if ((rSrc1 == r13sp) && (value <= 1020)) { /* sp */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opcode = kThumbAddSpRel;
+                value >>= 2;
+            } else if ((rSrc1 == r15pc) && (value <= 1020)) { /* pc */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opcode = kThumbAddPcRel;
+                value >>= 2;
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+                res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+                newLIR2(cUnit, opcode, rDest, absValue - 7);
+                return res;
+            } else
+                opcode = kThumbAddRRR;
+            break;
+
+        case kOpSub:
+            if (rDest == rSrc1)
+                return opRegImm(cUnit, op, rDest, value);
+            if (shortForm) {
+                opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+                res = newLIR3(cUnit, opcode, rDest, rSrc1, 7);
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+                newLIR2(cUnit, opcode, rDest, absValue - 7);
+                return res;
+            } else
+                opcode = kThumbSubRRR;
+            break;
+        case kOpLsl:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbLslRRI5;
+                break;
+        case kOpLsr:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbLsrRRI5;
+                break;
+        case kOpAsr:
+                shortForm = (!neg && value <= 31);
+                opcode = kThumbAsrRRI5;
+                break;
+        case kOpMul:
+        case kOpAnd:
+        case kOpOr:
+        case kOpXor:
+                if (rDest == rSrc1) {
+                    int rScratch = dvmCompilerAllocTemp(cUnit);
+                    res = loadConstant(cUnit, rScratch, value);
+                    opRegReg(cUnit, op, rDest, rScratch);
+                } else {
+                    res = loadConstant(cUnit, rDest, value);
+                    opRegReg(cUnit, op, rDest, rSrc1);
+                }
+                return res;
+        default:
+            ALOGE("Jit: bad case in opRegRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+    else {
+        if (rDest != rSrc1) {
+            res = loadConstant(cUnit, rDest, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
+        } else {
+            int rScratch = dvmCompilerAllocTemp(cUnit);
+            res = loadConstant(cUnit, rScratch, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
+        }
+    }
+    return res;
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdc:
+            opcode = kThumbAdcRR;
+            break;
+        case kOpAnd:
+            opcode = kThumbAndRR;
+            break;
+        case kOpBic:
+            opcode = kThumbBicRR;
+            break;
+        case kOpCmn:
+            opcode = kThumbCmnRR;
+            break;
+        case kOpCmp:
+            opcode = kThumbCmpRR;
+            break;
+        case kOpXor:
+            opcode = kThumbEorRR;
+            break;
+        case kOpMov:
+            if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+                opcode = kThumbMovRR;
+            else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbMovRR_H2H;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbMovRR_H2L;
+            else
+                opcode = kThumbMovRR_L2H;
+            break;
+        case kOpMul:
+            opcode = kThumbMul;
+            break;
+        case kOpMvn:
+            opcode = kThumbMvn;
+            break;
+        case kOpNeg:
+            opcode = kThumbNeg;
+            break;
+        case kOpOr:
+            opcode = kThumbOrr;
+            break;
+        case kOpSbc:
+            opcode = kThumbSbc;
+            break;
+        case kOpTst:
+            opcode = kThumbTst;
+            break;
+        case kOpLsl:
+            opcode = kThumbLslRR;
+            break;
+        case kOpLsr:
+            opcode = kThumbLsrRR;
+            break;
+        case kOpAsr:
+            opcode = kThumbAsrRR;
+            break;
+        case kOpRor:
+            opcode = kThumbRorRR;
+        case kOpAdd:
+        case kOpSub:
+            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+        case kOp2Byte:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
+             opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
+             return res;
+        case kOp2Short:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+             opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
+             return res;
+        case kOp2Char:
+             res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+             opRegRegImm(cUnit, kOpLsr, rDestSrc1, rDestSrc1, 16);
+             return res;
+        default:
+            ALOGE("Jit: bad case in opRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    ArmLIR *res;
+    res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+    loadConstantNoClobber(cUnit, rDestHi, valHi);
+    return res;
+}
+
+/* Load value from base + scaled index. */
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    int rNewIndex = rIndex;
+    if (scale) {
+        // Scale the index, but can't trash the original.
+        rNewIndex = dvmCompilerAllocTemp(cUnit);
+        first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+    }
+    switch (size) {
+        case kWord:
+            opcode = kThumbLdrRRR;
+            break;
+        case kUnsignedHalf:
+            opcode = kThumbLdrhRRR;
+            break;
+        case kSignedHalf:
+            opcode = kThumbLdrshRRR;
+            break;
+        case kUnsignedByte:
+            opcode = kThumbLdrbRRR;
+            break;
+        case kSignedByte:
+            opcode = kThumbLdrsbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rDest, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    if (scale)
+        dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpcode opcode = kThumbBkpt;
+    int rNewIndex = rIndex;
+    if (scale) {
+        rNewIndex = dvmCompilerAllocTemp(cUnit);
+        first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+    }
+    switch (size) {
+        case kWord:
+            opcode = kThumbStrRRR;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kThumbStrhRRR;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kThumbStrbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rSrc, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    if (scale)
+        dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return (first) ? first : res;
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps.  If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+    ArmLIR *res;
+    ArmLIR *load = NULL;
+    ArmLIR *load2 = NULL;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    int encodedDisp = displacement;
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            if ((displacement < 124) && (displacement >= 0)) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else {
+                opcode = kThumbLdrRRR;
+            }
+            break;
+        case kWord:
+            if (LOWREG(rDest) && (rBase == r15pc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13sp) &&
+                      (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrSpRel;
+            } else if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else {
+                opcode = kThumbLdrRRR;
+            }
+            break;
+        case kUnsignedHalf:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbLdrhRRI5;
+            } else {
+                opcode = kThumbLdrhRRR;
+            }
+            break;
+        case kSignedHalf:
+            opcode = kThumbLdrshRRR;
+            break;
+        case kUnsignedByte:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbLdrbRRI5;
+            } else {
+                opcode = kThumbLdrbRRR;
+            }
+            break;
+        case kSignedByte:
+            opcode = kThumbLdrsbRRR;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+    if (shortForm) {
+        load = res = newLIR3(cUnit, opcode, rDest, rBase, encodedDisp);
+        if (pair) {
+            load2 = newLIR3(cUnit, opcode, rDestHi, rBase, encodedDisp+1);
+        }
+    } else {
+        if (pair) {
+            int rTmp = dvmCompilerAllocFreeTemp(cUnit);
+            res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
+            load = newLIR3(cUnit, kThumbLdrRRI5, rDest, rTmp, 0);
+            load2 = newLIR3(cUnit, kThumbLdrRRI5, rDestHi, rTmp, 1);
+            dvmCompilerFreeTemp(cUnit, rTmp);
+        } else {
+            int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
+                                        : rDest;
+            res = loadConstant(cUnit, rTmp, displacement);
+            load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
+            if (rBase == r5FP)
+                annotateDalvikRegAccess(load, displacement >> 2,
+                                        true /* isLoad */);
+            if (rTmp != rDest)
+                dvmCompilerFreeTemp(cUnit, rTmp);
+        }
+    }
+    if (rBase == r5FP) {
+        if (load != NULL)
+            annotateDalvikRegAccess(load, displacement >> 2,
+                                    true /* isLoad */);
+        if (load2 != NULL)
+            annotateDalvikRegAccess(load2, (displacement >> 2) + 1,
+                                    true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (load != NULL && cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+    if (load2 != NULL && cUnit->heapMemOp)
+        load2->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDestLo, int rDestHi,
+                                int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    ArmLIR *res;
+    ArmLIR *store = NULL;
+    ArmLIR *store2 = NULL;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    int encodedDisp = displacement;
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            if ((displacement < 124) && (displacement >= 0)) {
+                assert((displacement & 0x3) == 0);
+                pair = true;
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else {
+                opcode = kThumbStrRRR;
+            }
+            break;
+        case kWord:
+            if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else {
+                opcode = kThumbStrRRR;
+            }
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbStrhRRI5;
+            } else {
+                opcode = kThumbStrhRRR;
+            }
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbStrbRRI5;
+            } else {
+                opcode = kThumbStrbRRR;
+            }
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+    if (shortForm) {
+        store = res = newLIR3(cUnit, opcode, rSrc, rBase, encodedDisp);
+        if (pair) {
+            store2 = newLIR3(cUnit, opcode, rSrcHi, rBase, encodedDisp + 1);
+        }
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        if (pair) {
+            res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
+            store =  newLIR3(cUnit, kThumbStrRRI5, rSrc, rScratch, 0);
+            store2 = newLIR3(cUnit, kThumbStrRRI5, rSrcHi, rScratch, 1);
+        } else {
+            res = loadConstant(cUnit, rScratch, displacement);
+            store = newLIR3(cUnit, opcode, rSrc, rBase, rScratch);
+        }
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+    if (rBase == r5FP) {
+        if (store != NULL)
+            annotateDalvikRegAccess(store, displacement >> 2,
+                                    false /* isLoad */);
+        if (store2 != NULL)
+            annotateDalvikRegAccess(store2, (displacement >> 2) + 1,
+                                    false /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (store != NULL && cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+    if (store2 != NULL && cUnit->heapMemOp)
+        store2->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    if (lowReg < highReg) {
+        storeMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+    } else {
+        storeWordDisp(cUnit, base, 0, lowReg);
+        storeWordDisp(cUnit, base, 4, highReg);
+    }
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    if (lowReg < highReg) {
+        loadMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+    } else {
+        loadWordDisp(cUnit, base, 0 , lowReg);
+        loadWordDisp(cUnit, base, 4 , highReg);
+    }
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res;
+    ArmOpcode opcode;
+    res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (LOWREG(rDest) && LOWREG(rSrc))
+        opcode = kThumbMovRR;
+    else if (!LOWREG(rDest) && !LOWREG(rSrc))
+         opcode = kThumbMovRR_H2H;
+    else if (LOWREG(rDest))
+         opcode = kThumbMovRR_H2L;
+    else
+         opcode = kThumbMovRR_L2H;
+
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+    // Handle overlap
+    if (srcHi == destLo) {
+        genRegCopy(cUnit, destHi, srcHi);
+        genRegCopy(cUnit, destLo, srcLo);
+    } else {
+        genRegCopy(cUnit, destLo, srcLo);
+        genRegCopy(cUnit, destHi, srcHi);
+    }
+}
+
+static ArmLIR *genCmpImmBranch(CompilationUnit *cUnit,
+                                     ArmConditionCode cond, int reg,
+                                     int checkValue)
+{
+    if ((checkValue & 0xff) != checkValue) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, tReg, checkValue);
+        newLIR2(cUnit, kThumbCmpRR, reg, tReg);
+        dvmCompilerFreeTemp(cUnit, tReg);
+    } else {
+        newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+    }
+    ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+    return branch;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    /*
+     * We need two separate pushes, since we want r5 to be pushed first.
+     * Store multiple will push LR first.
+     */
+    ArmLIR *pushFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pushFP->opcode = kThumbPush;
+    pushFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(pushFP);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);
+
+    ArmLIR *pushLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    pushLR->opcode = kThumbPush;
+    /* Thumb push can handle LR, but is encoded differently at bit 8 */
+    pushLR->operands[0] = 1 << 8;
+    setupResourceMasks(pushLR);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    /*
+     * Since Thumb cannot pop memory content into LR, we have to pop LR
+     * to a temp first (r5 in this case). Then we move r5 to LR, then pop the
+     * original r5 from stack.
+     */
+    /* Pop memory content(LR) into r5 first */
+    ArmLIR *popForLR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    popForLR->opcode = kThumbPop;
+    popForLR->operands[0] = 1 << r5FP;
+    setupResourceMasks(popForLR);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);
+
+    ArmLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
+    dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);
+
+    /* Now restore the original r5 */
+    ArmLIR *popFP = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    popFP->opcode = kThumbPop;
+    popFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(popFP);
+    dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
+}
+#endif
diff --git a/vm/compiler/codegen/arm/Thumb/Gen.cpp b/vm/compiler/codegen/arm/Thumb/Gen.cpp
new file mode 100644
index 0000000..622f47e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Gen.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 6 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (2 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (12 bytes):
+ *
+ *       mov   r0, pc       @ move adr of "mov r0,pc" + 4 to r0
+ *       sub   r0, #10      @ back up to addr prof count pointer
+ *       ldr   r0, [r0]     @ get address of counter
+ *       ldr   r1, [r0]
+ *       add   r1, #1
+ *       str   r1, [r0]
+ *
+ * For periodic profiling (4 bytes):
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+    newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        /* Thumb instruction used directly here to ensure correct size */
+        newLIR2(cUnit, kThumbMovRR_H2L, r0, r15pc);
+        newLIR2(cUnit, kThumbSubRI8, r0, 10);
+        newLIR3(cUnit, kThumbLdrRRI5, r0, r0, 0);
+        newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+        newLIR2(cUnit, kThumbAddRI8, r1, 1);
+        newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+        return 12;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        return 4;
+    }
+}
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
+                rlSrc.lowReg, 0x80000000);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
+                        0x80000000);
+    genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static bool partialOverlap(int sreg1, int sreg2)
+{
+    return abs(sreg1 - sreg2) == 1;
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    if (partialOverlap(rlSrc1.sRegLow,rlSrc2.sRegLow) ||
+        partialOverlap(rlSrc1.sRegLow,rlDest.sRegLow) ||
+        partialOverlap(rlSrc2.sRegLow,rlDest.sRegLow)) {
+        // Rare case - not enough registers to properly handle
+        genInterpSingleStep(cUnit, mir);
+    } else if (rlDest.sRegLow == rlSrc1.sRegLow) {
+        // Already 2-operand
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+        opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (rlDest.sRegLow == rlSrc2.sRegLow) {
+        // Bad case - must use/clobber Src1 and reassign Dest
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        opRegReg(cUnit, firstOp, rlSrc1.lowReg, rlResult.lowReg);
+        opRegReg(cUnit, secondOp, rlSrc1.highReg, rlResult.highReg);
+        // Old reg assignments are now invalid
+        dvmCompilerClobber(cUnit, rlResult.lowReg);
+        dvmCompilerClobber(cUnit, rlResult.highReg);
+        dvmCompilerClobber(cUnit, rlSrc1.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc1.highReg);
+        rlDest.location = kLocDalvikFrame;
+        assert(rlSrc1.location == kLocPhysReg);
+        // Reassign registers - rlDest will now get rlSrc1's old regs
+        storeValueWide(cUnit, rlDest, rlSrc1);
+    } else {
+        // Copy Src1 to Dest
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, false);
+        loadValueDirectWide(cUnit, rlSrc1, rlResult.lowReg,
+                            rlResult.highReg);
+        rlResult.location = kLocPhysReg;
+        opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+        opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *) dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps = (RegisterInfo *)
+            dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+    pool->numFPTemps = 0;
+    pool->FPTemps = NULL;
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *res;
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    int rAddr = dvmCompilerAllocTemp(cUnit);
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR2(cUnit, kThumbMovRR, rAddr, r5FP);
+    newLIR2(cUnit, kThumbSubRI8, rAddr, sizeof(StackSaveArea) - offset);
+    storeWordDisp( cUnit, rAddr, 0, rDPC);
+    return res;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    genMonitorPortable(cUnit, mir);
+}
+
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    int reg0 = loadValue(cUnit, rlSrc, kCoreReg).lowReg;
+    int signMask = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    newLIR2(cUnit, kThumbAndRR, reg0, signMask);
+    dvmCompilerFreeTemp(cUnit, signMask);
+    storeWordDisp(cUnit, r6SELF, offset, reg0);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reg0);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation regSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    int reglo = regSrc.lowReg;
+    int reghi = regSrc.highReg;
+    int signMask = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    storeWordDisp(cUnit, r6SELF, offset, reglo);
+    newLIR2(cUnit, kThumbAndRR, reghi, signMask);
+    dvmCompilerFreeTemp(cUnit, signMask);
+    storeWordDisp(cUnit, r6SELF, offset + 4, reghi);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reghi);
+    return false;
+}
+
+/* No select in thumb, so we need to branch.  Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    int reg0 = loadValue(cUnit, rlSrc1, kCoreReg).lowReg;
+    int reg1 = loadValue(cUnit, rlSrc2, kCoreReg).lowReg;
+    newLIR2(cUnit, kThumbCmpRR, reg0, reg1);
+    ArmLIR *branch1 = newLIR2(cUnit, kThumbBCond, 2,
+           isMin ? kArmCondLt : kArmCondGt);
+    newLIR2(cUnit, kThumbMovRR, reg0, reg1);
+    ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    newLIR3(cUnit, kThumbStrRRI5, reg0, r6SELF, offset >> 2);
+    branch1->generic.target = (LIR *)target;
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit,reg0);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    // We can't implement "add src, src, src, lsl#shift" on Thumb, so we have
+    // to do a regular multiply.
+    opRegRegImm(cUnit, kOpMul, rlResult.lowReg, rlSrc.lowReg, lit);
+}
+
+static void genMultiplyByShiftAndReverseSubtract(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit)
+{
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lit);
+    opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Ralloc.cpp b/vm/compiler/codegen/arm/Thumb/Ralloc.cpp
new file mode 100644
index 0000000..6769972
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Ralloc.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit, bool fpHint,
+                                  int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+    lowReg = dvmCompilerAllocTemp(cUnit);
+    highReg = dvmCompilerAllocTemp(cUnit);
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
+{
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Factory.cpp b/vm/compiler/codegen/arm/Thumb2/Factory.cpp
new file mode 100644
index 0000000..cc036cb
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Factory.cpp
@@ -0,0 +1,1321 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7, r8, r9, r10, r11, r12};
+static int fpTemps[] = {fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
+                        fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
+
+static int encodeImmSingle(int value)
+{
+    int res;
+    int bitA =    (value & 0x80000000) >> 31;
+    int notBitB = (value & 0x40000000) >> 30;
+    int bitB =    (value & 0x20000000) >> 29;
+    int bSmear =  (value & 0x3e000000) >> 25;
+    int slice =   (value & 0x01f80000) >> 19;
+    int zeroes =  (value & 0x0007ffff);
+    if (zeroes != 0)
+        return -1;
+    if (bitB) {
+        if ((notBitB != 0) || (bSmear != 0x1f))
+            return -1;
+    } else {
+        if ((notBitB != 1) || (bSmear != 0x0))
+            return -1;
+    }
+    res = (bitA << 7) | (bitB << 6) | slice;
+    return res;
+}
+
+static ArmLIR *loadFPConstantValue(CompilationUnit *cUnit, int rDest,
+                                   int value)
+{
+    int encodedImm = encodeImmSingle(value);
+    assert(SINGLEREG(rDest));
+    if (value == 0) {
+      // TODO: we need better info about the target CPU.  a vector exclusive or
+      //       would probably be better here if we could rely on its existance.
+      // Load an immediate +2.0 (which encodes to 0)
+      newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, 0);
+      // +0.0 = +2.0 - +2.0
+      return newLIR3(cUnit, kThumb2Vsubs, rDest, rDest, rDest);
+    } else if (encodedImm >= 0) {
+        return newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, encodedImm);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2Vldrs;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    loadPcRel->operands[1] = r15pc;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return loadPcRel;
+}
+
+static int leadingZeros(u4 val)
+{
+    u4 alt;
+    int n;
+    int count;
+
+    count = 16;
+    n = 32;
+    do {
+        alt = val >> count;
+        if (alt != 0) {
+            n = n - count;
+            val = alt;
+        }
+        count >>= 1;
+    } while (count);
+    return n - val;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 modified
+ * immediate.  If not, return -1.  If so, return i:imm3:a:bcdefgh form.
+ */
+static int modifiedImmediate(u4 value)
+{
+   int zLeading;
+   int zTrailing;
+   u4 b0 = value & 0xff;
+
+   /* Note: case of value==0 must use 0:000:0:0000000 encoding */
+   if (value <= 0xFF)
+       return b0;  // 0:000:a:bcdefgh
+   if (value == ((b0 << 16) | b0))
+       return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
+   if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
+       return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
+   b0 = (value >> 8) & 0xff;
+   if (value == ((b0 << 24) | (b0 << 8)))
+       return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
+   /* Can we do it with rotation? */
+   zLeading = leadingZeros(value);
+   zTrailing = 32 - leadingZeros(~value & (value - 1));
+   /* A run of eight or fewer active bits? */
+   if ((zLeading + zTrailing) < 24)
+       return -1;  /* No - bail */
+   /* left-justify the constant, discarding msb (known to be 1) */
+   value <<= zLeading + 1;
+   /* Create bcdefgh */
+   value >>= 25;
+   /* Put it all together */
+   return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    ArmLIR *res;
+    int modImm;
+
+    if (FPREG(rDest)) {
+        return loadFPConstantValue(cUnit, rDest, value);
+    }
+
+    /* See if the value can be constructed cheaply */
+    if (LOWREG(rDest) && (value >= 0) && (value <= 255)) {
+        return newLIR2(cUnit, kThumbMovImm, rDest, value);
+    }
+    /* Check Modified immediate special cases */
+    modImm = modifiedImmediate(value);
+    if (modImm >= 0) {
+        res = newLIR2(cUnit, kThumb2MovImmShift, rDest, modImm);
+        return res;
+    }
+    modImm = modifiedImmediate(~value);
+    if (modImm >= 0) {
+        res = newLIR2(cUnit, kThumb2MvnImmShift, rDest, modImm);
+        return res;
+    }
+    /* 16-bit immediate? */
+    if ((value & 0xffff) == value) {
+        res = newLIR2(cUnit, kThumb2MovImm16, rDest, value);
+        return res;
+    }
+    /* No shortcut - go ahead and use literal pool */
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->literalList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->literalList, value);
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2LdrPcRel12;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+    /*
+     * To save space in the constant pool, we use the ADD_RRI8 instruction to
+     * add up to 255 to an existing constant value.
+     */
+    if (dataTarget->operands[0] != value) {
+        opRegImm(cUnit, kOpAdd, rDest, value - dataTarget->operands[0]);
+    }
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    ArmLIR *res;
+    cUnit->hasClassLiterals = true;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit->classPointerList, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, &cUnit->classPointerList, value);
+        /* Counts the number of class pointers in this translation */
+        cUnit->numClassPointers++;
+    }
+    ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opcode = kThumb2LdrPcRel12;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    setMemRefType(loadPcRel, true, kLiteral);
+    loadPcRel->aliasInfo = dataTarget->operands[0];
+    res = loadPcRel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+    return res;
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kThumbBUncond;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR0(cUnit, opcode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+    return newLIR2(cUnit, kThumb2BCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpPush: {
+            if ((value & 0xff00) == 0) {
+                opcode = kThumbPush;
+            } else if ((value & 0xff00) == (1 << r14lr)) {
+                /* Thumb push can handle lr, which is encoded by bit 8 */
+                opcode = kThumbPush;
+                value = (value & 0xff) | (1<<8);
+            } else {
+                opcode = kThumb2Push;
+            }
+            break;
+        }
+        case kOpPop: {
+            if ((value & 0xff00) == 0) {
+                opcode = kThumbPop;
+            } else if ((value & 0xff00) == (1 << r15pc)) {
+                /* Thumb pop can handle pc, which is encoded by bit 8 */
+                opcode = kThumbPop;
+                value = (value & 0xff) | (1<<8);
+            } else {
+                opcode = kThumb2Pop;
+            }
+            break;
+        }
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opcode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpBlx:
+            opcode = kThumbBlxR;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opcode, rDestSrc);
+}
+
+static ArmLIR *opRegRegShift(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2, int shift)
+{
+    bool thumbForm = ((shift == 0) && LOWREG(rDestSrc1) && LOWREG(rSrc2));
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdc:
+            opcode = (thumbForm) ? kThumbAdcRR : kThumb2AdcRRR;
+            break;
+        case kOpAnd:
+            opcode = (thumbForm) ? kThumbAndRR : kThumb2AndRRR;
+            break;
+        case kOpBic:
+            opcode = (thumbForm) ? kThumbBicRR : kThumb2BicRRR;
+            break;
+        case kOpCmn:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbCmnRR : kThumb2CmnRR;
+            break;
+        case kOpCmp:
+            if (thumbForm)
+                opcode = kThumbCmpRR;
+            else if ((shift == 0) && !LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbCmpHH;
+            else if ((shift == 0) && LOWREG(rDestSrc1))
+                opcode = kThumbCmpLH;
+            else if (shift == 0)
+                opcode = kThumbCmpHL;
+            else
+                opcode = kThumb2CmpRR;
+            break;
+        case kOpXor:
+            opcode = (thumbForm) ? kThumbEorRR : kThumb2EorRRR;
+            break;
+        case kOpMov:
+            assert(shift == 0);
+            if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+                opcode = kThumbMovRR;
+            else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opcode = kThumbMovRR_H2H;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbMovRR_H2L;
+            else
+                opcode = kThumbMovRR_L2H;
+            break;
+        case kOpMul:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbMul : kThumb2MulRRR;
+            break;
+        case kOpDiv:
+            assert(shift == 0);
+            opcode = kThumb2SdivRRR;
+            break;
+        case kOpMvn:
+            opcode = (thumbForm) ? kThumbMvn : kThumb2MnvRR;
+            break;
+        case kOpNeg:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbNeg : kThumb2NegRR;
+            break;
+        case kOpOr:
+            opcode = (thumbForm) ? kThumbOrr : kThumb2OrrRRR;
+            break;
+        case kOpSbc:
+            opcode = (thumbForm) ? kThumbSbc : kThumb2SbcRRR;
+            break;
+        case kOpTst:
+            opcode = (thumbForm) ? kThumbTst : kThumb2TstRR;
+            break;
+        case kOpLsl:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbLslRR : kThumb2LslRRR;
+            break;
+        case kOpLsr:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbLsrRR : kThumb2LsrRRR;
+            break;
+        case kOpAsr:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbAsrRR : kThumb2AsrRRR;
+            break;
+        case kOpRor:
+            assert(shift == 0);
+            opcode = (thumbForm) ? kThumbRorRR : kThumb2RorRRR;
+            break;
+        case kOpAdd:
+            opcode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
+            break;
+        case kOpSub:
+            opcode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
+            break;
+        case kOp2Byte:
+            assert(shift == 0);
+            return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 8);
+        case kOp2Short:
+            assert(shift == 0);
+            return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 16);
+        case kOp2Char:
+            assert(shift == 0);
+            return newLIR4(cUnit, kThumb2Ubfx, rDestSrc1, rSrc2, 0, 16);
+        default:
+            assert(0);
+            break;
+    }
+    assert(opcode >= 0);
+    if (EncodingMap[opcode].flags & IS_BINARY_OP)
+        return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
+    else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
+        if (EncodingMap[opcode].fieldLoc[2].kind == kFmtShift)
+            return newLIR3(cUnit, opcode, rDestSrc1, rSrc2, shift);
+        else
+            return newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rSrc2);
+    } else if (EncodingMap[opcode].flags & IS_QUAD_OP)
+        return newLIR4(cUnit, opcode, rDestSrc1, rDestSrc1, rSrc2, shift);
+    else {
+        assert(0);
+        return NULL;
+    }
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    return opRegRegShift(cUnit, op, rDestSrc1, rSrc2, 0);
+}
+
+static ArmLIR *opRegRegRegShift(CompilationUnit *cUnit, OpKind op,
+                                int rDest, int rSrc1, int rSrc2, int shift)
+{
+    ArmOpcode opcode = kThumbBkpt;
+    bool thumbForm = (shift == 0) && LOWREG(rDest) && LOWREG(rSrc1) &&
+                      LOWREG(rSrc2);
+    switch (op) {
+        case kOpAdd:
+            opcode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
+            break;
+        case kOpSub:
+            opcode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
+            break;
+        case kOpAdc:
+            opcode = kThumb2AdcRRR;
+            break;
+        case kOpAnd:
+            opcode = kThumb2AndRRR;
+            break;
+        case kOpBic:
+            opcode = kThumb2BicRRR;
+            break;
+        case kOpXor:
+            opcode = kThumb2EorRRR;
+            break;
+        case kOpMul:
+            assert(shift == 0);
+            opcode = kThumb2MulRRR;
+            break;
+        case kOpDiv:
+            assert(shift == 0);
+            opcode = kThumb2SdivRRR;
+            break;
+        case kOpRem:
+            opcode = kThumb2MlsRRRR;
+            break;
+        case kOpOr:
+            opcode = kThumb2OrrRRR;
+            break;
+        case kOpSbc:
+            opcode = kThumb2SbcRRR;
+            break;
+        case kOpLsl:
+            assert(shift == 0);
+            opcode = kThumb2LslRRR;
+            break;
+        case kOpLsr:
+            assert(shift == 0);
+            opcode = kThumb2LsrRRR;
+            break;
+        case kOpAsr:
+            assert(shift == 0);
+            opcode = kThumb2AsrRRR;
+            break;
+        case kOpRor:
+            assert(shift == 0);
+            opcode = kThumb2RorRRR;
+            break;
+        default:
+            assert(0);
+            break;
+    }
+    assert(opcode >= 0);
+    if (EncodingMap[opcode].flags & IS_QUAD_OP)
+        return newLIR4(cUnit, opcode, rDest, rSrc1, rSrc2, shift);
+    else {
+        assert(EncodingMap[opcode].flags & IS_TERTIARY_OP);
+        return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
+    }
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    return opRegRegRegShift(cUnit, op, rDest, rSrc1, rSrc2, 0);
+}
+
+static ArmLIR *opRegRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2, int rSrc3)
+{
+    return opRegRegRegShift(cUnit, op, rDest, rSrc1, rSrc2, rSrc3);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    ArmOpcode opcode = kThumbBkpt;
+    ArmOpcode altOpcode = kThumbBkpt;
+    bool allLowRegs = (LOWREG(rDest) && LOWREG(rSrc1));
+    int modImm = modifiedImmediate(value);
+    int modImmNeg = modifiedImmediate(-value);
+
+    switch(op) {
+        case kOpLsl:
+            if (allLowRegs)
+                return newLIR3(cUnit, kThumbLslRRI5, rDest, rSrc1, value);
+            else
+                return newLIR3(cUnit, kThumb2LslRRI5, rDest, rSrc1, value);
+        case kOpLsr:
+            if (allLowRegs)
+                return newLIR3(cUnit, kThumbLsrRRI5, rDest, rSrc1, value);
+            else
+                return newLIR3(cUnit, kThumb2LsrRRI5, rDest, rSrc1, value);
+        case kOpAsr:
+            if (allLowRegs)
+                return newLIR3(cUnit, kThumbAsrRRI5, rDest, rSrc1, value);
+            else
+                return newLIR3(cUnit, kThumb2AsrRRI5, rDest, rSrc1, value);
+        case kOpRor:
+            return newLIR3(cUnit, kThumb2RorRRI5, rDest, rSrc1, value);
+        case kOpAdd:
+            if (LOWREG(rDest) && (rSrc1 == r13sp) &&
+                (value <= 1020) && ((value & 0x3)==0)) {
+                return newLIR3(cUnit, kThumbAddSpRel, rDest, rSrc1,
+                               value >> 2);
+            } else if (LOWREG(rDest) && (rSrc1 == r15pc) &&
+                       (value <= 1020) && ((value & 0x3)==0)) {
+                return newLIR3(cUnit, kThumbAddPcRel, rDest, rSrc1,
+                               value >> 2);
+            }
+            opcode = kThumb2AddRRI8;
+            altOpcode = kThumb2AddRRR;
+            // Note: intentional fallthrough
+        case kOpSub:
+            if (allLowRegs && ((absValue & 0x7) == absValue)) {
+                if (op == kOpAdd)
+                    opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+                else
+                    opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+                return newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+            } else if ((absValue & 0xff) == absValue) {
+                if (op == kOpAdd)
+                    opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
+                else
+                    opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
+                return newLIR3(cUnit, opcode, rDest, rSrc1, absValue);
+            }
+            if (modImmNeg >= 0) {
+                op = (op == kOpAdd) ? kOpSub : kOpAdd;
+                modImm = modImmNeg;
+            }
+            if (op == kOpSub) {
+                opcode = kThumb2SubRRI8;
+                altOpcode = kThumb2SubRRR;
+            }
+            break;
+        case kOpAdc:
+            opcode = kThumb2AdcRRI8;
+            altOpcode = kThumb2AdcRRR;
+            break;
+        case kOpSbc:
+            opcode = kThumb2SbcRRI8;
+            altOpcode = kThumb2SbcRRR;
+            break;
+        case kOpOr:
+            opcode = kThumb2OrrRRI8;
+            altOpcode = kThumb2OrrRRR;
+            break;
+        case kOpAnd:
+            opcode = kThumb2AndRRI8;
+            altOpcode = kThumb2AndRRR;
+            break;
+        case kOpXor:
+            opcode = kThumb2EorRRI8;
+            altOpcode = kThumb2EorRRR;
+            break;
+        case kOpMul:
+            //TUNING: power of 2, shift & add
+            modImm = -1;
+            altOpcode = kThumb2MulRRR;
+            break;
+        case kOpDiv:
+            modImm = -1;
+            altOpcode = kThumb2SdivRRR;
+            break;
+        case kOpCmp: {
+            int modImm = modifiedImmediate(value);
+            ArmLIR *res;
+            if (modImm >= 0) {
+                res = newLIR2(cUnit, kThumb2CmpRI8, rSrc1, modImm);
+            } else {
+                int rTmp = dvmCompilerAllocTemp(cUnit);
+                res = loadConstant(cUnit, rTmp, value);
+                opRegReg(cUnit, kOpCmp, rSrc1, rTmp);
+                dvmCompilerFreeTemp(cUnit, rTmp);
+            }
+            return res;
+        }
+        default:
+            assert(0);
+    }
+
+    if (modImm >= 0) {
+        return newLIR3(cUnit, opcode, rDest, rSrc1, modImm);
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, rScratch, value);
+        if (EncodingMap[altOpcode].flags & IS_QUAD_OP)
+            res = newLIR4(cUnit, altOpcode, rDest, rSrc1, rScratch, 0);
+        else
+            res = newLIR3(cUnit, altOpcode, rDest, rSrc1, rScratch);
+        dvmCompilerFreeTemp(cUnit, rScratch);
+        return res;
+    }
+}
+
+/* Handle Thumb-only variants here - otherwise punt to opRegRegImm */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (((absValue & 0xff) == absValue) && LOWREG(rDestSrc1));
+    ArmOpcode opcode = kThumbBkpt;
+    switch (op) {
+        case kOpAdd:
+            if ( !neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+            }
+            break;
+        case kOpSub:
+            if (!neg && (rDestSrc1 == r13sp) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+            } else if (shortForm) {
+                opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+            }
+            break;
+        case kOpCmp:
+            if (LOWREG(rDestSrc1) && shortForm)
+                opcode = (shortForm) ?  kThumbCmpRI8 : kThumbCmpRR;
+            else if (LOWREG(rDestSrc1))
+                opcode = kThumbCmpRR;
+            else {
+                shortForm = false;
+                opcode = kThumbCmpHL;
+            }
+            break;
+        default:
+            /* Punt to opRegRegImm - if bad case catch it there */
+            shortForm = false;
+            break;
+    }
+    if (shortForm)
+        return newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+    }
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 floating point
+ * immediate.  If not, return -1.  If so return encoded 8-bit value.
+ */
+static int encodeImmDoubleHigh(int value)
+{
+    int res;
+    int bitA =    (value & 0x80000000) >> 31;
+    int notBitB = (value & 0x40000000) >> 30;
+    int bitB =    (value & 0x20000000) >> 29;
+    int bSmear =  (value & 0x3fc00000) >> 22;
+    int slice =   (value & 0x003f0000) >> 16;
+    int zeroes =  (value & 0x0000ffff);
+    if (zeroes != 0)
+        return -1;
+    if (bitB) {
+        if ((notBitB != 0) || (bSmear != 0x1f))
+            return -1;
+    } else {
+        if ((notBitB != 1) || (bSmear != 0x0))
+            return -1;
+    }
+    res = (bitA << 7) | (bitB << 6) | slice;
+    return res;
+}
+
+static int encodeImmDouble(int valLo, int valHi)
+{
+    int res = -1;
+    if (valLo == 0)
+        res = encodeImmDoubleHigh(valHi);
+    return res;
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    int encodedImm = encodeImmDouble(valLo, valHi);
+    ArmLIR *res;
+    int targetReg = S2D(rDestLo, rDestHi);
+    if (FPREG(rDestLo)) {
+        if ((valLo == 0) && (valHi == 0)) {
+          // TODO: we need better info about the target CPU.  a vector
+          // exclusive or would probably be better here if we could rely on
+          // its existance.
+          // Load an immediate +2.0 (which encodes to 0)
+          newLIR2(cUnit, kThumb2Vmovd_IMM8, targetReg, 0);
+          // +0.0 = +2.0 - +2.0
+          res = newLIR3(cUnit, kThumb2Vsubd, targetReg, targetReg, targetReg);
+        } else if (encodedImm >= 0) {
+            res = newLIR2(cUnit, kThumb2Vmovd_IMM8, targetReg, encodedImm);
+        } else {
+            ArmLIR* dataTarget = scanLiteralPoolWide(cUnit->literalList, valLo, valHi);
+            if (dataTarget == NULL) {
+                dataTarget = addWideData(cUnit, &cUnit->literalList, valLo, valHi);
+            }
+            ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+            loadPcRel->opcode = kThumb2Vldrd;
+            loadPcRel->generic.target = (LIR *) dataTarget;
+            loadPcRel->operands[0] = targetReg;
+            loadPcRel->operands[1] = r15pc;
+            setupResourceMasks(loadPcRel);
+            setMemRefType(loadPcRel, true, kLiteral);
+            // TODO: rework literal load disambiguation to more cleanly handle 64-bit loads
+            loadPcRel->aliasInfo = (uintptr_t)dataTarget;
+            dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+            res =  loadPcRel;
+        }
+    } else {
+        res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+        loadConstantNoClobber(cUnit, rDestHi, valHi);
+    }
+    return res;
+}
+
+static int encodeShift(int code, int amount) {
+    return ((amount & 0x1f) << 2) | code;
+}
+
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rDest);
+    ArmLIR *load;
+    ArmOpcode opcode = kThumbBkpt;
+    bool thumbForm = (allLowRegs && (scale == 0));
+    int regPtr;
+
+    if (FPREG(rDest)) {
+        assert(SINGLEREG(rDest));
+        assert((size == kWord) || (size == kSingle));
+        opcode = kThumb2Vldrs;
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+
+    switch (size) {
+        case kSingle:
+            regPtr = dvmCompilerAllocTemp(cUnit);
+            if (scale) {
+                newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+                        encodeShift(kArmLsl, scale));
+            } else {
+                opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+            }
+            load = newLIR3(cUnit, opcode, rDest, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+            if (cUnit->heapMemOp)
+                load->flags.insertWrapper = true;
+#endif
+            return load;
+        case kWord:
+            opcode = (thumbForm) ? kThumbLdrRRR : kThumb2LdrRRR;
+            break;
+        case kUnsignedHalf:
+            opcode = (thumbForm) ? kThumbLdrhRRR : kThumb2LdrhRRR;
+            break;
+        case kSignedHalf:
+            opcode = (thumbForm) ? kThumbLdrshRRR : kThumb2LdrshRRR;
+            break;
+        case kUnsignedByte:
+            opcode = (thumbForm) ? kThumbLdrbRRR : kThumb2LdrbRRR;
+            break;
+        case kSignedByte:
+            opcode = (thumbForm) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
+            break;
+        default:
+            assert(0);
+    }
+    if (thumbForm)
+        load = newLIR3(cUnit, opcode, rDest, rBase, rIndex);
+    else
+        load = newLIR4(cUnit, opcode, rDest, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rSrc);
+    ArmLIR *store;
+    ArmOpcode opcode = kThumbBkpt;
+    bool thumbForm = (allLowRegs && (scale == 0));
+    int regPtr;
+
+    if (FPREG(rSrc)) {
+        assert(SINGLEREG(rSrc));
+        assert((size == kWord) || (size == kSingle));
+        opcode = kThumb2Vstrs;
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+
+    switch (size) {
+        case kSingle:
+            regPtr = dvmCompilerAllocTemp(cUnit);
+            if (scale) {
+                newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+                        encodeShift(kArmLsl, scale));
+            } else {
+                opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+            }
+            store = newLIR3(cUnit, opcode, rSrc, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+            if (cUnit->heapMemOp)
+                store->flags.insertWrapper = true;
+#endif
+            return store;
+        case kWord:
+            opcode = (thumbForm) ? kThumbStrRRR : kThumb2StrRRR;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = (thumbForm) ? kThumbStrhRRR : kThumb2StrhRRR;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = (thumbForm) ? kThumbStrbRRR : kThumb2StrbRRR;
+            break;
+        default:
+            assert(0);
+    }
+    if (thumbForm)
+        store = newLIR3(cUnit, opcode, rSrc, rBase, rIndex);
+    else
+        store = newLIR4(cUnit, opcode, rSrc, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+#endif
+    return store;
+}
+
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null.
+ */
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+{
+    ArmLIR *res, *load;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    bool thumb2Form = (displacement < 4092 && displacement >= 0);
+    bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
+    int encodedDisp = displacement;
+
+    switch (size) {
+        case kDouble:
+        case kLong:
+            if (FPREG(rDest)) {
+                if (SINGLEREG(rDest)) {
+                    assert(FPREG(rDestHi));
+                    rDest = S2D(rDest, rDestHi);
+                }
+                opcode = kThumb2Vldrd;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+                break;
+            } else {
+                res = loadBaseDispBody(cUnit, mir, rBase, displacement, rDest,
+                                       -1, kWord, sReg);
+                loadBaseDispBody(cUnit, NULL, rBase, displacement + 4, rDestHi,
+                                 -1, kWord, INVALID_SREG);
+                return res;
+            }
+        case kSingle:
+        case kWord:
+            if (FPREG(rDest)) {
+                opcode = kThumb2Vldrs;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+                break;
+            }
+            if (LOWREG(rDest) && (rBase == r15pc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13sp) &&
+                      (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrSpRel;
+            } else if (allLowRegs && displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbLdrRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrRRI12;
+            }
+            break;
+        case kUnsignedHalf:
+            if (allLowRegs && displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbLdrhRRI5;
+            } else if (displacement < 4092 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumb2LdrhRRI12;
+            }
+            break;
+        case kSignedHalf:
+            if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrshRRI12;
+            }
+            break;
+        case kUnsignedByte:
+            if (allLowRegs && displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbLdrbRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrbRRI12;
+            }
+            break;
+        case kSignedByte:
+            if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2LdrsbRRI12;
+            }
+            break;
+        default:
+            assert(0);
+    }
+
+    if (shortForm) {
+        load = res = newLIR3(cUnit, opcode, rDest, rBase, encodedDisp);
+    } else {
+        int regOffset = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, regOffset, encodedDisp);
+        load = loadBaseIndexed(cUnit, rBase, regOffset, rDest, 0, size);
+        dvmCompilerFreeTemp(cUnit, regOffset);
+    }
+
+    if (rBase == r5FP) {
+        annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static  ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                 int displacement, int rDestLo, int rDestHi,
+                                 int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    ArmLIR *res, *store;
+    ArmOpcode opcode = kThumbBkpt;
+    bool shortForm = false;
+    bool thumb2Form = (displacement < 4092 && displacement >= 0);
+    bool allLowRegs = (LOWREG(rBase) && LOWREG(rSrc));
+    int encodedDisp = displacement;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            if (!FPREG(rSrc)) {
+                res = storeBaseDispBody(cUnit, rBase, displacement, rSrc,
+                                        -1, kWord);
+                storeBaseDispBody(cUnit, rBase, displacement + 4, rSrcHi,
+                                  -1, kWord);
+                return res;
+            }
+            if (SINGLEREG(rSrc)) {
+                assert(FPREG(rSrcHi));
+                rSrc = S2D(rSrc, rSrcHi);
+            }
+            opcode = kThumb2Vstrd;
+            if (displacement <= 1020) {
+                shortForm = true;
+                encodedDisp >>= 2;
+            }
+            break;
+        case kSingle:
+        case kWord:
+            if (FPREG(rSrc)) {
+                assert(SINGLEREG(rSrc));
+                opcode = kThumb2Vstrs;
+                if (displacement <= 1020) {
+                    shortForm = true;
+                    encodedDisp >>= 2;
+                }
+            break;
+            }
+            if (allLowRegs && displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                encodedDisp >>= 2;
+                opcode = kThumbStrRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrRRI12;
+            }
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            if (allLowRegs && displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                encodedDisp >>= 1;
+                opcode = kThumbStrhRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrhRRI12;
+            }
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            if (allLowRegs && displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opcode = kThumbStrbRRI5;
+            } else if (thumb2Form) {
+                shortForm = true;
+                opcode = kThumb2StrbRRI12;
+            }
+            break;
+        default:
+            assert(0);
+    }
+    if (shortForm) {
+        store = res = newLIR3(cUnit, opcode, rSrc, rBase, encodedDisp);
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, encodedDisp);
+        store = storeBaseIndexed(cUnit, rBase, rScratch, rSrc, 0, size);
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+
+    if (rBase == r5FP) {
+        annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+        res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+    } else {
+        res = newLIR2(cUnit, kThumb2Ldmia, rBase, rMask);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    ArmLIR *res;
+    genBarrier(cUnit);
+    if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+        res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+    } else {
+        res = newLIR2(cUnit, kThumb2Stmia, rBase, rMask);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res;
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    storeBaseDispWide(cUnit, base, 0, lowReg, highReg);
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    loadBaseDispWide(cUnit, NULL, base, 0, lowReg, highReg, INVALID_SREG);
+}
+
+/*
+ * Generate a register comparison to an immediate and branch.  Caller
+ * is responsible for setting branch target field.
+ */
+static ArmLIR *genCmpImmBranch(CompilationUnit *cUnit,
+                              ArmConditionCode cond, int reg,
+                              int checkValue)
+{
+    ArmLIR *branch;
+    int modImm;
+    if ((LOWREG(reg)) && (checkValue == 0) &&
+       ((cond == kArmCondEq) || (cond == kArmCondNe))) {
+        branch = newLIR2(cUnit,
+                         (cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
+                         reg, 0);
+    } else {
+        modImm = modifiedImmediate(checkValue);
+        if (LOWREG(reg) && ((checkValue & 0xff) == checkValue)) {
+            newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+        } else if (modImm >= 0) {
+            newLIR2(cUnit, kThumb2CmpRI8, reg, modImm);
+        } else {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            loadConstant(cUnit, tReg, checkValue);
+            opRegReg(cUnit, kOpCmp, reg, tReg);
+        }
+        branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+    }
+    return branch;
+}
+
+static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    } else {
+        assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
+        if (DOUBLEREG(rDest)) {
+            res->opcode = kThumb2Vmovd;
+        } else {
+            if (SINGLEREG(rDest)) {
+                res->opcode = SINGLEREG(rSrc) ? kThumb2Vmovs : kThumb2Fmsr;
+            } else {
+                assert(SINGLEREG(rSrc));
+                res->opcode = kThumb2Fmrs;
+            }
+        }
+        res->operands[0] = rDest;
+        res->operands[1] = rSrc;
+    }
+    setupResourceMasks(res);
+    return res;
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res;
+    ArmOpcode opcode;
+    if (FPREG(rDest) || FPREG(rSrc))
+        return fpRegCopy(cUnit, rDest, rSrc);
+    res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    if (LOWREG(rDest) && LOWREG(rSrc))
+        opcode = kThumbMovRR;
+    else if (!LOWREG(rDest) && !LOWREG(rSrc))
+         opcode = kThumbMovRR_H2H;
+    else if (LOWREG(rDest))
+         opcode = kThumbMovRR_H2L;
+    else
+         opcode = kThumbMovRR_L2H;
+
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+    bool destFP = FPREG(destLo) && FPREG(destHi);
+    bool srcFP = FPREG(srcLo) && FPREG(srcHi);
+    assert(FPREG(srcLo) == FPREG(srcHi));
+    assert(FPREG(destLo) == FPREG(destHi));
+    if (destFP) {
+        if (srcFP) {
+            genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
+        } else {
+            newLIR3(cUnit, kThumb2Fmdrr, S2D(destLo, destHi), srcLo, srcHi);
+        }
+    } else {
+        if (srcFP) {
+            newLIR3(cUnit, kThumb2Fmrrd, destLo, destHi, S2D(srcLo, srcHi));
+        } else {
+            // Handle overlap
+            if (srcHi == destLo) {
+                genRegCopy(cUnit, destHi, srcHi);
+                genRegCopy(cUnit, destLo, srcLo);
+            } else {
+                genRegCopy(cUnit, destLo, srcLo);
+                genRegCopy(cUnit, destHi, srcHi);
+            }
+        }
+    }
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    ArmLIR *push = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    push->opcode = kThumbPush;
+    /* Thumb push can handle LR (encoded at bit 8) */
+    push->operands[0] = (1 << r5FP | 1 << 8);
+    setupResourceMasks(push);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) push);
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         ArmLIR *origLIR) {
+    ArmLIR *pop = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
+    /* Thumb pop cannot store into LR - use Thumb2 here */
+    pop->opcode = kThumb2Pop;
+    pop->operands[0] = (1 << r5FP | 1 << r14lr);
+    setupResourceMasks(pop);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) pop);
+}
+#endif
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.cpp b/vm/compiler/codegen/arm/Thumb2/Gen.cpp
new file mode 100644
index 0000000..4154483
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.cpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb2 ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 6 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (2 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (10 bytes)
+ *       ldr   r0, [pc-8]   @ get prof count addr    [4 bytes]
+ *       ldr   r1, [r0]     @ load counter           [2 bytes]
+ *       add   r1, #1       @ increment              [2 bytes]
+ *       str   r1, [r0]     @ store                  [2 bytes]
+ *
+ * For periodic profiling (4 bytes)
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+    newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        /* Thumb[2] instruction used directly here to ensure correct size */
+        newLIR2(cUnit, kThumb2LdrPcReln12, r0, 8);
+        newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+        newLIR2(cUnit, kThumbAddRI8, r1, 1);
+        newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+        return 10;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR2(cUnit, kThumbBlx1,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR2(cUnit, kThumbBlx2,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        return 4;
+    }
+}
+
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/*
+ * To avoid possible conflicts, we use a lot of temps here.  Note that
+ * our usage of Thumb2 instruction forms avoids the problems with register
+ * reuse for multiply instructions prior to arm6.
+ */
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    int resLo = dvmCompilerAllocTemp(cUnit);
+    int resHi = dvmCompilerAllocTemp(cUnit);
+    int tmp1 = dvmCompilerAllocTemp(cUnit);
+
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+
+    newLIR3(cUnit, kThumb2MulRRR, tmp1, rlSrc2.lowReg, rlSrc1.highReg);
+    newLIR4(cUnit, kThumb2Umull, resLo, resHi, rlSrc2.lowReg, rlSrc1.lowReg);
+    newLIR4(cUnit, kThumb2Mla, tmp1, rlSrc1.lowReg, rlSrc2.highReg, tmp1);
+    newLIR4(cUnit, kThumb2AddRRR, resHi, tmp1, resHi, 0);
+    dvmCompilerFreeTemp(cUnit, tmp1);
+
+    rlResult = dvmCompilerGetReturnWide(cUnit);  // Just as a template, will patch
+    rlResult.lowReg = resLo;
+    rlResult.highReg = resHi;
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
+                rlSrc2.highReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    int numFPTemps = sizeof(fpTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *)dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps = (RegisterInfo *)
+            dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
+    pool->numFPTemps = numFPTemps;
+    pool->FPTemps = (RegisterInfo *)
+            dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/*
+ * Generate a Thumb2 IT instruction, which can nullify up to
+ * four subsequent instructions based on a condition and its
+ * inverse.  The condition applies to the first instruction, which
+ * is executed if the condition is met.  The string "guide" consists
+ * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
+ * A "T" means the instruction is executed if the condition is
+ * met, and an "E" means the instruction is executed if the condition
+ * is not met.
+ */
+static ArmLIR *genIT(CompilationUnit *cUnit, ArmConditionCode code,
+                     const char *guide)
+{
+    int mask;
+    int condBit = code & 1;
+    int altBit = condBit ^ 1;
+    int mask3 = 0;
+    int mask2 = 0;
+    int mask1 = 0;
+
+    //Note: case fallthroughs intentional
+    switch(strlen(guide)) {
+        case 3:
+            mask1 = (guide[2] == 'T') ? condBit : altBit;
+        case 2:
+            mask2 = (guide[1] == 'T') ? condBit : altBit;
+        case 1:
+            mask3 = (guide[0] == 'T') ? condBit : altBit;
+            break;
+        case 0:
+            break;
+        default:
+            ALOGE("Jit: bad case in genIT");
+            dvmCompilerAbort(cUnit);
+    }
+    mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
+           (1 << (3 - strlen(guide)));
+    return newLIR2(cUnit, kThumb2It, code, mask);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    ArmLIR *res;
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR3(cUnit, kThumb2StrRRI8Predec, rDPC, r5FP,
+            sizeof(StackSaveArea) - offset);
+    dvmCompilerFreeTemp(cUnit, rDPC);
+    return res;
+}
+
+/*
+ * Handle simple case (thin lock) inline.  If it's complicated, bail
+ * out to the heavyweight lock/unlock routines.  We'll use dedicated
+ * registers here in order to be in the right position in case we
+ * to bail to dvm[Lock/Unlock]Object(self, object)
+ *
+ * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
+ * r1 -> object [arg1 for dvm[Lock/Unlock]Object
+ * r2 -> intial contents of object->lock, later result of strex
+ * r3 -> self->threadId
+ * r7 -> temp to hold new lock value [unlock only]
+ * r4 -> allow to be used by utilities as general temp
+ *
+ * The result of the strex is 0 if we acquire the lock.
+ *
+ * See comments in Sync.c for the layout of the lock word.
+ * Of particular interest to this code is the test for the
+ * simple case - which we handle inline.  For monitor enter, the
+ * simple case is thin lock, held by no-one.  For monitor exit,
+ * the simple case is thin lock, held by the unlocking thread with
+ * a recurse count of 0.
+ *
+ * A minor complication is that there is a field in the lock word
+ * unrelated to locking: the hash state.  This field must be ignored, but
+ * preserved.
+ *
+ */
+static void genMonitorEnter(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    ArmLIR *target;
+    ArmLIR *hopTarget;
+    ArmLIR *branch;
+    ArmLIR *hopBranch;
+
+    assert(LW_SHAPE_THIN == 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    dvmCompilerLockAllTemps(cUnit);  // Prepare for explicit register usage
+    dvmCompilerFreeTemp(cUnit, r4PC);  // Free up r4 for general use
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, threadId), r3); // Get threadId
+    newLIR3(cUnit, kThumb2Ldrex, r2, r1,
+            offsetof(Object, lock) >> 2); // Get object->lock
+    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+    // Is lock unheld on lock or held by us (==threadId) on unlock?
+    newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
+    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+            LW_LOCK_OWNER_SHIFT - 1);
+    hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
+    newLIR4(cUnit, kThumb2Strex, r2, r3, r1, offsetof(Object, lock) >> 2);
+    dvmCompilerGenMemBarrier(cUnit, kISH);
+    branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
+
+    hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+    hopTarget->defMask = ENCODE_ALL;
+    hopBranch->generic.target = (LIR *)hopTarget;
+
+    // Export PC (part 1)
+    loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+    /* Get dPC of next insn */
+    loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, r5FP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    /* Call template, and don't return */
+    genRegCopy(cUnit, r0, r6SELF);
+    genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    // Resume here
+    target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)target;
+}
+
+/*
+ * For monitor unlock, we don't have to use ldrex/strex.  Once
+ * we've determined that the lock is thin and that we own it with
+ * a zero recursion count, it's safe to punch it back to the
+ * initial, unlock thin state with a store word.
+ */
+static void genMonitorExit(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    ArmLIR *target;
+    ArmLIR *branch;
+    ArmLIR *hopTarget;
+    ArmLIR *hopBranch;
+
+    assert(LW_SHAPE_THIN == 0);
+    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    dvmCompilerLockAllTemps(cUnit);  // Prepare for explicit register usage
+    dvmCompilerFreeTemp(cUnit, r4PC);  // Free up r4 for general use
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r1, offsetof(Object, lock), r2); // Get object->lock
+    loadWordDisp(cUnit, r6SELF, offsetof(Thread, threadId), r3); // Get threadId
+    // Is lock unheld on lock or held by us (==threadId) on unlock?
+    opRegRegImm(cUnit, kOpAnd, r7, r2,
+                (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+            LW_LOCK_OWNER_SHIFT - 1);
+    opRegReg(cUnit, kOpSub, r2, r3);
+    hopBranch = opCondBranch(cUnit, kArmCondNe);
+    dvmCompilerGenMemBarrier(cUnit, kISH);
+    storeWordDisp(cUnit, r1, offsetof(Object, lock), r7);
+    branch = opNone(cUnit, kOpUncondBr);
+
+    hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+    hopTarget->defMask = ENCODE_ALL;
+    hopBranch->generic.target = (LIR *)hopTarget;
+
+    // Export PC (part 1)
+    loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+    LOAD_FUNC_ADDR(cUnit, r7, (int)dvmUnlockObject);
+    genRegCopy(cUnit, r0, r6SELF);
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, r5FP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    opReg(cUnit, kOpBlx, r7);
+    /* Did we throw? */
+    ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
+    loadConstant(cUnit, r0,
+                 (int) (cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+
+    // Resume here
+    target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branch->generic.target = (LIR *)target;
+    branchOver->generic.target = (LIR *) target;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    if (mir->dalvikInsn.opcode == OP_MONITOR_ENTER)
+        genMonitorEnter(cUnit, mir);
+    else
+        genMonitorExit(cUnit, mir);
+}
+
+/*
+ * 64-bit 3way compare function.
+ *     mov   r7, #-1
+ *     cmp   op1hi, op2hi
+ *     blt   done
+ *     bgt   flip
+ *     sub   r7, op1lo, op2lo (treat as unsigned)
+ *     beq   done
+ *     ite   hi
+ *     mov(hi)   r7, #-1
+ *     mov(!hi)  r7, #1
+ * flip:
+ *     neg   r7
+ * done:
+ */
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
+                       RegLocation rlDest, RegLocation rlSrc1,
+                       RegLocation rlSrc2)
+{
+    RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
+    ArmLIR *target1;
+    ArmLIR *target2;
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+    rlTemp.lowReg = dvmCompilerAllocTemp(cUnit);
+    loadConstant(cUnit, rlTemp.lowReg, -1);
+    opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
+    ArmLIR *branch1 = opCondBranch(cUnit, kArmCondLt);
+    ArmLIR *branch2 = opCondBranch(cUnit, kArmCondGt);
+    opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    ArmLIR *branch3 = opCondBranch(cUnit, kArmCondEq);
+
+    genIT(cUnit, kArmCondHi, "E");
+    newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
+    loadConstant(cUnit, rlTemp.lowReg, 1);
+    genBarrier(cUnit);
+
+    target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target2->defMask = -1;
+    opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
+
+    target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target1->defMask = -1;
+
+    storeValue(cUnit, rlDest, rlTemp);
+
+    branch1->generic.target = (LIR *)target1;
+    branch2->generic.target = (LIR *)target2;
+    branch3->generic.target = branch1->generic.target;
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, true);
+    rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vabss, rlResult.lowReg, rlSrc.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+    rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR2(cUnit, kThumb2Vabsd, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc.lowReg, rlSrc.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+    genIT(cUnit, (isMin) ? kArmCondGt : kArmCondLt, "E");
+    opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
+    opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
+    genBarrier(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
+                     encodeShift(kArmLsl, secondBit - firstBit));
+    if (firstBit != 0) {
+        opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
+    }
+}
+
+static void genMultiplyByShiftAndReverseSubtract(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit)
+{
+    newLIR4(cUnit, kThumb2RsbRRR, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
+            encodeShift(kArmLsl, lit));
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp b/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp
new file mode 100644
index 0000000..6adfd62
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Ralloc.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/* Stress mode for testing: if defined will reverse corereg/floatreg hint */
+//#define REGCLASS_STRESS_MODE
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+
+#if defined(REGCLASS_STRESS_MODE)
+    fpHint = !fpHint;
+#endif
+
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
+        lowReg = dvmCompilerAllocTempDouble(cUnit);
+        highReg = lowReg + 1;
+    } else {
+        lowReg = dvmCompilerAllocTemp(cUnit);
+        highReg = dvmCompilerAllocTemp(cUnit);
+    }
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass)
+{
+#if defined(REGCLASS_STRESS_MODE)
+    fpHint = !fpHint;
+#endif
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
+        return dvmCompilerAllocTempFloat(cUnit);
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
new file mode 100644
index 0000000..6c89b11
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 200;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* No method JIT for Thumb backend */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
new file mode 100644
index 0000000..727c521
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    vstmia r0, {d8-d15}
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    vldmia r0, {d8-d15}
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp
new file mode 100644
index 0000000..55321bb
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE_VFP
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb-specific factory utilities */
+#include "../Thumb/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.cpp"
+/* Thumb+VFP codegen routines */
+#include "../FP/ThumbVFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "../armv5te/MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
new file mode 100644
index 0000000..5c7fbbe
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-armv5te.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 200;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* No method JIT for Thumb backend */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.h b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
new file mode 100644
index 0000000..39a9548
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv5te/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv5te/CallingConvention.S b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
new file mode 100644
index 0000000..0cbc64f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv5te/Codegen.cpp b/vm/compiler/codegen/arm/armv5te/Codegen.cpp
new file mode 100644
index 0000000..a379c2a
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb-specific building blocks */
+#include "../Thumb/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.cpp"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/ThumbPortableFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp b/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..a0dba0a
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/MethodCodegenDriver.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    ALOGE("Method-based JIT not supported for the v5te target");
+    dvmAbort();
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
new file mode 100644
index 0000000..a81a2e7
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB2;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv7-a-neon/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv7-a-neon/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 40;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 1500 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, barrierKind);
+    dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h
new file mode 100644
index 0000000..0cb82f6
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../../template/armv7-a-neon/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+} TemplateOpcode;
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S b/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    vstmia r0, {d8-d15}
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    vldmia r0, {d8-d15}
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/Codegen.cpp b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.cpp
new file mode 100644
index 0000000..8f15174
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A_NEON
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Target indepedent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..84744b5
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/MethodCodegenDriver.cpp
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#if 0
+
+/*
+ * Rebuild the interpreter frame then punt to the interpreter to execute
+ * instruction at specified PC.
+ *
+ * Currently parameters are passed to the current frame, so we just need to
+ * grow the stack save area above it, fill certain fields in StackSaveArea and
+ * Thread that are skipped during whole-method invocation (specified below),
+ * then return to the interpreter.
+ *
+ * StackSaveArea:
+ *  - prevSave
+ *  - prevFrame
+ *  - savedPc
+ *  - returnAddr
+ *  - method
+ *
+ * Thread:
+ *  - method
+ *  - methodClassDex
+ *  - curFrame
+ */
+static void genMethodInflateAndPunt(CompilationUnit *cUnit, MIR *mir,
+                                    BasicBlock *bb)
+{
+    int oldStackSave = r0;
+    int newStackSave = r1;
+    int oldFP = r2;
+    int savedPC = r3;
+    int currentPC = r4PC;
+    int returnAddr = r7;
+    int method = r8;
+    int pDvmDex = r9;
+
+    /*
+     * TODO: check whether to raise the stack overflow exception when growing
+     * the stack save area.
+     */
+
+    /* Send everything to home location */
+    dvmCompilerFlushAllRegs(cUnit);
+
+    /* oldStackSave = r5FP + sizeof(current frame) */
+    opRegRegImm(cUnit, kOpAdd, oldStackSave, r5FP,
+                cUnit->method->registersSize * 4);
+    /* oldFP = oldStackSave + sizeof(stackSaveArea) */
+    opRegRegImm(cUnit, kOpAdd, oldFP, oldStackSave, sizeof(StackSaveArea));
+    /* newStackSave = r5FP - sizeof(StackSaveArea) */
+    opRegRegImm(cUnit, kOpSub, newStackSave, r5FP, sizeof(StackSaveArea));
+
+    loadWordDisp(cUnit, r13sp, 0, savedPC);
+    loadConstant(cUnit, currentPC, (int) (cUnit->method->insns + mir->offset));
+    loadConstant(cUnit, method, (int) cUnit->method);
+    loadConstant(cUnit, pDvmDex, (int) cUnit->method->clazz->pDvmDex);
+#ifdef EASY_GDB
+    /* newStackSave->prevSave = oldStackSave */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevSave),
+                  oldStackSave);
+#endif
+    /* newStackSave->prevSave = oldStackSave */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevFrame),
+                  oldFP);
+    /* newStackSave->savedPc = savedPC */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, savedPc),
+                  savedPC);
+    /* return address */
+    loadConstant(cUnit, returnAddr, 0);
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, returnAddr),
+                  returnAddr);
+    /* newStackSave->method = method */
+    storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, method), method);
+    /* thread->method = method */
+    storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, method), method);
+    /* thread->interpSave.curFrame = current FP */
+    storeWordDisp(cUnit, r6SELF, offsetof(Thread, interpSave.curFrame), r5FP);
+    /* thread->methodClassDex = pDvmDex */
+    storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, methodClassDex),
+                  pDvmDex);
+    /* Restore the stack pointer */
+    opRegImm(cUnit, kOpAdd, r13sp, 16);
+    genPuntToInterp(cUnit, mir->offset);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ *
+ * TODO - most them are just pass-through to the trace-based versions for now
+ */
+static bool handleMethodFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                             BasicBlock *bb, ArmLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch && gDvmJit.genSuspendPoll) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+    genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    return false;
+}
+
+static bool handleMethodFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID:
+            return false;
+        default:
+            return handleFmt10x(cUnit, mir);
+    }
+}
+
+static bool handleMethodFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt11n_Fmt31i(cUnit, mir);
+}
+
+static bool handleMethodFmt11x(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_THROW:
+            genMethodInflateAndPunt(cUnit, mir, bb);
+            return false;
+        default:
+            return handleFmt11x(cUnit, mir);
+    }
+}
+
+static bool handleMethodFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt12x(cUnit, mir);
+}
+
+static bool handleMethodFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt20bc(cUnit, mir);
+}
+
+static bool handleMethodFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21c_Fmt31c(cUnit, mir);
+}
+
+static bool handleMethodFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21h(cUnit, mir);
+}
+
+static bool handleMethodFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt21s(cUnit, mir);
+}
+
+static bool handleMethodFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    return handleFmt21t(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22b_Fmt22s(cUnit, mir);
+}
+
+static bool handleMethodFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22c(cUnit, mir);
+}
+
+static bool handleMethodFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22cs(cUnit, mir);
+}
+
+static bool handleMethodFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                               ArmLIR *labelList)
+{
+    return handleFmt22t(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt22x_Fmt32x(cUnit, mir);
+}
+
+static bool handleMethodFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt23x(cUnit, mir);
+}
+
+static bool handleMethodFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt31t(cUnit, mir);
+}
+
+static bool handleMethodFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, ArmLIR *labelList)
+{
+    return handleFmt35c_3rc(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, ArmLIR *labelList)
+{
+    return handleFmt35ms_3rms(cUnit, mir, bb, labelList);
+}
+
+static bool handleMethodExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleExecuteInline(cUnit, mir);
+}
+
+static bool handleMethodFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    return handleFmt51l(cUnit, mir);
+}
+
+/* Handle the content in each basic block */
+static bool methodBlockCodeGen(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    ArmLIR *labelList = (ArmLIR *) cUnit->blockLabelList;
+    int blockId = bb->id;
+
+    cUnit->curBlock = bb;
+    labelList[blockId].operands[0] = bb->startOffset;
+
+    /* Insert the block label */
+    labelList[blockId].opcode = kArmPseudoNormalBlockLabel;
+    dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    ArmLIR *headLIR = NULL;
+
+    if (bb->blockType == kEntryBlock) {
+        /* r0 = callsitePC */
+        opImm(cUnit, kOpPush, (1 << r0 | 1 << r1 | 1 << r5FP | 1 << r14lr));
+        opRegImm(cUnit, kOpSub, r5FP,
+                 sizeof(StackSaveArea) + cUnit->method->registersSize * 4);
+
+    } else if (bb->blockType == kExitBlock) {
+        /* No need to pop r0 and r1 */
+        opRegImm(cUnit, kOpAdd, r13sp, 8);
+        opImm(cUnit, kOpPop, (1 << r5FP | 1 << r15pc));
+    }
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+        dvmCompilerResetRegPool(cUnit);
+        if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+            dvmCompilerClobberAllRegs(cUnit);
+        }
+
+        if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+            dvmCompilerResetDefTracking(cUnit);
+        }
+
+        Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+        InstructionFormat dalvikFormat =
+            dexGetFormatFromOpcode(dalvikOpcode);
+
+        ArmLIR *boundaryLIR;
+
+        /*
+         * Don't generate the boundary LIR unless we are debugging this
+         * trace or we need a scheduling barrier.
+         */
+        if (headLIR == NULL || cUnit->printMe == true) {
+            boundaryLIR =
+                newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+                        mir->offset,
+                        (int) dvmCompilerGetDalvikDisassembly(
+                            &mir->dalvikInsn, ""));
+            /* Remember the first LIR for this block */
+            if (headLIR == NULL) {
+                headLIR = boundaryLIR;
+                /* Set the first boundaryLIR as a scheduling barrier */
+                headLIR->defMask = ENCODE_ALL;
+            }
+        }
+
+        /* Don't generate the SSA annotation unless verbose mode is on */
+        if (cUnit->printMe && mir->ssaRep) {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+        }
+
+        bool notHandled;
+        switch (dalvikFormat) {
+            case kFmt10t:
+            case kFmt20t:
+            case kFmt30t:
+                notHandled = handleMethodFmt10t_Fmt20t_Fmt30t(cUnit, mir, bb,
+                                                              labelList);
+                break;
+            case kFmt10x:
+                notHandled = handleMethodFmt10x(cUnit, mir);
+                break;
+            case kFmt11n:
+            case kFmt31i:
+                notHandled = handleMethodFmt11n_Fmt31i(cUnit, mir);
+                break;
+            case kFmt11x:
+                notHandled = handleMethodFmt11x(cUnit, mir, bb, labelList);
+                break;
+            case kFmt12x:
+                notHandled = handleMethodFmt12x(cUnit, mir);
+                break;
+            case kFmt20bc:
+                notHandled = handleMethodFmt20bc(cUnit, mir);
+                break;
+            case kFmt21c:
+            case kFmt31c:
+                notHandled = handleMethodFmt21c_Fmt31c(cUnit, mir);
+                break;
+            case kFmt21h:
+                notHandled = handleMethodFmt21h(cUnit, mir);
+                break;
+            case kFmt21s:
+                notHandled = handleMethodFmt21s(cUnit, mir);
+                break;
+            case kFmt21t:
+                notHandled = handleMethodFmt21t(cUnit, mir, bb, labelList);
+                break;
+            case kFmt22b:
+            case kFmt22s:
+                notHandled = handleMethodFmt22b_Fmt22s(cUnit, mir);
+                break;
+            case kFmt22c:
+                notHandled = handleMethodFmt22c(cUnit, mir);
+                break;
+            case kFmt22cs:
+                notHandled = handleMethodFmt22cs(cUnit, mir);
+                break;
+            case kFmt22t:
+                notHandled = handleMethodFmt22t(cUnit, mir, bb, labelList);
+                break;
+            case kFmt22x:
+            case kFmt32x:
+                notHandled = handleMethodFmt22x_Fmt32x(cUnit, mir);
+                break;
+            case kFmt23x:
+                notHandled = handleMethodFmt23x(cUnit, mir);
+                break;
+            case kFmt31t:
+                notHandled = handleMethodFmt31t(cUnit, mir);
+                break;
+            case kFmt3rc:
+            case kFmt35c:
+                notHandled = handleMethodFmt35c_3rc(cUnit, mir, bb, labelList);
+                break;
+            case kFmt3rms:
+            case kFmt35ms:
+                notHandled = handleMethodFmt35ms_3rms(cUnit, mir, bb,
+                                                      labelList);
+                break;
+            case kFmt35mi:
+            case kFmt3rmi:
+                notHandled = handleMethodExecuteInline(cUnit, mir);
+                break;
+            case kFmt51l:
+                notHandled = handleMethodFmt51l(cUnit, mir);
+                break;
+            default:
+                notHandled = true;
+                break;
+        }
+
+        /* FIXME - to be implemented */
+        if (notHandled == true && dalvikOpcode >= kNumPackedOpcodes) {
+            notHandled = false;
+        }
+
+        if (notHandled) {
+            ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                 mir->offset,
+                 dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                 dalvikFormat);
+            dvmCompilerAbort(cUnit);
+            break;
+        }
+    }
+
+    if (headLIR) {
+        /*
+         * Eliminate redundant loads/stores and delay stores into later
+         * slots
+         */
+        dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                           cUnit->lastLIRInsn);
+
+        /*
+         * Generate an unconditional branch to the fallthrough block.
+         */
+        if (bb->fallThrough) {
+            genUnconditionalBranch(cUnit,
+                                   &labelList[bb->fallThrough->id]);
+        }
+    }
+    return false;
+}
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    // FIXME - enable method compilation for selected routines here
+    if (strcmp(cUnit->method->name, "add")) return;
+
+    /* Used to hold the labels of each block */
+    cUnit->blockLabelList =
+        (void *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, methodBlockCodeGen,
+                                          kPreOrderDFSTraversal,
+                                          false /* isIterative */);
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+    // FIXME - temporarily enable verbose printing for all methods
+    cUnit->printMe = true;
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+#else
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit) {
+    // Method-based JIT not supported for ARM.
+}
+
+#endif
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
new file mode 100644
index 0000000..72ae3ce
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_THUMB2;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv7-a/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv7-a/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 40;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 1500 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    /* Hard limit for Arm of 2M */
+    assert(gDvmJit.codeCacheSize <= 2 * 1024 * 1024);
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last
+     * offset from the struct is less than 128.
+     */
+    if ((offsetof(Thread, jitToInterpEntries) +
+         sizeof(struct JitToInterpEntries)) >= 128) {
+        ALOGE("Thread.jitToInterpEntries size overflow");
+        dvmAbort();
+    }
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, barrierKind);
+    dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
new file mode 100644
index 0000000..8003f73
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode {
+#include "../../../template/armv7-a/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV7_A_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/arm/armv7-a/CallingConvention.S b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+    vstmia r0, {d8-d15}
+    bx     lr
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+    vldmia r0, {d8-d15}
+    bx     lr
diff --git a/vm/compiler/codegen/arm/armv7-a/Codegen.cpp b/vm/compiler/codegen/arm/armv7-a/Codegen.cpp
new file mode 100644
index 0000000..e1b0ee9
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/Codegen.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A
+#define TGT_LIR ArmLIR
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Arm codegen building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.cpp"
+/* Target independent factory utilities */
+#include "../../CodegenFactory.cpp"
+/* Arm-specific factory utilities */
+#include "../ArchFactory.cpp"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.cpp"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.cpp"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Driver for method-based JIT */
+#include "../armv7-a-neon/MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/mips/ArchUtility.cpp b/vm/compiler/codegen/mips/ArchUtility.cpp
new file mode 100644
index 0000000..0b10cda
--- /dev/null
+++ b/vm/compiler/codegen/mips/ArchUtility.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "../../CompilerInternals.h"
+#include "libdex/DexOpcodes.h"
+#include "MipsLIR.h"
+
+/* For dumping instructions */
+#define MIPS_REG_COUNT 32
+static const char *mipsRegName[MIPS_REG_COUNT] = {
+    "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+    "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+    "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+    "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
+};
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+static void buildInsnString(const char *fmt, MipsLIR *lir, char* buf,
+                            unsigned char *baseAddr, int size)
+{
+    int i;
+    char *bufEnd = &buf[size-1];
+    const char *fmtEnd = &fmt[strlen(fmt)];
+    char tbuf[256];
+    const char *name;
+    char nc;
+    while (fmt < fmtEnd) {
+        int operand;
+        if (*fmt == '!') {
+            fmt++;
+            assert(fmt < fmtEnd);
+            nc = *fmt++;
+            if (nc=='!') {
+                strcpy(tbuf, "!");
+            } else {
+               assert(fmt < fmtEnd);
+               assert((unsigned)(nc-'0') < 4);
+               operand = lir->operands[nc-'0'];
+               switch(*fmt++) {
+                   case 'b':
+                       strcpy(tbuf,"0000");
+                       for (i=3; i>= 0; i--) {
+                           tbuf[i] += operand & 1;
+                           operand >>= 1;
+                       }
+                       break;
+                   case 's':
+                       sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
+                       break;
+                   case 'S':
+		       assert(((operand & FP_REG_MASK) & 1) == 0);
+                       sprintf(tbuf,"$f%d",operand & FP_REG_MASK);
+                       break;
+                   case 'h':
+                       sprintf(tbuf,"%04x", operand);
+                       break;
+                   case 'M':
+                   case 'd':
+                       sprintf(tbuf,"%d", operand);
+                       break;
+                   case 'D':
+                       sprintf(tbuf,"%d", operand+1);
+                       break;
+                   case 'E':
+                       sprintf(tbuf,"%d", operand*4);
+                       break;
+                   case 'F':
+                       sprintf(tbuf,"%d", operand*2);
+                       break;
+                   case 'c':
+                       switch (operand) {
+                           case kMipsCondEq:
+                               strcpy(tbuf, "eq");
+                               break;
+                           case kMipsCondNe:
+                               strcpy(tbuf, "ne");
+                               break;
+                           case kMipsCondLt:
+                               strcpy(tbuf, "lt");
+                               break;
+                           case kMipsCondGe:
+                               strcpy(tbuf, "ge");
+                               break;
+                           case kMipsCondGt:
+                               strcpy(tbuf, "gt");
+                               break;
+                           case kMipsCondLe:
+                               strcpy(tbuf, "le");
+                               break;
+                           case kMipsCondCs:
+                               strcpy(tbuf, "cs");
+                               break;
+                           case kMipsCondMi:
+                               strcpy(tbuf, "mi");
+                               break;
+                           default:
+                               strcpy(tbuf, "");
+                               break;
+                       }
+                       break;
+                   case 't':
+                       sprintf(tbuf,"0x%08x (L%p)",
+                               (int) baseAddr + lir->generic.offset + 4 +
+                               (operand << 2),
+                               lir->generic.target);
+                       break;
+                   case 'T':
+                       sprintf(tbuf,"0x%08x",
+                               (int) (operand << 2));
+                       break;
+                   case 'u': {
+                       int offset_1 = lir->operands[0];
+                       int offset_2 = NEXT_LIR(lir)->operands[0];
+                       intptr_t target =
+                           ((((intptr_t) baseAddr + lir->generic.offset + 4) &
+                            ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+                           0xfffffffc;
+                       sprintf(tbuf, "%p", (void *) target);
+                       break;
+                    }
+
+                   /* Nothing to print for BLX_2 */
+                   case 'v':
+                       strcpy(tbuf, "see above");
+                       break;
+                   case 'r':
+                       assert(operand >= 0 && operand < MIPS_REG_COUNT);
+                       strcpy(tbuf, mipsRegName[operand]);
+                       break;
+                   case 'B':
+                       switch (operand) {
+                           case kSY:
+                               name = "0/sy";
+                               break;
+                           case kWMB:
+                               name = "4/wmb";
+                               break;
+                           case kMB:
+                               name = "16/mb";
+                               break;
+                           case kACQUIRE:
+                               name = "17/acquire";
+                               break;
+                           case kRELEASE:
+                               name = "18/release";
+                               break;
+                           case kRMB:
+                               name = "19/rmb";
+                               break;
+                           default:
+                               name = "DecodeError";
+                               break;
+                       }
+                       strcpy(tbuf, name);
+                       break;
+                   default:
+                       strcpy(tbuf,"DecodeError");
+                       break;
+               }
+               if (buf+strlen(tbuf) <= bufEnd) {
+                   strcpy(buf, tbuf);
+                   buf += strlen(tbuf);
+               } else {
+                   break;
+               }
+            }
+        } else {
+           *buf++ = *fmt++;
+        }
+        if (buf == bufEnd)
+            break;
+    }
+    *buf = 0;
+}
+
+void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
+{
+    char buf[256];
+    buf[0] = 0;
+    MipsLIR *mipsLIR = (MipsLIR *) lir;
+
+    if (mask == ENCODE_ALL) {
+        strcpy(buf, "all");
+    } else {
+        char num[8];
+        int i;
+
+        for (i = 0; i < kRegEnd; i++) {
+            if (mask & (1ULL << i)) {
+                sprintf(num, "%d ", i);
+                strcat(buf, num);
+            }
+        }
+
+        if (mask & ENCODE_CCODE) {
+            strcat(buf, "cc ");
+        }
+        if (mask & ENCODE_FP_STATUS) {
+            strcat(buf, "fpcc ");
+        }
+        /* Memory bits */
+        if (mipsLIR && (mask & ENCODE_DALVIK_REG)) {
+            sprintf(buf + strlen(buf), "dr%d%s", mipsLIR->aliasInfo & 0xffff,
+                    (mipsLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+        }
+        if (mask & ENCODE_LITERAL) {
+            strcat(buf, "lit ");
+        }
+
+        if (mask & ENCODE_HEAP_REF) {
+            strcat(buf, "heap ");
+        }
+        if (mask & ENCODE_MUST_NOT_ALIAS) {
+            strcat(buf, "noalias ");
+        }
+    }
+    if (buf[0]) {
+        ALOGD("%s: %s", prefix, buf);
+    }
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+#define DUMP_SSA_REP(X)
+
+/* Pretty-print a LIR instruction */
+void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
+{
+    MipsLIR *lir = (MipsLIR *) arg;
+    char buf[256];
+    char opName[256];
+    int offset = lir->generic.offset;
+    int dest = lir->operands[0];
+    const bool dumpNop = false;
+
+    /* Handle pseudo-ops individually, and all regular insns as a group */
+    switch(lir->opcode) {
+        case kMipsChainingCellBottom:
+            ALOGD("-------- end of chaining cells (0x%04x)", offset);
+            break;
+        case kMipsPseudoBarrier:
+            ALOGD("-------- BARRIER");
+            break;
+        case kMipsPseudoExtended:
+            /* intentional fallthrough */
+        case kMipsPseudoSSARep:
+            DUMP_SSA_REP(ALOGD("-------- %s", (char *) dest));
+            break;
+        case kMipsPseudoChainingCellBackwardBranch:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (backward branch): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellNormal:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (normal): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellHot:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (hot): 0x%04x", dest);
+            break;
+        case kMipsPseudoChainingCellInvokePredicted:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (predicted): %s%s",
+                 dest ? ((Method *) dest)->clazz->descriptor : "",
+                 dest ? ((Method *) dest)->name : "N/A");
+            break;
+        case kMipsPseudoChainingCellInvokeSingleton:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- chaining cell (invoke singleton): %s%s/%p",
+                 ((Method *)dest)->clazz->descriptor,
+                 ((Method *)dest)->name,
+                 ((Method *)dest)->insns);
+            break;
+        case kMipsPseudoEntryBlock:
+            ALOGD("-------- entry offset: 0x%04x", dest);
+            break;
+        case kMipsPseudoDalvikByteCodeBoundary:
+            ALOGD("-------- dalvik offset: 0x%04x @ %s", dest,
+                 (char *) lir->operands[1]);
+            break;
+        case kMipsPseudoExitBlock:
+            ALOGD("-------- exit offset: 0x%04x", dest);
+            break;
+        case kMipsPseudoPseudoAlign4:
+            ALOGD("%p (%04x): .align4", baseAddr + offset, offset);
+            break;
+        case kMipsPseudoPCReconstructionCell:
+            ALOGD("L%p:", lir);
+            ALOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x", dest,
+                 lir->operands[1]);
+            break;
+        case kMipsPseudoPCReconstructionBlockLabel:
+            /* Do nothing */
+            break;
+        case kMipsPseudoEHBlockLabel:
+            ALOGD("Exception_Handling:");
+            break;
+        case kMipsPseudoTargetLabel:
+        case kMipsPseudoNormalBlockLabel:
+            ALOGD("L%p:", lir);
+            break;
+        default:
+            if (lir->flags.isNop && !dumpNop) {
+                break;
+            }
+            buildInsnString(EncodingMap[lir->opcode].name, lir, opName,
+                            baseAddr, 256);
+            buildInsnString(EncodingMap[lir->opcode].fmt, lir, buf, baseAddr,
+                            256);
+            ALOGD("%p (%04x): %08x %-9s%s%s",
+                 baseAddr + offset, offset, *(u4 *)(baseAddr + offset), opName, buf,
+                 lir->flags.isNop ? "(nop)" : "");
+            break;
+    }
+
+    if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->useMask, "use"));
+    }
+    if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->defMask, "def"));
+    }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+    ALOGD("Dumping LIR insns");
+    LIR *lirInsn;
+    MipsLIR *mipsLIR;
+
+    ALOGD("installed code is at %p", cUnit->baseAddr);
+    ALOGD("total size is %d bytes", cUnit->totalSize);
+    for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+        dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
+    }
+    for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
+        mipsLIR = (MipsLIR *) lirInsn;
+        ALOGD("%p (%04x): .class (%s)",
+             (char*)cUnit->baseAddr + mipsLIR->generic.offset,
+             mipsLIR->generic.offset,
+             ((CallsiteInfo *) mipsLIR->operands[0])->classDescriptor);
+    }
+    for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
+        mipsLIR = (MipsLIR *) lirInsn;
+        ALOGD("%p (%04x): .word (%#x)",
+             (char*)cUnit->baseAddr + mipsLIR->generic.offset,
+             mipsLIR->generic.offset,
+             mipsLIR->operands[0]);
+    }
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /* 0x66 is an invalid opcode for mips. */
+    memset(start, 0x66, size);
+}
diff --git a/vm/compiler/codegen/mips/Assemble.cpp b/vm/compiler/codegen/mips/Assemble.cpp
new file mode 100644
index 0000000..76aa606
--- /dev/null
+++ b/vm/compiler/codegen/mips/Assemble.cpp
@@ -0,0 +1,2329 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+
+#include "../../CompilerInternals.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+#include <unistd.h>             /* for cacheflush */
+#include <sys/mman.h>           /* for protection change */
+
+#define MAX_ASSEMBLER_RETRIES 10
+
+/*
+ * opcode: MipsOpCode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+                     k3, k3s, k3e, flags, name, fmt, size) \
+        {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+                    {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ *     0 -> operands[0] (dest)
+ *     1 -> operands[1] (src1)
+ *     2 -> operands[2] (src2)
+ *     3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ *     h -> 4-digit hex
+ *     d -> decimal
+ *     E -> decimal*4
+ *     F -> decimal*2
+ *     c -> branch condition (beq, bne, etc.)
+ *     t -> pc-relative target
+ *     T -> pc-region target
+ *     u -> 1st half of bl[x] target
+ *     v -> 2nd half ob bl[x] target
+ *     R -> register list
+ *     s -> single precision floating point register
+ *     S -> double precision floating point register
+ *     m -> Thumb2 modified immediate
+ *     n -> complimented Thumb2 modified immediate
+ *     M -> Thumb2 16-bit zero-extended immediate
+ *     b -> 4-digit binary
+ *     B -> sync option string (SY, WMB, MB, ACQUIRE, RELEASE, RMB)
+ *
+ *  [!] escape.  To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum MipsOpcode from MipsLIR.h */
+MipsEncodingMap EncodingMap[kMipsLast] = {
+    ENCODING_MAP(kMips32BitData, 0x00000000,
+                 kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP,
+                 "data", "0x!0h(!0d)", 2),
+    ENCODING_MAP(kMipsAddiu, 0x24000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "addiu", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsAddu, 0x00000021,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "addu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsAnd, 0x00000024,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "and", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsAndi, 0x30000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "andi", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsB, 0x10000000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+                 "b", "!0t", 2),
+    ENCODING_MAP(kMipsBal, 0x04110000,
+                 kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR,
+                 "bal", "!0t", 2),
+    ENCODING_MAP(kMipsBeq, 0x10000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01,
+                 "beq", "!0r,!1r,!2t", 2),
+    ENCODING_MAP(kMipsBeqz, 0x10000000, /* same as beq above with t = $zero */
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "beqz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBgez, 0x04010000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bgez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBgtz, 0x1C000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bgtz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBlez, 0x18000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "blez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBltz, 0x04000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bltz", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBnez, 0x14000000, /* same as bne below with t = $zero */
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "bnez", "!0r,!1t", 2),
+    ENCODING_MAP(kMipsBne, 0x14000000,
+                 kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01,
+                 "bne", "!0r,!1r,!2t", 2),
+    ENCODING_MAP(kMipsDiv, 0x0000001a,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtBitBlt, 25, 21,
+                 kFmtBitBlt, 20, 16, IS_QUAD_OP | REG_DEF01 | REG_USE23,
+                 "div", "!2r,!3r", 2),
+#if __mips_isa_rev>=2
+    ENCODING_MAP(kMipsExt, 0x7c000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6,
+                 kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1,
+                 "ext", "!0r,!1r,!2d,!3D", 2),
+#endif
+    ENCODING_MAP(kMipsJal, 0x0c000000,
+                 kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+                 "jal", "!0T(!0E)", 2),
+    ENCODING_MAP(kMipsJalr, 0x00000009,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF0_USE1,
+                 "jalr", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsJr, 0x00000008,
+                 kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0,
+                 "jr", "!0r", 2),
+    ENCODING_MAP(kMipsLahi, 0x3C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "lahi/lui", "!0r,0x!1h(!1d)", 2),
+    ENCODING_MAP(kMipsLalo, 0x34000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "lalo/ori", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsLui, 0x3C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+                 "lui", "!0r,0x!1h(!1d)", 2),
+    ENCODING_MAP(kMipsLb, 0x80000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lb", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLbu, 0x90000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lbu", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLh, 0x84000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lh", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLhu, 0x94000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lhu", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsLw, 0x8C000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lw", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsMfhi, 0x00000010,
+                 kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mfhi", "!0r", 2),
+    ENCODING_MAP(kMipsMflo, 0x00000012,
+                 kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mflo", "!0r", 2),
+    ENCODING_MAP(kMipsMove, 0x00000025, /* or using zero reg */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "move", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsMovz, 0x0000000a,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "movz", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsMul, 0x70000002,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsNop, 0x00000000,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "nop", "", 2),
+    ENCODING_MAP(kMipsNor, 0x00000027, /* used for "not" too */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "nor", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsOr, 0x00000025,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "or", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsOri, 0x34000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "ori", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsPref, 0xCC000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE2,
+                 "pref", "!0d,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsSb, 0xA0000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sb", "!0r,!1d(!2r)", 2),
+#if __mips_isa_rev>=2
+    ENCODING_MAP(kMipsSeb, 0x7c000420,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "seb", "!0r,!1r", 2),
+    ENCODING_MAP(kMipsSeh, 0x7c000620,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "seh", "!0r,!1r", 2),
+#endif
+    ENCODING_MAP(kMipsSh, 0xA4000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sh", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsSll, 0x00000000,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "sll", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSllv, 0x00000004,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sllv", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSlt, 0x0000002a,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "slt", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSlti, 0x28000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "slti", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSltu, 0x0000002b,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sltu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSra, 0x00000003,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "sra", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSrav, 0x00000007,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "srav", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSrl, 0x00000002,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "srl", "!0r,!1r,0x!2h(!2d)", 2),
+    ENCODING_MAP(kMipsSrlv, 0x00000006,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "srlv", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSubu, 0x00000023, /* used for "neg" too */
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "subu", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsSw, 0xAC000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sw", "!0r,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsSync, 0x0000000F,
+                 kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_UNARY_OP,
+                 "sync", "!0B", 2),
+    ENCODING_MAP(kMipsXor, 0x00000026,
+                 kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "xor", "!0r,!1r,!2r", 2),
+    ENCODING_MAP(kMipsXori, 0x38000000,
+                 kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+                 "xori", "!0r,!1r,0x!2h(!2d)", 2),
+#ifdef __mips_hard_float
+    ENCODING_MAP(kMipsFadds, 0x46000000,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "add.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFsubs, 0x46000001,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sub.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFmuls, 0x46000002,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFdivs, 0x46000003,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "div.s", "!0s,!1s,!2s", 2),
+    ENCODING_MAP(kMipsFaddd, 0x46200000,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "add.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFsubd, 0x46200001,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "sub.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFmuld, 0x46200002,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "mul.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFdivd, 0x46200003,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+                 "div.d", "!0S,!1S,!2S", 2),
+    ENCODING_MAP(kMipsFcvtsd, 0x46200020,
+                 kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.s.d", "!0s,!1S", 2),
+    ENCODING_MAP(kMipsFcvtsw, 0x46800020,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.s.w", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFcvtds, 0x46000021,
+                 kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.d.s", "!0S,!1s", 2),
+    ENCODING_MAP(kMipsFcvtdw, 0x46800021,
+                 kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.d.w", "!0S,!1s", 2),
+    ENCODING_MAP(kMipsFcvtws, 0x46000024,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.w.s", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFcvtwd, 0x46200024,
+                 kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "cvt.w.d", "!0s,!1S", 2),
+    ENCODING_MAP(kMipsFmovs, 0x46000006,
+                 kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov.s", "!0s,!1s", 2),
+    ENCODING_MAP(kMipsFmovd, 0x46200006,
+                 kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mov.d", "!0S,!1S", 2),
+    ENCODING_MAP(kMipsFlwc1, 0xC4000000,
+                 kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "lwc1", "!0s,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFldc1, 0xD4000000,
+                 kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+                 "ldc1", "!0S,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFswc1, 0xE4000000,
+                 kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "swc1", "!0s,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsFsdc1, 0xF4000000,
+                 kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+                 "sdc1", "!0S,!1d(!2r)", 2),
+    ENCODING_MAP(kMipsMfc1, 0x44000000,
+                 kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+                 "mfc1", "!0r,!1s", 2),
+    ENCODING_MAP(kMipsMtc1, 0x44800000,
+                 kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1,
+                 "mtc1", "!0r,!1s", 2),
+#endif
+    ENCODING_MAP(kMipsUndefined, 0x64000000,
+                 kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, NO_OPERAND,
+                 "undefined", "", 2),
+};
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+/* Write the numbers in the constant and class pool to the output stream */
+static void installLiteralPools(CompilationUnit *cUnit)
+{
+    int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+    /* Install number of class pointer literals */
+    *dataPtr++ = cUnit->numClassPointers;
+    MipsLIR *dataLIR = (MipsLIR *) cUnit->classPointerList;
+    while (dataLIR) {
+        /*
+         * Install the callsiteinfo pointers into the cells for now. They will
+         * be converted into real pointers in dvmJitInstallClassObjectPointers.
+         */
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+    dataLIR = (MipsLIR *) cUnit->literalList;
+    while (dataLIR) {
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+}
+
+/*
+ * Assemble the LIR into binary instruction format.  Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.  In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+static AssemblerStatus assembleInstructions(CompilationUnit *cUnit,
+                                            intptr_t startAddr)
+{
+    int *bufferAddr = (int *) cUnit->codeBuffer;
+    MipsLIR *lir;
+
+    for (lir = (MipsLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
+        if (lir->opcode < 0) {
+            continue;
+        }
+
+
+        if (lir->flags.isNop) {
+            continue;
+        }
+
+        if (lir->opcode == kMipsB || lir->opcode == kMipsBal) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Unconditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[0] = delta >> 2;
+        } else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Conditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[1] = delta >> 2;
+        } else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) {
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            int delta = target - pc;
+            if (delta & 0x3) {
+                ALOGE("PC-rel distance is not multiple of 4: %d", delta);
+                dvmAbort();
+            }
+            if (delta > 131068 || delta < -131069) {
+                ALOGE("Conditional branch distance out of range: %d", delta);
+                dvmAbort();
+            }
+            lir->operands[2] = delta >> 2;
+        } else if (lir->opcode == kMipsJal) {
+            intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
+            intptr_t target = lir->operands[0];
+            /* ensure PC-region branch can be used */
+            assert((curPC & 0xF0000000) == (target & 0xF0000000));
+            if (target & 0x3) {
+                ALOGE("Jump target is not multiple of 4: %d", target);
+                dvmAbort();
+            }
+            lir->operands[0] =  target >> 2;
+        } else if (lir->opcode == kMipsLahi) { /* load address hi (via lui) */
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t target = startAddr + targetLIR->generic.offset;
+            lir->operands[1] = target >> 16;
+        } else if (lir->opcode == kMipsLalo) { /* load address lo (via ori) */
+            MipsLIR *targetLIR = (MipsLIR *) lir->generic.target;
+            intptr_t target = startAddr + targetLIR->generic.offset;
+            lir->operands[2] = lir->operands[2] + target;
+        }
+
+
+        MipsEncodingMap *encoder = &EncodingMap[lir->opcode];
+        u4 bits = encoder->skeleton;
+        int i;
+        for (i = 0; i < 4; i++) {
+            u4 operand;
+            u4 value;
+            operand = lir->operands[i];
+            switch(encoder->fieldLoc[i].kind) {
+                case kFmtUnused:
+                    break;
+                case kFmtBitBlt:
+                    if (encoder->fieldLoc[i].start == 0 && encoder->fieldLoc[i].end == 31) {
+                        value = operand;
+                    } else {
+                        value = (operand << encoder->fieldLoc[i].start) &
+                                ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    }
+                    bits |= value;
+                    break;
+                case kFmtDfp: {
+                    assert(DOUBLEREG(operand));
+                    assert((operand & 0x1) == 0);
+                    value = ((operand & FP_REG_MASK) << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                }
+                case kFmtSfp:
+                    assert(SINGLEREG(operand));
+                    value = ((operand & FP_REG_MASK) << encoder->fieldLoc[i].start) &
+                            ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+                    bits |= value;
+                    break;
+                default:
+                    assert(0);
+            }
+        }
+        assert(encoder->size == 2);
+        *bufferAddr++ = bits;
+    }
+    return kSuccess;
+}
+
+static int assignLiteralOffsetCommon(LIR *lir, int offset)
+{
+    for (;lir != NULL; lir = lir->next) {
+        lir->offset = offset;
+        offset += 4;
+    }
+    return offset;
+}
+
+/* Determine the offset of each literal field */
+static int assignLiteralOffset(CompilationUnit *cUnit, int offset)
+{
+    /* Reserved for the size field of class pointer pool */
+    offset += 4;
+    offset = assignLiteralOffsetCommon(cUnit->classPointerList, offset);
+    offset = assignLiteralOffsetCommon(cUnit->literalList, offset);
+    return offset;
+}
+
+/*
+ * Translation layout in the code cache.  Note that the codeAddress pointer
+ * in JitTable will point directly to the code body (field codeAddress).  The
+ * chain cell offset codeAddress - 4, and the address of the trace profile
+ * counter is at codeAddress - 8.
+ *
+ *      +----------------------------+
+ *      | Trace Profile Counter addr |  -> 4 bytes (PROF_COUNTER_ADDR_SIZE)
+ *      +----------------------------+
+ *   +--| Offset to chain cell counts|  -> 4 bytes (CHAIN_CELL_OFFSET_SIZE)
+ *   |  +----------------------------+
+ *   |  | Trace profile code         |  <- entry point when profiling
+ *   |  .  -   -   -   -   -   -   - .
+ *   |  | Code body                  |  <- entry point when not profiling
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Chaining Cells             |  -> 16/20 bytes, 4 byte aligned
+ *   |  .                            .
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Gap for large switch stmt  |  -> # cases >= MAX_CHAINED_SWITCH_CASES
+ *   |  +----------------------------+
+ *   +->| Chaining cell counts       |  -> 8 bytes, chain cell counts by type
+ *      +----------------------------+
+ *      | Trace description          |  -> variable sized
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | # Class pointer pool size  |  -> 4 bytes
+ *      +----------------------------+
+ *      | Class pointer pool         |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | Literal pool               |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *
+ */
+
+#define PROF_COUNTER_ADDR_SIZE 4
+#define CHAIN_CELL_OFFSET_SIZE 4
+
+/*
+ * Utility functions to navigate various parts in a trace. If we change the
+ * layout/offset in the future, we just modify these functions and we don't need
+ * to propagate the changes to all the use cases.
+ */
+static inline char *getTraceBase(const JitEntry *p)
+{
+    return (char*)p->codeAddress -
+        (PROF_COUNTER_ADDR_SIZE + CHAIN_CELL_OFFSET_SIZE);
+}
+
+/* Handy function to retrieve the profile count */
+static inline JitTraceCounter_t getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return 0;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    return **p;
+}
+
+/* Handy function to reset the profile count */
+static inline void resetProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return;
+
+    JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+    **p = 0;
+}
+
+/* Get the pointer of the chain cell count */
+static inline ChainCellCounts* getChainCellCountsPointer(const char *base)
+{
+    /* 4 is the size of the profile count */
+    u4 *chainCellOffsetP = (u4 *) (base + PROF_COUNTER_ADDR_SIZE);
+    u4 chainCellOffset = *chainCellOffsetP;
+    return (ChainCellCounts *) ((char *) chainCellOffsetP + chainCellOffset);
+}
+
+/* Get the size of all chaining cells */
+static inline u4 getChainCellSize(const ChainCellCounts* pChainCellCounts)
+{
+    int cellSize = 0;
+    int i;
+
+    /* Get total count of chain cells */
+    for (i = 0; i < kChainingCellGap; i++) {
+        if (i != kChainingCellInvokePredicted) {
+            cellSize += pChainCellCounts->u.count[i] *
+                        (CHAIN_CELL_NORMAL_SIZE >> 2);
+        } else {
+            cellSize += pChainCellCounts->u.count[i] *
+                (CHAIN_CELL_PREDICTED_SIZE >> 2);
+        }
+    }
+    return cellSize;
+}
+
+/* Get the starting pointer of the trace description section */
+static JitTraceDescription* getTraceDescriptionPointer(const char *base)
+{
+    ChainCellCounts* pCellCounts = getChainCellCountsPointer(base);
+    return (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+}
+
+/* Get the size of a trace description */
+static int getTraceDescriptionSize(const JitTraceDescription *desc)
+{
+    int runCount;
+    /* Trace end is always of non-meta type (ie isCode == true) */
+    for (runCount = 0; ; runCount++) {
+        if (desc->trace[runCount].isCode &&
+            desc->trace[runCount].info.frag.runEnd)
+           break;
+    }
+    return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+#if defined(SIGNATURE_BREAKPOINT)
+/* Inspect the assembled instruction stream to find potential matches */
+static void matchSignatureBreakpoint(const CompilationUnit *cUnit,
+                                     unsigned int size)
+{
+    unsigned int i, j;
+    u4 *ptr = (u4 *) cUnit->codeBuffer;
+
+    for (i = 0; i < size - gDvmJit.signatureBreakpointSize + 1; i++) {
+        if (ptr[i] == gDvmJit.signatureBreakpoint[0]) {
+            for (j = 1; j < gDvmJit.signatureBreakpointSize; j++) {
+                if (ptr[i+j] != gDvmJit.signatureBreakpoint[j]) {
+                    break;
+                }
+            }
+            if (j == gDvmJit.signatureBreakpointSize) {
+                ALOGD("Signature match starting from offset %#x (%d words)",
+                     i*4, gDvmJit.signatureBreakpointSize);
+                int descSize = getTraceDescriptionSize(cUnit->traceDesc);
+                JitTraceDescription *newCopy =
+                    (JitTraceDescription *) malloc(descSize);
+                memcpy(newCopy, cUnit->traceDesc, descSize);
+                dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+                break;
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    MipsLIR *mipsLIR;
+    int offset = 0;
+    int i;
+    ChainCellCounts chainCellCounts;
+    int descSize = (cUnit->jitMode == kJitMethod) ?
+        0 : getTraceDescriptionSize(cUnit->traceDesc);
+    int chainingCellGap = 0;
+
+    info->instructionSet = cUnit->instructionSet;
+
+    /* Beginning offset needs to allow space for chain cell offset */
+    for (mipsLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         mipsLIR;
+         mipsLIR = NEXT_LIR(mipsLIR)) {
+        mipsLIR->generic.offset = offset;
+        if (mipsLIR->opcode >= 0 && !mipsLIR->flags.isNop) {
+            mipsLIR->flags.size = EncodingMap[mipsLIR->opcode].size * 2;
+            offset += mipsLIR->flags.size;
+        }
+        /* Pseudo opcodes don't consume space */
+    }
+
+    /* Const values have to be word aligned */
+    offset = (offset + 3) & ~3;
+
+    u4 chainCellOffset = offset;
+    MipsLIR *chainCellOffsetLIR = NULL;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /*
+         * Get the gap (# of u4) between the offset of chaining cell count and
+         * the bottom of real chaining cells. If the translation has chaining
+         * cells, the gap is guaranteed to be multiples of 4.
+         */
+        chainingCellGap = (offset - cUnit->chainingCellBottom->offset) >> 2;
+
+        /* Add space for chain cell counts & trace description */
+        chainCellOffsetLIR = (MipsLIR *) cUnit->chainCellOffsetLIR;
+        assert(chainCellOffsetLIR);
+        assert(chainCellOffset < 0x10000);
+        assert(chainCellOffsetLIR->opcode == kMips32BitData &&
+               chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+        /*
+         * Adjust the CHAIN_CELL_OFFSET_TAG LIR's offset to remove the
+         * space occupied by the pointer to the trace profiling counter.
+         */
+        chainCellOffsetLIR->operands[0] = chainCellOffset - 4;
+
+        offset += sizeof(chainCellCounts) + descSize;
+
+        assert((offset & 0x3) == 0);  /* Should still be word aligned */
+    }
+
+    /* Set up offsets for literals */
+    cUnit->dataOffset = offset;
+
+    /*
+     * Assign each class pointer/constant an offset from the beginning of the
+     * compilation unit.
+     */
+    offset = assignLiteralOffset(cUnit, offset);
+
+    cUnit->totalSize = offset;
+
+    if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+        gDvmJit.codeCacheFull = true;
+        info->discardResult = true;
+        return;
+    }
+
+    /* Allocate enough space for the code block */
+    cUnit->codeBuffer = (unsigned char *)dvmCompilerNew(chainCellOffset, true);
+    if (cUnit->codeBuffer == NULL) {
+        ALOGE("Code buffer allocation failure");
+        info->discardResult = true;
+        return;
+    }
+
+    /*
+     * Attempt to assemble the trace.  Note that assembleInstructions
+     * may rewrite the code sequence and request a retry.
+     */
+    cUnit->assemblerStatus = assembleInstructions(cUnit,
+          (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+    switch(cUnit->assemblerStatus) {
+        case kSuccess:
+            break;
+        case kRetryAll:
+            if (cUnit->assemblerRetries < MAX_ASSEMBLER_RETRIES) {
+                if (cUnit->jitMode != kJitMethod) {
+                    /* Restore pristine chain cell marker on retry */
+                    chainCellOffsetLIR->operands[0] = CHAIN_CELL_OFFSET_TAG;
+                }
+                return;
+            }
+            /* Too many retries - reset and try cutting the trace in half */
+            cUnit->assemblerRetries = 0;
+            cUnit->assemblerStatus = kRetryHalve;
+            return;
+        case kRetryHalve:
+            return;
+        default:
+             ALOGE("Unexpected assembler status: %d", cUnit->assemblerStatus);
+             dvmAbort();
+    }
+
+#if defined(SIGNATURE_BREAKPOINT)
+    if (info->discardResult == false && gDvmJit.signatureBreakpoint != NULL &&
+        chainCellOffset/4 >= gDvmJit.signatureBreakpointSize) {
+        matchSignatureBreakpoint(cUnit, chainCellOffset/4);
+    }
+#endif
+
+    /* Don't go all the way if the goal is just to get the verbose output */
+    if (info->discardResult) return;
+
+    /*
+     * The cache might disappear - acquire lock and check version
+     * Continue holding lock until translation cache update is complete.
+     * These actions are required here in the compiler thread because
+     * it is unaffected by suspend requests and doesn't know if a
+     * translation cache flush is in progress.
+     */
+    dvmLockMutex(&gDvmJit.compilerLock);
+    if (info->cacheVersion != gDvmJit.cacheVersion) {
+        /* Cache changed - discard current translation */
+        info->discardResult = true;
+        info->codeAddress = NULL;
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+        return;
+    }
+
+    cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    gDvmJit.codeCacheByteUsed += offset;
+
+    UNPROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Install the code block */
+    memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset);
+    gDvmJit.numCompilations++;
+
+    if (cUnit->jitMode != kJitMethod) {
+        /* Install the chaining cell counts */
+        for (i=0; i< kChainingCellGap; i++) {
+            chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+        }
+
+        /* Set the gap number in the chaining cell count structure */
+        chainCellCounts.u.count[kChainingCellGap] = chainingCellGap;
+
+        memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts,
+               sizeof(chainCellCounts));
+
+        /* Install the trace description */
+        memcpy((char*) cUnit->baseAddr + chainCellOffset +
+                       sizeof(chainCellCounts),
+               cUnit->traceDesc, descSize);
+    }
+
+    /* Write the literals directly into the code cache */
+    installLiteralPools(cUnit);
+
+    /* Flush dcache and invalidate the icache to maintain coherence */
+    dvmCompilerCacheFlush((long)cUnit->baseAddr,
+                          (long)((char *) cUnit->baseAddr + offset));
+
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* Translation cache update complete - release lock */
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    /* Record code entry point and instruction set */
+    info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+    /* transfer the size of the profiling code */
+    info->profileCodeSize = cUnit->profileCodeSize;
+}
+
+/*
+ * Returns the skeleton bit pattern associated with an opcode.  All
+ * variable fields are zeroed.
+ */
+static u4 getSkeleton(MipsOpCode op)
+{
+    return EncodingMap[op].skeleton;
+}
+
+static u4 assembleChainingBranch(int branchOffset, bool thumbTarget)
+{
+    return getSkeleton(kMipsJal) | ((branchOffset & 0x0FFFFFFF) >> 2);
+}
+
+/*
+ * Perform translation chain operation.
+ * For MIPS, we'll use a JAL instruction to generate an
+ * unconditional chaining branch of up to 256M. The JAL
+ * instruction also has a restriction that the jump target
+ * must be in the same 256M page as the JAL instruction's
+ * delay slot address.
+ * If the target is out of JAL's range, don't chain.
+ * If one or more threads is suspended, don't chain.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+    u4 newInst;
+
+    /*
+     * Only chain translations when there is no urge to ask all threads to
+     * suspend themselves via the interpreter.
+     */
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false) &&
+        ((((int) tgtAddr) & 0xF0000000) == (((int) branchAddr+4) & 0xF0000000))) {
+        gDvmJit.translationChains++;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: chaining 0x%x to 0x%x",
+                 (int) branchAddr, (int) tgtAddr & -2));
+
+        newInst = assembleChainingBranch((int) tgtAddr & -2, 0);
+
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        *branchAddr = newInst;
+        dvmCompilerCacheFlush((long)branchAddr, (long)branchAddr + 4);
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        gDvmJit.hasNewChain = true;
+    }
+
+    return tgtAddr;
+}
+
+#if !defined(WITH_SELF_VERIFICATION)
+/*
+ * Attempt to enqueue a work order to patch an inline cache for a predicted
+ * chaining cell for virtual/interface calls.
+ */
+static void inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
+                                    PredictedChainingCell *newContent)
+{
+    /*
+     * Make sure only one thread gets here since updating the cell (ie fast
+     * path and queueing the request (ie the queued path) have to be done
+     * in an atomic fashion.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    /* Fast path for uninitialized chaining cell */
+    if (cellAddr->clazz == NULL &&
+        cellAddr->branch == PREDICTED_CHAIN_BX_PAIR_INIT) {
+
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->method = newContent->method;
+        cellAddr->branch = newContent->branch;
+
+        /*
+         * The update order matters - make sure clazz is updated last since it
+         * will bring the uninitialized chaining cell to life.
+         */
+        android_atomic_release_store((int32_t)newContent->clazz,
+            (volatile int32_t *)(void*) &cellAddr->clazz);
+        dvmCompilerCacheFlush((long) cellAddr, (long) (cellAddr+1));
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchInit++;
+#endif
+    /* Check if this is a frequently missed clazz */
+    } else if (cellAddr->stagedClazz != newContent->clazz) {
+        /* Not proven to be frequent yet - build up the filter cache */
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->stagedClazz = newContent->clazz;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchRejected++;
+#endif
+    /*
+     * Different classes but same method implementation - it is safe to just
+     * patch the class value without the need to stop the world.
+     */
+    } else if (cellAddr->method == newContent->method) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->clazz = newContent->clazz;
+        /* No need to flush the cache here since the branch is not patched */
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchLockFree++;
+#endif
+    /*
+     * Cannot patch the chaining cell inline - queue it until the next safe
+     * point.
+     */
+    } else if (gDvmJit.compilerICPatchIndex < COMPILER_IC_PATCH_QUEUE_SIZE) {
+        int index = gDvmJit.compilerICPatchIndex++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+#endif
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Ask all other threads to stop before patching this chaining cell.
+ *      This is required because another thread may have passed the class check
+ *      but hasn't reached the chaining cell yet to follow the chain. If we
+ *      patch the content before halting the other thread, there could be a
+ *      small window for race conditions to happen that it may follow the new
+ *      but wrong chain to invoke a different method.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+    newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+    goto done;
+#else
+    PredictedChainingCell newCell;
+    int baseAddr, tgtAddr;
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        goto done;
+    }
+
+    tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+    baseAddr = (int) cell + 4;   // PC is cur_addr + 4
+
+    if ((baseAddr & 0xF0000000) != (tgtAddr & 0xF0000000)) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to distant target %s ignored",
+                 cell, method->name));
+        goto done;
+    }
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p to method %s%s delayed",
+                 cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    newCell.branch = assembleChainingBranch(tgtAddr, true);
+    newCell.delay_slot = getSkeleton(kMipsNop);
+    newCell.clazz = clazz;
+    newCell.method = method;
+    newCell.stagedClazz = NULL;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+#endif
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Patch the inline cache content based on the content passed from the work
+ * order.
+ */
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGD("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                 "patched",
+                 cellAddr,
+                 cellAddr->clazz->descriptor,
+                 cellContent->clazz->descriptor,
+                 cellContent->method->name));
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    /* Then synchronize the I/D cache */
+    dvmCompilerCacheFlush((long) minAddr, (long) (maxAddr+1));
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    gDvmJit.compilerICPatchIndex = 0;
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+
+/*
+ * Unchain a trace given the starting address of the translation
+ * in the code cache.  Refer to the diagram in dvmCompilerAssembleLIR.
+ * Returns the address following the last cell unchained.  Note that
+ * the incoming codeAddr is a thumb code address, and therefore has
+ * the low bit set.
+ */
+static u4* unchainSingle(JitEntry *trace)
+{
+    const char *base = getTraceBase(trace);
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    u4* pChainCells;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+
+    if (cellSize == 0)
+        return (u4 *) pChainCellCounts;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+                  pChainCellCounts->u.count[kChainingCellGap];
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        int elemSize = CHAIN_CELL_NORMAL_SIZE >> 2;  /* In 32-bit words */
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = CHAIN_CELL_PREDICTED_SIZE >> 2;
+        }
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            int targetOffset;
+            switch(i) {
+                case kChainingCellNormal:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpNormal);
+                    break;
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpTraceSelect);
+                    break;
+                case kChainingCellInvokePredicted:
+                    targetOffset = 0;
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+#if defined(WITH_SELF_VERIFICATION)
+                case kChainingCellBackwardBranch:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpBackwardBranch);
+                    break;
+#else
+                case kChainingCellBackwardBranch:
+                    targetOffset = offsetof(Thread,
+                          jitToInterpEntries.dvmJitToInterpNormal);
+                    break;
+#endif
+                default:
+                    targetOffset = 0; // make gcc happy
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGD("Jit Runtime: unchaining %#x", (int)pChainCells));
+            /*
+             * Code sequence for a chaining cell is:
+             *     lw   a0, offset(rSELF)
+             *     jalr ra, a0
+             */
+            if (i != kChainingCellInvokePredicted) {
+                *pChainCells = getSkeleton(kMipsLw) | (r_A0 << 16) |
+                               targetOffset | (rSELF << 21);
+                *(pChainCells+1) = getSkeleton(kMipsJalr) | (r_RA << 11) |
+                                   (r_A0 << 21);
+            }
+            pChainCells += elemSize;  /* Advance by a fixed number of words */
+        }
+    }
+    return pChainCells;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    u4* lowAddress = NULL;
+    u4* highAddress = NULL;
+    unsigned int i;
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(ALOGD("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress &&
+                (gDvmJit.pJitEntryTable[i].codeAddress !=
+                 dvmCompilerGetInterpretTemplate())) {
+                u4* lastAddress;
+                lastAddress = unchainSingle(&gDvmJit.pJitEntryTable[i]);
+                if (lowAddress == NULL ||
+                      (u4*)gDvmJit.pJitEntryTable[i].codeAddress < lowAddress)
+                    lowAddress = (u4*)gDvmJit.pJitEntryTable[i].codeAddress;
+                if (lastAddress > highAddress)
+                    highAddress = lastAddress;
+            }
+        }
+
+        if (lowAddress && highAddress)
+            dvmCompilerCacheFlush((long)lowAddress, (long)highAddress);
+
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        gDvmJit.translationChains = 0;
+    }
+    gDvmJit.hasNewChain = false;
+}
+
+typedef struct jitProfileAddrToLine {
+    u4 lineNum;
+    u4 bytecodeOffset;
+} jitProfileAddrToLine;
+
+
+/* Callback function to track the bytecode offset/line number relationiship */
+static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum)
+{
+    jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt;
+
+    /* Best match so far for this offset */
+    if (addrToLine->bytecodeOffset >= bytecodeOffset) {
+        addrToLine->lineNum = lineNum;
+    }
+    return 0;
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+                            unsigned long sum)
+{
+    int idx;
+
+    if (p->codeAddress == NULL) {
+        if (!silent)
+            ALOGD("TRACEPROFILE NULL");
+        return 0;
+    }
+    if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+        if (!silent)
+            ALOGD("TRACEPROFILE INTERPRET_ONLY");
+        return 0;
+    }
+
+    JitTraceCounter_t count = getProfileCount(p);
+    if (reset) {
+        resetProfileCount(p);
+    }
+    if (silent) {
+        return count;
+    }
+    JitTraceDescription *desc = getTraceDescriptionPointer(getTraceBase(p));
+    const Method *method = desc->method;
+    char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+    jitProfileAddrToLine addrToLine = {0, desc->trace[0].info.frag.startOffset};
+
+    /*
+     * We may end up decoding the debug information for the same method
+     * multiple times, but the tradeoff is we don't need to allocate extra
+     * space to store the addr/line mapping. Since this is a debugging feature
+     * and done infrequently so the slower but simpler mechanism should work
+     * just fine.
+     */
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+                       dvmGetMethodCode(method),
+                       method->clazz->descriptor,
+                       method->prototype.protoIdx,
+                       method->accessFlags,
+                       addrToLineCb, NULL, &addrToLine);
+
+    ALOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+         (int) getTraceBase(p),
+         count,
+         ((float ) count) / sum * 100.0,
+         desc->trace[0].info.frag.startOffset,
+         desc->trace[0].info.frag.numInsts,
+         addrToLine.lineNum,
+         method->clazz->descriptor, method->name, methodDesc);
+    free(methodDesc);
+
+    /* Find the last fragment (ie runEnd is set) */
+    for (idx = 0;
+         desc->trace[idx].isCode && !desc->trace[idx].info.frag.runEnd;
+         idx++) {
+    }
+
+    /*
+     * runEnd must comes with a JitCodeDesc frag. If isCode is false it must
+     * be a meta info field (only used by callsite info for now).
+     */
+    if (!desc->trace[idx].isCode) {
+        const Method *method = (const Method *)
+            desc->trace[idx+JIT_TRACE_CUR_METHOD-1].info.meta;
+        char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+        /* Print the callee info in the trace */
+        ALOGD("    -> %s%s;%s", method->clazz->descriptor, method->name,
+             methodDesc);
+    }
+
+    return count;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry)
+{
+    const JitEntry *jitEntry = knownEntry ? knownEntry
+                                          : dvmJitFindEntry(pc, false);
+    if ((jitEntry == NULL) || (jitEntry->codeAddress == 0))
+        return NULL;
+
+    JitTraceDescription *desc =
+        getTraceDescriptionPointer(getTraceBase(jitEntry));
+
+    /* Now make a copy and return */
+    int descSize = getTraceDescriptionSize(desc);
+    JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+    memcpy(newCopy, desc, descSize);
+    return newCopy;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long sum = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Analyze the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            sum += dumpTraceProfile(&sortedEntries[i],
+                                       true /* silent */,
+                                       false /* reset */,
+                                       0);
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    if (sum == 0) {
+        sum = 1;
+    }
+
+    ALOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+
+    /* Dump the sorted entries. The count of each trace will be reset to 0. */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            dumpTraceProfile(&sortedEntries[i],
+                             false /* silent */,
+                             true /* reset */,
+                             sum);
+        }
+    }
+
+    for (i=0; i < gDvmJit.jitTableSize && i < 10; i++) {
+        /* Stip interpreter stubs */
+        if (sortedEntries[i].codeAddress == dvmCompilerGetInterpretTemplate()) {
+            continue;
+        }
+        JitTraceDescription* desc =
+            dvmCopyTraceDescriptor(NULL, &sortedEntries[i]);
+        if (desc) {
+            dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+                                   kWorkOrderTraceDebug, desc);
+        }
+    }
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+static void findClassPointersSingleTrace(char *base, void (*callback)(void *))
+{
+    unsigned int chainTypeIdx, chainIdx;
+    ChainCellCounts *pChainCellCounts = getChainCellCountsPointer(base);
+    int cellSize = getChainCellSize(pChainCellCounts);
+    /* Scan the chaining cells */
+    if (cellSize) {
+        /* Locate the beginning of the chain cell region */
+        u4 *pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+            pChainCellCounts->u.count[kChainingCellGap];
+        /* The cells are sorted in order - walk through them */
+        for (chainTypeIdx = 0; chainTypeIdx < kChainingCellGap;
+             chainTypeIdx++) {
+            if (chainTypeIdx != kChainingCellInvokePredicted) {
+                /* In 32-bit words */
+                pChainCells += (CHAIN_CELL_NORMAL_SIZE >> 2) *
+                    pChainCellCounts->u.count[chainTypeIdx];
+                continue;
+            }
+            for (chainIdx = 0;
+                 chainIdx < pChainCellCounts->u.count[chainTypeIdx];
+                 chainIdx++) {
+                PredictedChainingCell *cell =
+                    (PredictedChainingCell *) pChainCells;
+                /*
+                 * Report the cell if it contains a sane class
+                 * pointer.
+                 */
+                if (cell->clazz != NULL &&
+                    cell->clazz !=
+                      (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ) {
+                    callback(&cell->clazz);
+                }
+                pChainCells += CHAIN_CELL_PREDICTED_SIZE >> 2;
+            }
+        }
+    }
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    int *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *classPointerP++;
+    for (; numClassPointers; numClassPointers--, classPointerP++) {
+        callback(classPointerP);
+    }
+}
+
+/*
+ * Scan class pointers in each translation and pass its address to the callback
+ * function. Currently such a pointers can be found in the pointer pool and the
+ * clazz field in the predicted chaining cells.
+ */
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    /* Handle the inflight compilation first */
+    if (gDvmJit.inflightBaseAddr)
+        findClassPointersSingleTrace((char *) gDvmJit.inflightBaseAddr,
+                                     callback);
+
+    if (gDvmJit.pJitEntryTable != NULL) {
+        unsigned int traceIdx;
+        dvmLockMutex(&gDvmJit.tableLock);
+        for (traceIdx = 0; traceIdx < gDvmJit.jitTableSize; traceIdx++) {
+            const JitEntry *entry = &gDvmJit.pJitEntryTable[traceIdx];
+            if (entry->dPC &&
+                !entry->u.info.isMethodEntry &&
+                entry->codeAddress &&
+                (entry->codeAddress != dvmCompilerGetInterpretTemplate())) {
+                char *base = getTraceBase(entry);
+                findClassPointersSingleTrace(base, callback);
+            }
+        }
+        dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+}
+
+/*
+ * Provide the final touch on the class object pointer pool to install the
+ * actual pointers. The thread has to be in the running state.
+ */
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+    char *base = codeAddress - cUnit->headerSize;
+
+    /* Scan the class pointer pool */
+    JitTraceDescription *desc = getTraceDescriptionPointer(base);
+    int descSize = getTraceDescriptionSize(desc);
+    intptr_t *classPointerP = (int *) ((char *) desc + descSize);
+    int numClassPointers = *(int *)classPointerP++;
+    intptr_t *startClassPointerP = classPointerP;
+
+    /*
+     * Change the thread state to VM_RUNNING so that GC won't be happening
+     * when the assembler looks up the class pointers. May suspend the current
+     * thread if there is a pending request before the state is actually
+     * changed to RUNNING.
+     */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_RUNNING);
+
+    /*
+     * Unprotecting the code cache will need to acquire the code cache
+     * protection lock first. Doing so after the state change may increase the
+     * time spent in the RUNNING state (which may delay the next GC request
+     * should there be contention on codeCacheProtectionLock). In practice
+     * this is probably not going to happen often since a GC is just served.
+     * More importantly, acquiring the lock before the state change will
+     * cause deadlock (b/4192964).
+     */
+    UNPROTECT_CODE_CACHE(startClassPointerP,
+                         numClassPointers * sizeof(intptr_t));
+#if defined(WITH_JIT_TUNING)
+    u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+    for (;numClassPointers; numClassPointers--) {
+        CallsiteInfo *callsiteInfo = (CallsiteInfo *) *classPointerP;
+        ClassObject *clazz = dvmFindClassNoInit(
+            callsiteInfo->classDescriptor, callsiteInfo->classLoader);
+        assert(!strcmp(clazz->descriptor, callsiteInfo->classDescriptor));
+        *classPointerP++ = (intptr_t) clazz;
+    }
+
+    /*
+     * Register the base address so that if GC kicks in after the thread state
+     * has been changed to VMWAIT and before the compiled code is registered
+     * in the JIT table, its content can be patched if class objects are
+     * moved.
+     */
+    gDvmJit.inflightBaseAddr = base;
+
+#if defined(WITH_JIT_TUNING)
+    u8 blockTime = dvmGetRelativeTimeUsec() - startTime;
+    gDvmJit.compilerThreadBlockGCTime += blockTime;
+    if (blockTime > gDvmJit.maxCompilerThreadBlockGCTime)
+        gDvmJit.maxCompilerThreadBlockGCTime = blockTime;
+    gDvmJit.numCompilerThreadBlockGC++;
+#endif
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(startClassPointerP, numClassPointers * sizeof(intptr_t));
+
+    /* Change the thread state back to VMWAIT */
+    dvmChangeStatus(gDvmJit.compilerThread, THREAD_VMWAIT);
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * The following are used to keep compiled loads and stores from modifying
+ * memory during self verification mode.
+ *
+ * Stores do not modify memory. Instead, the address and value pair are stored
+ * into heapSpace. Addresses within heapSpace are unique. For accesses smaller
+ * than a word, the word containing the address is loaded first before being
+ * updated.
+ *
+ * Loads check heapSpace first and return data from there if an entry exists.
+ * Otherwise, data is loaded from memory as usual.
+ */
+
+/* Used to specify sizes of memory operations */
+enum {
+    kSVByte,
+    kSVSignedByte,
+    kSVHalfword,
+    kSVSignedHalfword,
+    kSVWord,
+    kSVDoubleword,
+    kSVVariable,
+};
+
+/* Load the value of a decoded register from the stack */
+static int selfVerificationMemRegLoad(int* sp, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    return *(sp + reg);
+}
+
+/* Load the value of a decoded doubleword register from the stack */
+static s8 selfVerificationMemRegLoadDouble(int* sp, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    return *((s8*)(sp + reg));
+}
+
+/* Store the value of a decoded register out to the stack */
+static void selfVerificationMemRegStore(int* sp, int data, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    *(sp + reg) = data;
+}
+
+/* Store the value of a decoded doubleword register out to the stack */
+static void selfVerificationMemRegStoreDouble(int* sp, s8 data, int reg)
+{
+assert(0); /* MIPSTODO retarg func */
+    *((s8*)(sp + reg)) = data;
+}
+
+/*
+ * Load the specified size of data from the specified address, checking
+ * heapSpace first if Self Verification mode wrote to it previously, and
+ * falling back to actual memory otherwise.
+ */
+static int selfVerificationLoad(int addr, int size)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int data;
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) {
+            addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+            break;
+        }
+    }
+
+    switch (size) {
+        case kSVByte:
+            data = *((u1*) addr);
+            break;
+        case kSVSignedByte:
+            data = *((s1*) addr);
+            break;
+        case kSVHalfword:
+            data = *((u2*) addr);
+            break;
+        case kSVSignedHalfword:
+            data = *((s2*) addr);
+            break;
+        case kSVWord:
+            data = *((u4*) addr);
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+            data = 0;
+            dvmAbort();
+    }
+
+    //ALOGD("*** HEAP LOAD: Addr: %#x Data: %#x Size: %d", addr, data, size);
+    return data;
+}
+
+/* Like selfVerificationLoad, but specifically for doublewords */
+static s8 selfVerificationLoadDoubleword(int addr)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    ShadowHeap* heapSpacePtr;
+
+    int addr2 = addr+4;
+    unsigned int data = *((unsigned int*) addr);
+    unsigned int data2 = *((unsigned int*) addr2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            data = heapSpacePtr->data;
+        } else if (heapSpacePtr->addr == addr2) {
+            data2 = heapSpacePtr->data;
+        }
+    }
+
+    //ALOGD("*** HEAP LOAD DOUBLEWORD: Addr: %#x Data: %#x Data2: %#x",
+    //    addr, data, data2);
+    return (((s8) data2) << 32) | data;
+}
+
+/*
+ * Handles a store of a specified size of data to a specified address.
+ * This gets logged as an addr/data pair in heapSpace instead of modifying
+ * memory.  Addresses in heapSpace are unique, and accesses smaller than a
+ * word pull the entire word from memory first before updating.
+ */
+static void selfVerificationStore(int addr, int data, int size)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int maskedAddr = addr & 0xFFFFFFFC;
+    int alignment = addr & 0x3;
+
+    //ALOGD("*** HEAP STORE: Addr: %#x Data: %#x Size: %d", addr, data, size);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == maskedAddr) break;
+    }
+
+    if (heapSpacePtr == shadowSpace->heapSpaceTail) {
+        heapSpacePtr->addr = maskedAddr;
+        heapSpacePtr->data = *((unsigned int*) maskedAddr);
+        shadowSpace->heapSpaceTail++;
+    }
+
+    addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+    switch (size) {
+        case kSVByte:
+            *((u1*) addr) = data;
+            break;
+        case kSVSignedByte:
+            *((s1*) addr) = data;
+            break;
+        case kSVHalfword:
+            *((u2*) addr) = data;
+            break;
+        case kSVSignedHalfword:
+            *((s2*) addr) = data;
+            break;
+        case kSVWord:
+            *((u4*) addr) = data;
+            break;
+        default:
+            ALOGE("*** ERROR: BAD SIZE IN selfVerificationSave: %d", size);
+            dvmAbort();
+    }
+}
+
+/* Like selfVerificationStore, but specifically for doublewords */
+static void selfVerificationStoreDoubleword(int addr, s8 double_data)
+{
+assert(0); /* MIPSTODO retarg func */
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    ShadowHeap *heapSpacePtr;
+
+    int addr2 = addr+4;
+    int data = double_data;
+    int data2 = double_data >> 32;
+    bool store1 = false, store2 = false;
+
+    //ALOGD("*** HEAP STORE DOUBLEWORD: Addr: %#x Data: %#x, Data2: %#x",
+    //    addr, data, data2);
+
+    for (heapSpacePtr = shadowSpace->heapSpace;
+         heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+        if (heapSpacePtr->addr == addr) {
+            heapSpacePtr->data = data;
+            store1 = true;
+        } else if (heapSpacePtr->addr == addr2) {
+            heapSpacePtr->data = data2;
+            store2 = true;
+        }
+    }
+
+    if (!store1) {
+        shadowSpace->heapSpaceTail->addr = addr;
+        shadowSpace->heapSpaceTail->data = data;
+        shadowSpace->heapSpaceTail++;
+    }
+    if (!store2) {
+        shadowSpace->heapSpaceTail->addr = addr2;
+        shadowSpace->heapSpaceTail->data = data2;
+        shadowSpace->heapSpaceTail++;
+    }
+}
+
+/*
+ * Decodes the memory instruction at the address specified in the link
+ * register. All registers (r0-r12,lr) and fp registers (d0-d15) are stored
+ * consecutively on the stack beginning at the specified stack pointer.
+ * Calls the proper Self Verification handler for the memory instruction and
+ * updates the link register to point past the decoded memory instruction.
+ */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp)
+{
+assert(0); /* MIPSTODO retarg func */
+    enum {
+        kMemOpLdrPcRel = 0x09, // ldr(3)  [01001] rd[10..8] imm_8[7..0]
+        kMemOpRRR      = 0x0A, // Full opcode is 7 bits
+        kMemOp2Single  = 0x0A, // Used for Vstrs and Vldrs
+        kMemOpRRR2     = 0x0B, // Full opcode is 7 bits
+        kMemOp2Double  = 0x0B, // Used for Vstrd and Vldrd
+        kMemOpStrRRI5  = 0x0C, // str(1)  [01100] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRI5  = 0x0D, // ldr(1)  [01101] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRI5 = 0x0E, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRI5 = 0x0F, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRI5 = 0x10, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRI5 = 0x11, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]
+        kMemOpLdrSpRel = 0x13, // ldr(4)  [10011] rd[10..8] imm_8[7..0]
+        kMemOpStmia    = 0x18, // stmia   [11000] rn[10..8] reglist [7..0]
+        kMemOpLdmia    = 0x19, // ldmia   [11001] rn[10..8] reglist [7..0]
+        kMemOpStrRRR   = 0x28, // str(2)  [0101000] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrhRRR  = 0x29, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpStrbRRR  = 0x2A, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrsbRRR = 0x2B, // ldrsb   [0101011] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrRRR   = 0x2C, // ldr(2)  [0101100] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrhRRR  = 0x2D, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrbRRR  = 0x2E, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]
+        kMemOpLdrshRRR = 0x2F, // ldrsh   [0101111] rm[8..6] rn[5..3] rd[2..0]
+        kMemOp2Stmia   = 0xE88, // stmia  [111010001000[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia   = 0xE89, // ldmia  [111010001001[ rn[19..16] mask[15..0]
+        kMemOp2Stmia2  = 0xE8A, // stmia  [111010001010[ rn[19..16] mask[15..0]
+        kMemOp2Ldmia2  = 0xE8B, // ldmia  [111010001011[ rn[19..16] mask[15..0]
+        kMemOp2Vstr    = 0xED8, // Used for Vstrs and Vstrd
+        kMemOp2Vldr    = 0xED9, // Used for Vldrs and Vldrd
+        kMemOp2Vstr2   = 0xEDC, // Used for Vstrs and Vstrd
+        kMemOp2Vldr2   = 0xEDD, // Used for Vstrs and Vstrd
+        kMemOp2StrbRRR = 0xF80, /* str rt,[rn,rm,LSL #imm] [111110000000]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrbRRR = 0xF81, /* ldrb rt,[rn,rm,LSL #imm] [111110000001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrhRRR = 0xF82, /* str rt,[rn,rm,LSL #imm] [111110000010]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrhRRR = 0xF83, /* ldrh rt,[rn,rm,LSL #imm] [111110000011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrRRR  = 0xF84, /* str rt,[rn,rm,LSL #imm] [111110000100]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrRRR  = 0xF85, /* ldr rt,[rn,rm,LSL #imm] [111110000101]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2StrbRRI12 = 0xF88, /* strb rt,[rn,#imm12] [111110001000]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrbRRI12 = 0xF89, /* ldrb rt,[rn,#imm12] [111110001001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrhRRI12 = 0xF8A, /* strh rt,[rn,#imm12] [111110001010]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrhRRI12 = 0xF8B, /* ldrh rt,[rn,#imm12] [111110001011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2StrRRI12 = 0xF8C, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrRRI12 = 0xF8D, /* ldr(Imm,T3) rd,[rn,#imm12] [111110001101]
+                                       rn[19..16] rt[15..12] imm12[11..0] */
+        kMemOp2LdrsbRRR = 0xF91, /* ldrsb rt,[rn,rm,LSL #imm] [111110010001]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrshRRR = 0xF93, /* ldrsh rt,[rn,rm,LSL #imm] [111110010011]
+                                rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+        kMemOp2LdrsbRRI12 = 0xF99, /* ldrsb rt,[rn,#imm12] [111110011001]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2LdrshRRI12 = 0xF9B, /* ldrsh rt,[rn,#imm12] [111110011011]
+                                       rt[15..12] rn[19..16] imm12[11..0] */
+        kMemOp2        = 0xE000, // top 3 bits set indicates Thumb2
+    };
+
+    int addr, offset, data;
+    long long double_data;
+    int size = kSVWord;
+    bool store = false;
+    unsigned int *lr_masked = (unsigned int *) (lr & 0xFFFFFFFE);
+    unsigned int insn = *lr_masked;
+
+    int old_lr;
+    old_lr = selfVerificationMemRegLoad(sp, 13);
+
+    if ((insn & kMemOp2) == kMemOp2) {
+        insn = (insn << 16) | (insn >> 16);
+        //ALOGD("*** THUMB2 - Addr: %#x Insn: %#x", lr, insn);
+
+        int opcode12 = (insn >> 20) & 0xFFF;
+        int opcode6 = (insn >> 6) & 0x3F;
+        int opcode4 = (insn >> 8) & 0xF;
+        int imm2 = (insn >> 4) & 0x3;
+        int imm8 = insn & 0xFF;
+        int imm12 = insn & 0xFFF;
+        int rd = (insn >> 12) & 0xF;
+        int rm = insn & 0xF;
+        int rn = (insn >> 16) & 0xF;
+        int rt = (insn >> 12) & 0xF;
+        bool wBack = true;
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+4, 13);
+
+        // Determine whether the mem op is a store or load
+        switch (opcode12) {
+            case kMemOp2Stmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2StrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2StrRRI12:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode12) {
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+                size = kSVByte;
+                break;
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrsbRRI12:
+                size = kSVSignedByte;
+                break;
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+                size = kSVHalfword;
+                break;
+            case kMemOp2LdrshRRR:
+            case kMemOp2LdrshRRI12:
+                size = kSVSignedHalfword;
+                break;
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                if (opcode4 == kMemOp2Double) size = kSVDoubleword;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode12) {
+            case kMemOp2Vstr:
+            case kMemOp2Vstr2:
+            case kMemOp2Vldr:
+            case kMemOp2Vldr2:
+                offset = imm8 << 2;
+                if (opcode4 == kMemOp2Single) {
+                    rt = rd << 1;
+                    if (insn & 0x400000) rt |= 0x1;
+                } else if (opcode4 == kMemOp2Double) {
+                    if (insn & 0x400000) rt |= 0x10;
+                    rt = rt << 1;
+                } else {
+                    ALOGE("*** ERROR: UNRECOGNIZED VECTOR MEM OP: %x", opcode4);
+                    dvmAbort();
+                }
+                rt += 14;
+                break;
+            case kMemOp2StrbRRR:
+            case kMemOp2LdrbRRR:
+            case kMemOp2StrhRRR:
+            case kMemOp2LdrhRRR:
+            case kMemOp2StrRRR:
+            case kMemOp2LdrRRR:
+            case kMemOp2LdrsbRRR:
+            case kMemOp2LdrshRRR:
+                offset = selfVerificationMemRegLoad(sp, rm) << imm2;
+                break;
+            case kMemOp2StrbRRI12:
+            case kMemOp2LdrbRRI12:
+            case kMemOp2StrhRRI12:
+            case kMemOp2LdrhRRI12:
+            case kMemOp2StrRRI12:
+            case kMemOp2LdrRRI12:
+            case kMemOp2LdrsbRRI12:
+            case kMemOp2LdrshRRI12:
+                offset = imm12;
+                break;
+            case kMemOp2Stmia:
+            case kMemOp2Ldmia:
+                wBack = false;
+            case kMemOp2Stmia2:
+            case kMemOp2Ldmia2:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 STMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationMemRegLoadDouble(sp, rt);
+                selfVerificationStoreDoubleword(addr+offset, double_data);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                ALOGD("*** THUMB2 LDMIA CURRENTLY UNUSED (AND UNTESTED)");
+                int i;
+                int regList = insn & 0xFFFF;
+                for (i = 0; i < 16; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+            } else if (size == kSVDoubleword) {
+                double_data = selfVerificationLoadDoubleword(addr+offset);
+                selfVerificationMemRegStoreDouble(sp, double_data, rt);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    } else {
+        //ALOGD("*** THUMB - Addr: %#x Insn: %#x", lr, insn);
+
+        // Update the link register
+        selfVerificationMemRegStore(sp, old_lr+2, 13);
+
+        int opcode5 = (insn >> 11) & 0x1F;
+        int opcode7 = (insn >> 9) & 0x7F;
+        int imm = (insn >> 6) & 0x1F;
+        int rd = (insn >> 8) & 0x7;
+        int rm = (insn >> 6) & 0x7;
+        int rn = (insn >> 3) & 0x7;
+        int rt = insn & 0x7;
+
+        // Determine whether the mem op is a store or load
+        switch (opcode5) {
+            case kMemOpRRR:
+                switch (opcode7) {
+                    case kMemOpStrRRR:
+                    case kMemOpStrhRRR:
+                    case kMemOpStrbRRR:
+                        store = true;
+                }
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpStrbRRI5:
+            case kMemOpStrhRRI5:
+            case kMemOpStmia:
+                store = true;
+        }
+
+        // Determine the size of the mem access
+        switch (opcode5) {
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                switch (opcode7) {
+                    case kMemOpStrbRRR:
+                    case kMemOpLdrbRRR:
+                        size = kSVByte;
+                        break;
+                    case kMemOpLdrsbRRR:
+                        size = kSVSignedByte;
+                        break;
+                    case kMemOpStrhRRR:
+                    case kMemOpLdrhRRR:
+                        size = kSVHalfword;
+                        break;
+                    case kMemOpLdrshRRR:
+                        size = kSVSignedHalfword;
+                        break;
+                }
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                size = kSVByte;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                size = kSVHalfword;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                size = kSVVariable;
+                break;
+        }
+
+        // Load the value of the address
+        if (opcode5 == kMemOpLdrPcRel)
+            addr = selfVerificationMemRegLoad(sp, 4);
+        else if (opcode5 == kMemOpStmia || opcode5 == kMemOpLdmia)
+            addr = selfVerificationMemRegLoad(sp, rd);
+        else
+            addr = selfVerificationMemRegLoad(sp, rn);
+
+        // Figure out the offset
+        switch (opcode5) {
+            case kMemOpLdrPcRel:
+                offset = (insn & 0xFF) << 2;
+                rt = rd;
+                break;
+            case kMemOpRRR:
+            case kMemOpRRR2:
+                offset = selfVerificationMemRegLoad(sp, rm);
+                break;
+            case kMemOpStrRRI5:
+            case kMemOpLdrRRI5:
+                offset = imm << 2;
+                break;
+            case kMemOpStrhRRI5:
+            case kMemOpLdrhRRI5:
+                offset = imm << 1;
+                break;
+            case kMemOpStrbRRI5:
+            case kMemOpLdrbRRI5:
+                offset = imm;
+                break;
+            case kMemOpStmia:
+            case kMemOpLdmia:
+                offset = 0;
+                break;
+            default:
+                ALOGE("*** ERROR: UNRECOGNIZED THUMB MEM OP: %x", opcode5);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        data = selfVerificationMemRegLoad(sp, i);
+                        selfVerificationStore(addr, data, kSVWord);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationMemRegLoad(sp, rt);
+                selfVerificationStore(addr+offset, data, size);
+            }
+        } else {
+            if (size == kSVVariable) {
+                bool wBack = true;
+                int i;
+                int regList = insn & 0xFF;
+                for (i = 0; i < 8; i++) {
+                    if (regList & 0x1) {
+                        if (i == rd) wBack = false;
+                        data = selfVerificationLoad(addr, kSVWord);
+                        selfVerificationMemRegStore(sp, data, i);
+                        addr += 4;
+                    }
+                    regList = regList >> 1;
+                }
+                if (wBack) selfVerificationMemRegStore(sp, addr, rd);
+            } else {
+                data = selfVerificationLoad(addr+offset, size);
+                selfVerificationMemRegStore(sp, data, rt);
+            }
+        }
+    }
+}
+#endif
diff --git a/vm/compiler/codegen/mips/CalloutHelper.h b/vm/compiler/codegen/mips/CalloutHelper.h
new file mode 100644
index 0000000..8534361
--- /dev/null
+++ b/vm/compiler/codegen/mips/CalloutHelper.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
+
+#include "Dalvik.h"
+
+/*
+ * Declare/comment prototypes of all native callout functions invoked by the
+ * JIT'ed code here and use the LOAD_FUNC_ADDR macro to load the address into
+ * a register. In this way we have a centralized place to find out all native
+ * helper functions and we can grep for LOAD_FUNC_ADDR to find out all the
+ * callsites.
+ */
+
+/* Load a statically compiled function address as a constant */
+#define LOAD_FUNC_ADDR(cUnit, reg, addr) loadConstant(cUnit, reg, addr)
+
+/* Conversions */
+extern "C" float __floatsisf(int op1);             // OP_INT_TO_FLOAT
+extern "C" int __fixsfsi(float op1);               // OP_FLOAT_TO_INT
+extern "C" float __truncdfsf2(double op1);         // OP_DOUBLE_TO_FLOAT
+extern "C" double __extendsfdf2(float op1);        // OP_FLOAT_TO_DOUBLE
+extern "C" double __floatsidf(int op1);            // OP_INT_TO_DOUBLE
+extern "C" int __fixdfsi(double op1);              // OP_DOUBLE_TO_INT
+extern "C" float __floatdisf(long long op1);       // OP_LONG_TO_FLOAT
+extern "C" double __floatdidf(long long op1);      // OP_LONG_TO_DOUBLE
+extern "C" long long __fixsfdi(float op1);         // OP_FLOAT_TO_LONG
+extern "C" long long __fixdfdi(double op1);        // OP_DOUBLE_TO_LONG
+
+/* Single-precision FP arithmetics */
+extern "C" float __addsf3(float a, float b);   // OP_ADD_FLOAT[_2ADDR]
+extern "C" float __subsf3(float a, float b);   // OP_SUB_FLOAT[_2ADDR]
+extern "C" float __divsf3(float a, float b);   // OP_DIV_FLOAT[_2ADDR]
+extern "C" float __mulsf3(float a, float b);   // OP_MUL_FLOAT[_2ADDR]
+extern "C" float fmodf(float a, float b);          // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+extern "C" double __adddf3(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+extern "C" double __subdf3(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+extern "C" double __divdf3(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+extern "C" double __muldf3(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+extern "C" double fmod(double a, double b);         // OP_REM_DOUBLE[_2ADDR]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+extern "C" long long __divdi3(long long op1, long long op2);
+extern "C" long long __moddi3(long long op1, long long op2);
+
+/* Originally declared in Sync.h */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj); //OP_MONITOR_EXIT
+
+/* Originally declared in oo/TypeCheck.h */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,   // OP_APUT_OBJECT
+                           const ClassObject* arrayClass);
+int dvmInstanceofNonTrivial(const ClassObject* instance,   // OP_CHECK_CAST &&
+                            const ClassObject* clazz);     // OP_INSTANCE_OF
+
+/* Originally declared in oo/Array.h */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, // OP_NEW_ARRAY
+                                  size_t length, int allocFlags);
+
+/* Originally declared in interp/InterpDefs.h */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,// OP_FILL_ARRAY_DATA
+                                  const u2* arrayData);
+
+/* Originally declared in compiler/codegen/mips/Assemble.c */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+
+/*
+ * Resolve interface callsites - OP_INVOKE_INTERFACE & OP_INVOKE_INTERFACE_RANGE
+ *
+ * Originally declared in mterp/common/FindInterface.h and only comment it here
+ * due to the INLINE attribute.
+ *
+ * INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ *  u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+ */
+
+/* Originally declared in alloc/Alloc.h */
+Object* dvmAllocObject(ClassObject* clazz, int flags);  // OP_NEW_INSTANCE
+
+/*
+ * Functions declared in gDvmInlineOpsTable[] are used for
+ * OP_EXECUTE_INLINE & OP_EXECUTE_INLINE_RANGE.
+ */
+extern "C" double sqrt(double x);  // INLINE_MATH_SQRT
+
+/*
+ * The following functions are invoked through the compiler templates (declared
+ * in compiler/template/armv5te/footer.S:
+ *
+ *      __aeabi_cdcmple         // CMPG_DOUBLE
+ *      __aeabi_cfcmple         // CMPG_FLOAT
+ *      dvmLockObject           // MONITOR_ENTER
+ */
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_CALLOUT_HELPER_H_
diff --git a/vm/compiler/codegen/mips/Codegen.h b/vm/compiler/codegen/mips/Codegen.h
new file mode 100644
index 0000000..107fa86
--- /dev/null
+++ b/vm/compiler/codegen/mips/Codegen.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerIR.h"
+#include "CalloutHelper.h"
+
+#if defined(_CODEGEN_C)
+/*
+ * loadConstant() sometimes needs to add a small imm to a pre-existing constant
+ */
+static MipsLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value);
+static MipsLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2);
+
+/* Forward-declare the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir);
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir);
+
+
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Self Verification memory instruction decoder */
+extern "C" void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * Mips/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass);
+
+extern MipsLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+                                          int rSrc);
+
+extern MipsLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+                                   int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerSetupResourceMasks(MipsLIR *lir);
+
+extern void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                                    int displacement, int rSrc, OpSize size);
+
+extern void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                        int displacement, int rSrcLo,
+                                        int rSrcHi);
diff --git a/vm/compiler/codegen/mips/CodegenCommon.cpp b/vm/compiler/codegen/mips/CodegenCommon.cpp
new file mode 100644
index 0000000..0622fb8
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenCommon.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+#include "compiler/Loop.h"
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[256];
+
+static void setMemRefType(MipsLIR *lir, bool isLoad, int memType)
+{
+    /* MIPSTODO simplify setMemRefType() */
+    u8 *maskPtr;
+    u8 mask = ENCODE_MEM;;
+    assert(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE));
+
+    if (isLoad) {
+        maskPtr = &lir->useMask;
+    } else {
+        maskPtr = &lir->defMask;
+    }
+    /* Clear out the memref flags */
+    *maskPtr &= ~mask;
+    /* ..and then add back the one we need */
+    switch(memType) {
+        case kLiteral:
+            assert(isLoad);
+            *maskPtr |= ENCODE_LITERAL;
+            break;
+        case kDalvikReg:
+            *maskPtr |= ENCODE_DALVIK_REG;
+            break;
+        case kHeapRef:
+            *maskPtr |= ENCODE_HEAP_REF;
+            break;
+        case kMustNotAlias:
+            /* Currently only loads can be marked as kMustNotAlias */
+            assert(!(EncodingMap[lir->opcode].flags & IS_STORE));
+            *maskPtr |= ENCODE_MUST_NOT_ALIAS;
+            break;
+        default:
+            ALOGE("Jit: invalid memref kind - %d", memType);
+            assert(0);  // Bail if debug build, set worst-case in the field
+            *maskPtr |= ENCODE_ALL;
+    }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through rFP +
+ * offset.
+ */
+static void annotateDalvikRegAccess(MipsLIR *lir, int regId, bool isLoad)
+{
+    /* MIPSTODO simplify annotateDalvikRegAccess() */
+    setMemRefType(lir, isLoad, kDalvikReg);
+
+    /*
+     * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit
+     * access.
+     */
+    lir->aliasInfo = regId;
+    if (DOUBLEREG(lir->operands[0])) {
+        lir->aliasInfo |= 0x80000000;
+    }
+}
+
+/*
+ * Decode the register id
+ */
+static inline u8 getRegMaskCommon(int reg)
+{
+    u8 seed;
+    int shift;
+    int regId = reg & 0x1f;
+
+    /*
+     * Each double register is equal to a pair of single-precision FP registers
+     */
+    if (!DOUBLEREG(reg)) {
+        seed = 1;
+    } else {
+        assert((regId & 1) == 0); /* double registers must be even */
+        seed = 3;
+    }
+
+    if (FPREG(reg)) {
+       assert(regId < 16); /* only 16 fp regs */
+       shift = kFPReg0;
+    } else if (EXTRAREG(reg)) {
+       assert(regId < 3); /* only 3 extra regs */
+       shift = kFPRegEnd;
+    } else {
+       shift = 0;
+    }
+
+    /* Expand the double register id into single offset */
+    shift += regId;
+    return (seed << shift);
+}
+
+/* External version of getRegMaskCommon */
+u8 dvmGetRegResourceMask(int reg)
+{
+    return getRegMaskCommon(reg);
+}
+
+/*
+ * Mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, int reg)
+{
+    *mask |= getRegMaskCommon(reg);
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+static void setupResourceMasks(MipsLIR *lir)
+{
+    /* MIPSTODO simplify setupResourceMasks() */
+    int opcode = lir->opcode;
+    int flags;
+
+    if (opcode <= 0) {
+        lir->useMask = lir->defMask = 0;
+        return;
+    }
+
+    flags = EncodingMap[lir->opcode].flags;
+
+    /* Set up the mask for resources that are updated */
+    if (flags & (IS_LOAD | IS_STORE)) {
+        /* Default to heap - will catch specialized classes later */
+        setMemRefType(lir, flags & IS_LOAD, kHeapRef);
+    }
+
+    /*
+     * Conservatively assume the branch here will call out a function that in
+     * turn will trash everything.
+     */
+    if (flags & IS_BRANCH) {
+        lir->defMask = lir->useMask = ENCODE_ALL;
+        return;
+    }
+
+    if (flags & REG_DEF0) {
+        setupRegMask(&lir->defMask, lir->operands[0]);
+    }
+
+    if (flags & REG_DEF1) {
+        setupRegMask(&lir->defMask, lir->operands[1]);
+    }
+
+    if (flags & REG_DEF_SP) {
+        lir->defMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & REG_DEF_LIST0) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_DEF_LIST1) {
+        lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & SETS_CCODES) {
+        lir->defMask |= ENCODE_CCODE;
+    }
+
+    /* Conservatively treat the IT block */
+    if (flags & IS_IT) {
+        lir->defMask = ENCODE_ALL;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & REG_USE_PC) {
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    if (flags & REG_USE_SP) {
+        lir->useMask |= ENCODE_REG_SP;
+    }
+
+    if (flags & REG_USE_LIST0) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
+    }
+
+    if (flags & REG_USE_LIST1) {
+        lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * Set up the accurate resource mask for branch instructions
+ */
+static void relaxBranchMasks(MipsLIR *lir)
+{
+    int flags = EncodingMap[lir->opcode].flags;
+
+    /* Make sure only branch instructions are passed here */
+    assert(flags & IS_BRANCH);
+
+    lir->defMask |= ENCODE_REG_PC;
+    lir->useMask |= ENCODE_REG_PC;
+
+
+    if (flags & REG_DEF_LR) {
+        lir->defMask |= ENCODE_REG_LR;
+    }
+
+    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+        int i;
+
+        for (i = 0; i < 4; i++) {
+            if (flags & (1 << (kRegUse0 + i))) {
+                setupRegMask(&lir->useMask, lir->operands[i]);
+            }
+        }
+    }
+
+    if (flags & USES_CCODES) {
+        lir->useMask |= ENCODE_CCODE;
+    }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static MipsLIR *newLIR0(CompilationUnit *cUnit, MipsOpCode opcode)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND));
+    insn->opcode = opcode;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR1(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR2(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_BINARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR3(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1, int src2)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) {
+        ALOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode);
+    }
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_TERTIARY_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+static MipsLIR *newLIR4(CompilationUnit *cUnit, MipsOpCode opcode,
+                           int dest, int src1, int src2, int info)
+{
+    MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    assert(isPseudoOpCode(opcode) ||
+           (EncodingMap[opcode].flags & IS_QUAD_OP));
+    insn->opcode = opcode;
+    insn->operands[0] = dest;
+    insn->operands[1] = src1;
+    insn->operands[2] = src2;
+    insn->operands[3] = info;
+    setupResourceMasks(insn);
+    dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+    return insn;
+}
+
+/*
+ * If the next instruction is a move-result or move-result-long,
+ * return the target Dalvik sReg[s] and convert the next to a
+ * nop.  Otherwise, return INVALID_SREG.  Used to optimize method inlining.
+ */
+static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir,
+                                  bool fpHint)
+{
+    if (mir->next &&
+        ((mir->next->dalvikInsn.opcode == OP_MOVE_RESULT) ||
+         (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_OBJECT))) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDest(cUnit, mir->next, 0);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static MipsLIR *addWordData(CompilationUnit *cUnit, LIR **constantListP,
+                           int value)
+{
+    /* Add the constant to the literal pool */
+    if (constantListP) {
+        MipsLIR *newValue = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        newValue->operands[0] = value;
+        newValue->generic.next = *constantListP;
+        *constantListP = (LIR *) newValue;
+        return newValue;
+    } else {
+        /* Add the constant in the middle of code stream */
+        newLIR1(cUnit, kMips32BitData, value);
+    }
+    return NULL;
+}
+
+static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir,
+                                      bool fpHint)
+{
+    if (mir->next &&
+        (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_WIDE)) {
+        mir->next->dalvikInsn.opcode = OP_NOP;
+        return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1);
+    } else {
+        RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE;
+        res.fp = fpHint;
+        return res;
+    }
+}
+
+
+/*
+ * Generate an kMipsPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+static void genBarrier(CompilationUnit *cUnit)
+{
+    MipsLIR *barrier = newLIR0(cUnit, kMipsPseudoBarrier);
+    /* Mark all resources as being clobbered */
+    barrier->defMask = -1;
+}
+
+/* Create the PC reconstruction slot if not already done */
+extern MipsLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+                              MipsLIR *branch,
+                              MipsLIR *pcrLabel)
+{
+    /* Forget all def info (because we might rollback here.  Bug #2367397 */
+    dvmCompilerResetDefTracking(cUnit);
+
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + dOffset);
+        pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = dOffset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+
+    /* Clear the conservative flags for branches that punt to the interpreter */
+    relaxBranchMasks(branch);
+
+    return pcrLabel;
+}
diff --git a/vm/compiler/codegen/mips/CodegenDriver.cpp b/vm/compiler/codegen/mips/CodegenDriver.cpp
new file mode 100644
index 0000000..273a154
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenDriver.cpp
@@ -0,0 +1,4868 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg)
+{
+    int regCardBase = dvmCompilerAllocTemp(cUnit);
+    int regCardNo = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBeq, valReg, r_ZERO);
+    loadWordDisp(cUnit, rSELF, offsetof(Thread, cardTable),
+                 regCardBase);
+    opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
+    storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
+                     kUnsignedByte);
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *)target;
+    dvmCompilerFreeTemp(cUnit, regCardBase);
+    dvmCompilerFreeTemp(cUnit, regCardNo);
+}
+
+static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
+                                     int srcSize, int tgtSize)
+{
+    /*
+     * Don't optimize the register usage since it calls out to template
+     * functions
+     */
+    RegLocation rlSrc;
+    RegLocation rlDest;
+    int srcReg = 0;
+    int srcRegHi = 0;
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+
+    if (srcSize == kWord) {
+        srcReg = r_A0;
+    } else if (srcSize == kSingle) {
+#ifdef __mips_hard_float
+        srcReg = r_F12;
+#else
+        srcReg = r_A0;
+#endif
+    } else if (srcSize == kLong) {
+        srcReg = r_ARG0;
+        srcRegHi = r_ARG1;
+    } else if (srcSize == kDouble) {
+#ifdef __mips_hard_float
+        srcReg = r_FARG0;
+        srcRegHi = r_FARG1;
+#else
+        srcReg = r_ARG0;
+        srcRegHi = r_ARG1;
+#endif
+    }
+    else {
+        assert(0);
+    }
+
+    if (srcSize == kWord || srcSize == kSingle) {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        loadValueDirectFixed(cUnit, rlSrc, srcReg);
+    } else {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        loadValueDirectWideFixed(cUnit, rlSrc, srcReg, srcRegHi);
+    }
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+    if (tgtSize == kWord || tgtSize == kSingle) {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+#ifdef __mips_hard_float
+        if (tgtSize == kSingle)
+            rlResult = dvmCompilerGetReturnAlt(cUnit);
+        else
+            rlResult = dvmCompilerGetReturn(cUnit);
+#else
+        rlResult = dvmCompilerGetReturn(cUnit);
+#endif
+        storeValue(cUnit, rlDest, rlResult);
+    } else {
+        RegLocation rlResult;
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+#ifdef __mips_hard_float
+        if (tgtSize == kDouble)
+            rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnWide(cUnit);
+#else
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+#endif
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+    return false;
+}
+
+
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+                                    RegLocation rlDest, RegLocation rlSrc1,
+                                    RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            funct = (void*) __addsf3;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            funct = (void*) __subsf3;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            funct = (void*) __divsf3;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            funct = (void*) __mulsf3;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+            funct = (void*) fmodf;
+            break;
+        case OP_NEG_FLOAT: {
+            genNegFloat(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+#ifdef __mips_hard_float
+    loadValueDirectFixed(cUnit, rlSrc1, r_F12);
+    loadValueDirectFixed(cUnit, rlSrc2, r_F14);
+#else
+    loadValueDirectFixed(cUnit, rlSrc1, r_A0);
+    loadValueDirectFixed(cUnit, rlSrc2, r_A1);
+#endif
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+#ifdef __mips_hard_float
+    rlResult = dvmCompilerGetReturnAlt(cUnit);
+#else
+    rlResult = dvmCompilerGetReturn(cUnit);
+#endif
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+                                     RegLocation rlDest, RegLocation rlSrc1,
+                                     RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    void* funct;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            funct = (void*) __adddf3;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            funct = (void*) __subdf3;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            funct = (void*) __divsf3;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            funct = (void*) __muldf3;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+            funct = (void*) (double (*)(double, double)) fmod;
+            break;
+        case OP_NEG_DOUBLE: {
+            genNegDouble(cUnit, rlDest, rlSrc1);
+            return false;
+        }
+        default:
+            return true;
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)funct);
+#ifdef __mips_hard_float
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_F12, r_F13);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_F14, r_F15);
+#else
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+#endif
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+#ifdef __mips_hard_float
+    rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+#else
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+#endif
+    storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+    cUnit->usesLinkRegister = true;
+#endif
+    return false;
+}
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__floatsisf, kWord, kSingle);
+        case OP_FLOAT_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__fixsfsi, kSingle, kWord);
+        case OP_DOUBLE_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__truncdfsf2, kDouble, kSingle);
+        case OP_FLOAT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__extendsfdf2, kSingle, kDouble);
+        case OP_INT_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__floatsidf, kWord, kDouble);
+        case OP_DOUBLE_TO_INT:
+            return genConversionCall(cUnit, mir, (void*)__fixdfsi, kDouble, kWord);
+        case OP_FLOAT_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)__fixsfdi, kSingle, kLong);
+        case OP_LONG_TO_FLOAT:
+            return genConversionCall(cUnit, mir, (void*)__floatdisf, kLong, kSingle);
+        case OP_DOUBLE_TO_LONG:
+            return genConversionCall(cUnit, mir, (void*)__fixdfdi, kDouble, kLong);
+        case OP_LONG_TO_DOUBLE:
+            return genConversionCall(cUnit, mir, (void*)__floatdidf, kLong, kDouble);
+        default:
+            return true;
+    }
+    return false;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void selfVerificationBranchInsert(LIR *currentLIR, Mipsopcode opcode,
+                          int dest, int src1)
+{
+assert(0); /* MIPSTODO port selfVerificationBranchInsert() */
+     MipsLIR *insn = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+     insn->opcode = opcode;
+     insn->operands[0] = dest;
+     insn->operands[1] = src1;
+     setupResourceMasks(insn);
+     dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+/*
+ * Example where r14 (LR) is preserved around a heap access under
+ * self-verification mode in Thumb2:
+ *
+ * D/dalvikvm( 1538): 0x59414c5e (0026): ldr     r14, [r15pc, #220] <-hoisted
+ * D/dalvikvm( 1538): 0x59414c62 (002a): mla     r4, r0, r8, r4
+ * D/dalvikvm( 1538): 0x59414c66 (002e): adds    r3, r4, r3
+ * D/dalvikvm( 1538): 0x59414c6a (0032): push    <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c6c (0034): blx_1   0x5940f494      |
+ * D/dalvikvm( 1538): 0x59414c6e (0036): blx_2   see above       <-MEM_OP_DECODE
+ * D/dalvikvm( 1538): 0x59414c70 (0038): ldr     r10, [r9, #0]   |
+ * D/dalvikvm( 1538): 0x59414c74 (003c): pop     <r5, r14>    ---+
+ * D/dalvikvm( 1538): 0x59414c78 (0040): mov     r11, r10
+ * D/dalvikvm( 1538): 0x59414c7a (0042): asr     r12, r11, #31
+ * D/dalvikvm( 1538): 0x59414c7e (0046): movs    r0, r2
+ * D/dalvikvm( 1538): 0x59414c80 (0048): movs    r1, r3
+ * D/dalvikvm( 1538): 0x59414c82 (004a): str     r2, [r5, #16]
+ * D/dalvikvm( 1538): 0x59414c84 (004c): mov     r2, r11
+ * D/dalvikvm( 1538): 0x59414c86 (004e): str     r3, [r5, #20]
+ * D/dalvikvm( 1538): 0x59414c88 (0050): mov     r3, r12
+ * D/dalvikvm( 1538): 0x59414c8a (0052): str     r11, [r5, #24]
+ * D/dalvikvm( 1538): 0x59414c8e (0056): str     r12, [r5, #28]
+ * D/dalvikvm( 1538): 0x59414c92 (005a): blx     r14             <-use of LR
+ *
+ */
+static void selfVerificationBranchInsertPass(CompilationUnit *cUnit)
+{
+assert(0); /* MIPSTODO port selfVerificationBranchInsertPass() */
+    MipsLIR *thisLIR;
+    Templateopcode opcode = TEMPLATE_MEM_OP_DECODE;
+
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        if (!thisLIR->flags.isNop && thisLIR->flags.insertWrapper) {
+            /*
+             * Push r5(FP) and r14(LR) onto stack. We need to make sure that
+             * SP is 8-byte aligned, and we use r5 as a temp to restore LR
+             * for Thumb-only target since LR cannot be directly accessed in
+             * Thumb mode. Another reason to choose r5 here is it is the Dalvik
+             * frame pointer and cannot be the target of the emulated heap
+             * load.
+             */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPreBranch(cUnit, thisLIR);
+            }
+
+            /* Branch to mem op decode template */
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+            selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2,
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+                       (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+
+            /* Restore LR */
+            if (cUnit->usesLinkRegister) {
+                genSelfVerificationPostBranch(cUnit, thisLIR);
+            }
+        }
+    }
+}
+#endif
+
+/* Generate conditional branch instructions */
+static MipsLIR *genConditionalBranchMips(CompilationUnit *cUnit,
+                                    MipsOpCode opc, int rs, int rt,
+                                    MipsLIR *target)
+{
+    MipsLIR *branch = opCompareBranch(cUnit, opc, rs, rt);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline MipsLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+                                  MipsLIR *pcrLabel)
+{
+    MipsLIR *branch = opNone(cUnit, kOpUncondBr);
+    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(rlDest.wide);
+
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    HEAP_ACCESS_SHADOW(true);
+    loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    int regPtr;
+    rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+    regPtr = dvmCompilerAllocTemp(cUnit);
+    opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+    HEAP_ACCESS_SHADOW(true);
+    storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+}
+
+/*
+ * Load a field from an object instance
+ *
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isVolatile)
+{
+    RegLocation rlResult;
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    HEAP_ACCESS_SHADOW(true);
+    loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+                 size, rlObj.sRegLow);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kSY);
+    }
+
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+/*
+ * Store a field to an object instance
+ *
+ */
+static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                    int fieldOffset, bool isObject, bool isVolatile)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlSrc = loadValue(cUnit, rlSrc, regClass);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+                 NULL);/* null object? */
+
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kSY);
+    }
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+    HEAP_ACCESS_SHADOW(false);
+    if (isVolatile) {
+        dvmCompilerGenMemBarrier(cUnit, kSY);
+    }
+    if (isObject) {
+        /* NOTE: marking card based on object head */
+        markCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
+    }
+}
+
+
+/*
+ * Generate array load
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlDest, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlResult;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+    int regPtr;
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
+                                rlArray.lowReg, mir->offset, NULL);
+    }
+
+    regPtr = dvmCompilerAllocTemp(cUnit);
+
+    assert(IS_SIMM16(dataOffset));
+    if (scale) {
+        opRegRegImm(cUnit, kOpLsl, regPtr, rlIndex.lowReg, scale);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    }
+
+    if (scale) {
+        opRegReg(cUnit, kOpAdd, regPtr, rlArray.lowReg);
+    } else {
+        opRegRegReg(cUnit, kOpAdd, regPtr, rlArray.lowReg, rlIndex.lowReg);
+    }
+
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+    if ((size == kLong) || (size == kDouble)) {
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseDispWide(cUnit, mir, regPtr, dataOffset, rlResult.lowReg,
+                         rlResult.highReg, INVALID_SREG);
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        HEAP_ACCESS_SHADOW(true);
+        loadBaseDisp(cUnit, mir, regPtr, dataOffset, rlResult.lowReg,
+                     size, INVALID_SREG);
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+}
+
+/*
+ * Generate array store
+ *
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+                        RegLocation rlArray, RegLocation rlIndex,
+                        RegLocation rlSrc, int scale)
+{
+    RegisterClass regClass = dvmCompilerRegClassBySize(size);
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regPtr;
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+
+    if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) {
+        dvmCompilerClobber(cUnit, rlArray.lowReg);
+        regPtr = rlArray.lowReg;
+    } else {
+        regPtr = dvmCompilerAllocTemp(cUnit);
+        genRegCopy(cUnit, regPtr, rlArray.lowReg);
+    }
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
+                                mir->offset, NULL);
+    }
+
+    assert(IS_SIMM16(dataOffset));
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    if (scale) {
+        opRegRegImm(cUnit, kOpLsl, tReg, rlIndex.lowReg, scale);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        int regLen = dvmCompilerAllocTemp(cUnit);
+        //NOTE: max live temps(4) here.
+        /* Get len */
+        loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+                       pcrLabel);
+        dvmCompilerFreeTemp(cUnit, regLen);
+    }
+
+    if (scale) {
+        opRegReg(cUnit, kOpAdd, tReg, rlArray.lowReg);
+    } else {
+        opRegRegReg(cUnit, kOpAdd, tReg, rlArray.lowReg, rlIndex.lowReg);
+    }
+
+    /* at this point, tReg points to array, 2 live temps */
+    if ((size == kLong) || (size == kDouble)) {
+        rlSrc = loadValueWide(cUnit, rlSrc, regClass);
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseDispWide(cUnit, tReg, dataOffset, rlSrc.lowReg, rlSrc.highReg)
+        HEAP_ACCESS_SHADOW(false);
+        dvmCompilerFreeTemp(cUnit, tReg);
+        dvmCompilerFreeTemp(cUnit, regPtr);
+    } else {
+        rlSrc = loadValue(cUnit, rlSrc, regClass);
+        HEAP_ACCESS_SHADOW(true);
+        storeBaseDisp(cUnit, tReg, dataOffset, rlSrc.lowReg, size);
+        dvmCompilerFreeTemp(cUnit, tReg);
+        HEAP_ACCESS_SHADOW(false);
+    }
+}
+
+/*
+ * Generate array object store
+ * Must use explicit register allocation here because of
+ * call-out to dvmCanPutArrayElement
+ */
+static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir,
+                              RegLocation rlArray, RegLocation rlIndex,
+                              RegLocation rlSrc, int scale)
+{
+    int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents);
+
+    int regLen = r_A0;
+    int regPtr = r_S0;  /* Preserved across call */
+    int regArray = r_A1;
+    int regIndex = r_S4;  /* Preserved across call */
+
+    dvmCompilerFlushAllRegs(cUnit);
+    // moved lock for r_S0 and r_S4 here from below since genBoundsCheck
+    // allocates a temporary that can result in clobbering either of them
+    dvmCompilerLockTemp(cUnit, regPtr);   // r_S0
+    dvmCompilerLockTemp(cUnit, regIndex); // r_S4
+
+    loadValueDirectFixed(cUnit, rlArray, regArray);
+    loadValueDirectFixed(cUnit, rlIndex, regIndex);
+
+    /* null object? */
+    MipsLIR * pcrLabel = NULL;
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray,
+                                mir->offset, NULL);
+    }
+
+    if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        /* Get len */
+        loadWordDisp(cUnit, regArray, lenOffset, regLen);
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+        genBoundsCheck(cUnit, regIndex, regLen, mir->offset,
+                       pcrLabel);
+    } else {
+        /* regPtr -> array data */
+        opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+    }
+
+    /* Get object to store */
+    loadValueDirectFixed(cUnit, rlSrc, r_A0);
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmCanPutArrayElement);
+
+    /* Are we storing null?  If so, avoid check */
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBeqz, r_A0, -1);
+
+    /* Make sure the types are compatible */
+    loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r_A1);
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A0);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    dvmCompilerClobberCallRegs(cUnit);
+
+    /*
+     * Using fixed registers here, and counting on r_S0 and r_S4 being
+     * preserved across the above call.  Tell the register allocation
+     * utilities about the regs we are using directly
+     */
+    dvmCompilerLockTemp(cUnit, r_A0);
+    dvmCompilerLockTemp(cUnit, r_A1);
+
+    /* Bad? - roll back and re-execute if so */
+    genRegImmCheck(cUnit, kMipsCondEq, r_V0, 0, mir->offset, pcrLabel);
+
+    /* Resume here - must reload element & array, regPtr & index preserved */
+    loadValueDirectFixed(cUnit, rlSrc, r_A0);
+    loadValueDirectFixed(cUnit, rlArray, r_A1);
+
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseIndexed(cUnit, regPtr, regIndex, r_A0,
+                     scale, kWord);
+    HEAP_ACCESS_SHADOW(false);
+
+    dvmCompilerFreeTemp(cUnit, regPtr);
+    dvmCompilerFreeTemp(cUnit, regIndex);
+
+    /* NOTE: marking card here based on object head */
+    markCard(cUnit, r_A0, r_A1);
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlShift)
+{
+    /*
+     * Don't mess with the regsiters here as there is a particular calling
+     * convention to the out-of-line handler.
+     */
+    RegLocation rlResult;
+
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirect(cUnit, rlShift, r_A2);
+    switch( mir->dalvikInsn.opcode) {
+        case OP_SHL_LONG:
+        case OP_SHL_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+            break;
+        case OP_SHR_LONG:
+        case OP_SHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+            break;
+        case OP_USHR_LONG:
+        case OP_USHR_LONG_2ADDR:
+            genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+            break;
+        default:
+            return true;
+    }
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir,
+                           RegLocation rlDest, RegLocation rlSrc1,
+                           RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    OpKind firstOp = kOpBkpt;
+    OpKind secondOp = kOpBkpt;
+    bool callOut = false;
+    bool checkZero = false;
+    void *callTgt;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NOT_LONG:
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
+            opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        case OP_ADD_LONG:
+        case OP_ADD_LONG_2ADDR:
+            firstOp = kOpAdd;
+            secondOp = kOpAdc;
+            break;
+        case OP_SUB_LONG:
+        case OP_SUB_LONG_2ADDR:
+            firstOp = kOpSub;
+            secondOp = kOpSbc;
+            break;
+        case OP_MUL_LONG:
+        case OP_MUL_LONG_2ADDR:
+            genMulLong(cUnit, rlDest, rlSrc1, rlSrc2);
+            return false;
+        case OP_DIV_LONG:
+        case OP_DIV_LONG_2ADDR:
+            callOut = true;
+            checkZero = true;
+            callTgt = (void*)__divdi3;
+            break;
+        case OP_REM_LONG:
+        case OP_REM_LONG_2ADDR:
+            callOut = true;
+            callTgt = (void*)__moddi3;
+            checkZero = true;
+            break;
+        case OP_AND_LONG_2ADDR:
+        case OP_AND_LONG:
+            firstOp = kOpAnd;
+            secondOp = kOpAnd;
+            break;
+        case OP_OR_LONG:
+        case OP_OR_LONG_2ADDR:
+            firstOp = kOpOr;
+            secondOp = kOpOr;
+            break;
+        case OP_XOR_LONG:
+        case OP_XOR_LONG_2ADDR:
+            firstOp = kOpXor;
+            secondOp = kOpXor;
+            break;
+        case OP_NEG_LONG: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            newLIR3(cUnit, kMipsSubu, rlResult.lowReg, r_ZERO, rlSrc2.lowReg);
+            newLIR3(cUnit, kMipsSubu, tReg, r_ZERO, rlSrc2.highReg);
+            newLIR3(cUnit, kMipsSltu, rlResult.highReg, r_ZERO, rlResult.lowReg);
+            newLIR3(cUnit, kMipsSubu, rlResult.highReg, tReg, rlResult.highReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            storeValueWide(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+        default:
+            ALOGE("Invalid long arith op");
+            dvmCompilerAbort(cUnit);
+    }
+    if (!callOut) {
+        genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
+    } else {
+        dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+        loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+        loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+        LOAD_FUNC_ADDR(cUnit, r_T9, (int) callTgt);
+        if (checkZero) {
+            int tReg = r_T1; // Using fixed registers during call sequence
+            opRegRegReg(cUnit, kOpOr, tReg, r_ARG2, r_ARG3);
+            genRegImmCheck(cUnit, kMipsCondEq, tReg, 0, mir->offset, NULL);
+        }
+        opReg(cUnit, kOpBlx, r_T9);
+        newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+        dvmCompilerClobberCallRegs(cUnit);
+        rlResult = dvmCompilerGetReturnWide(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+#if defined(WITH_SELF_VERIFICATION)
+        cUnit->usesLinkRegister = true;
+#endif
+    }
+    return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir,
+                          RegLocation rlDest, RegLocation rlSrc1,
+                          RegLocation rlSrc2)
+{
+    OpKind op = kOpBkpt;
+    bool checkZero = false;
+    bool unary = false;
+    RegLocation rlResult;
+    bool shiftOp = false;
+    int isDivRem = false;
+    MipsOpCode opc;
+    int divReg;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_NEG_INT:
+            op = kOpNeg;
+            unary = true;
+            break;
+        case OP_NOT_INT:
+            op = kOpMvn;
+            unary = true;
+            break;
+        case OP_ADD_INT:
+        case OP_ADD_INT_2ADDR:
+            op = kOpAdd;
+            break;
+        case OP_SUB_INT:
+        case OP_SUB_INT_2ADDR:
+            op = kOpSub;
+            break;
+        case OP_MUL_INT:
+        case OP_MUL_INT_2ADDR:
+            op = kOpMul;
+            break;
+        case OP_DIV_INT:
+        case OP_DIV_INT_2ADDR:
+            isDivRem = true;
+            checkZero = true;
+            opc = kMipsMflo;
+            divReg = r_LO;
+            break;
+        case OP_REM_INT:
+        case OP_REM_INT_2ADDR:
+            isDivRem = true;
+            checkZero = true;
+            opc = kMipsMfhi;
+            divReg = r_HI;
+            break;
+        case OP_AND_INT:
+        case OP_AND_INT_2ADDR:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT:
+        case OP_OR_INT_2ADDR:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT:
+        case OP_XOR_INT_2ADDR:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT:
+        case OP_SHL_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT:
+        case OP_SHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT:
+        case OP_USHR_INT_2ADDR:
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+        default:
+            ALOGE("Invalid word arith op: %#x(%d)",
+                 mir->dalvikInsn.opcode, mir->dalvikInsn.opcode);
+            dvmCompilerAbort(cUnit);
+    }
+
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    if (unary) {
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        opRegReg(cUnit, op, rlResult.lowReg,
+                 rlSrc1.lowReg);
+    } else if (isDivRem) {
+        rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+        if (checkZero) {
+            genNullCheck(cUnit, rlSrc2.sRegLow, rlSrc2.lowReg, mir->offset, NULL);
+        }
+        newLIR4(cUnit, kMipsDiv, r_HI, r_LO, rlSrc1.lowReg, rlSrc2.lowReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        newLIR2(cUnit, opc, rlResult.lowReg, divReg);
+    } else {
+        rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+        if (shiftOp) {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, op, rlResult.lowReg,
+                        rlSrc1.lowReg, tReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        } else {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, op, rlResult.lowReg,
+                        rlSrc1.lowReg, rlSrc2.lowReg);
+        }
+    }
+    storeValue(cUnit, rlDest, rlResult);
+
+    return false;
+}
+
+static bool genArithOp(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    /* Deduce sizes of operands */
+    if (mir->ssaRep->numUses == 2) {
+        rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    } else if (mir->ssaRep->numUses == 3) {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+    } else {
+        rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        assert(mir->ssaRep->numUses == 4);
+    }
+    if (mir->ssaRep->numDefs == 1) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    } else {
+        assert(mir->ssaRep->numDefs == 2);
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    }
+
+    if ((opcode >= OP_ADD_LONG_2ADDR) && (opcode <= OP_XOR_LONG_2ADDR)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_LONG) && (opcode <= OP_XOR_LONG)) {
+        return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG_2ADDR) && (opcode <= OP_USHR_LONG_2ADDR)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_SHL_LONG) && (opcode <= OP_USHR_LONG)) {
+        return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_USHR_INT_2ADDR)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_INT) && (opcode <= OP_USHR_INT)) {
+        return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT_2ADDR) && (opcode <= OP_REM_FLOAT_2ADDR)) {
+        return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_FLOAT) && (opcode <= OP_REM_FLOAT)) {
+        return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    if ((opcode >= OP_ADD_DOUBLE) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+    }
+    return true;
+}
+
+/* Generate unconditional branch instructions */
+static MipsLIR *genUnconditionalBranch(CompilationUnit *cUnit, MipsLIR *target)
+{
+    MipsLIR *branch = opNone(cUnit, kOpUncondBr);
+    branch->generic.target = (LIR *) target;
+    return branch;
+}
+
+/* Perform the actual operation for OP_RETURN_* */
+void genReturnCommon(CompilationUnit *cUnit, MIR *mir)
+{
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                         TEMPLATE_RETURN_PROF : TEMPLATE_RETURN);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.returnOp++;
+#endif
+    int dPC = (int) (cUnit->method->insns + mir->offset);
+    /* Insert branch, but defer setting of target */
+    MipsLIR *branch = genUnconditionalBranch(cUnit, NULL);
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    MipsLIR *pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+    pcrLabel->operands[0] = dPC;
+    pcrLabel->operands[1] = mir->offset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+                                  DecodedInstruction *dInsn,
+                                  MipsLIR **pcrLabel)
+{
+    unsigned int i;
+    unsigned int regMask = 0;
+    RegLocation rlArg;
+    int numDone = 0;
+
+    /*
+     * Load arguments to r_A0..r_T0.  Note that these registers may contain
+     * live values, so we clobber them immediately after loading to prevent
+     * them from being used as sources for subsequent loads.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    for (i = 0; i < dInsn->vA; i++) {
+        regMask |= 1 << i;
+        rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++);
+        loadValueDirectFixed(cUnit, rlArg, i+r_A0); /* r_A0 thru r_T0 */
+    }
+    if (regMask) {
+        /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+        opRegRegImm(cUnit, kOpSub, r_S4, rFP,
+                    sizeof(StackSaveArea) + (dInsn->vA << 2));
+        /* generate null check */
+        if (pcrLabel) {
+            *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r_A0,
+                                     mir->offset, NULL);
+        }
+        storeMultiple(cUnit, r_S4, regMask);
+    }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+                                DecodedInstruction *dInsn,
+                                MipsLIR **pcrLabel)
+{
+    int srcOffset = dInsn->vC << 2;
+    int numArgs = dInsn->vA;
+    int regMask;
+
+    /*
+     * Note: here, all promoted registers will have been flushed
+     * back to the Dalvik base locations, so register usage restrictins
+     * are lifted.  All parms loaded from original Dalvik register
+     * region - even though some might conceivably have valid copies
+     * cached in a preserved register.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * r4PC     : &rFP[vC]
+     * r_S4: &newFP[0]
+     */
+    opRegRegImm(cUnit, kOpAdd, r4PC, rFP, srcOffset);
+    /* load [r_A0 up to r_A3)] */
+    regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+    /*
+     * Protect the loadMultiple instruction from being reordered with other
+     * Dalvik stack accesses.
+     */
+    if (numArgs != 0) loadMultiple(cUnit, r4PC, regMask);
+
+    opRegRegImm(cUnit, kOpSub, r_S4, rFP,
+                sizeof(StackSaveArea) + (numArgs << 2));
+    /* generate null check */
+    if (pcrLabel) {
+        *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r_A0,
+                                 mir->offset, NULL);
+    }
+
+    /*
+     * Handle remaining 4n arguments:
+     * store previously loaded 4 values and load the next 4 values
+     */
+    if (numArgs >= 8) {
+        MipsLIR *loopLabel = NULL;
+        /*
+         * r_A0 contains "this" and it will be used later, so push it to the stack
+         * first. Pushing r_S1 (rFP) is just for stack alignment purposes.
+         */
+
+        newLIR2(cUnit, kMipsMove, r_T0, r_A0);
+        newLIR2(cUnit, kMipsMove, r_T1, r_S1);
+
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            loadConstant(cUnit, rFP, ((numArgs - 4) >> 2) << 2);
+            loopLabel = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            loopLabel->defMask = ENCODE_ALL;
+        }
+        storeMultiple(cUnit, r_S4, regMask);
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+        /* No need to generate the loop structure if numArgs <= 11 */
+        if (numArgs > 11) {
+            opRegImm(cUnit, kOpSub, rFP, 4);
+            genConditionalBranchMips(cUnit, kMipsBne, rFP, r_ZERO, loopLabel);
+        }
+    }
+
+    /* Save the last batch of loaded values */
+    if (numArgs != 0) storeMultiple(cUnit, r_S4, regMask);
+
+    /* Generate the loop epilogue - don't use r_A0 */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+        /*
+         * Protect the loadMultiple instruction from being reordered with other
+         * Dalvik stack accesses.
+         */
+        loadMultiple(cUnit, r4PC, regMask);
+    }
+    if (numArgs >= 8) {
+        newLIR2(cUnit, kMipsMove, r_A0, r_T0);
+        newLIR2(cUnit, kMipsMove, r_S1, r_T1);
+    }
+
+    /* Save the modulo 4 arguments */
+    if ((numArgs > 4) && (numArgs % 4)) {
+        storeMultiple(cUnit, r_S4, regMask);
+    }
+}
+
+/*
+ * Generate code to setup the call stack then jump to the chaining cell if it
+ * is not a native method.
+ */
+static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir,
+                                     BasicBlock *bb, MipsLIR *labelList,
+                                     MipsLIR *pcrLabel,
+                                     const Method *calleeMethod)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  All temp & preserved registers may be used.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+    MipsLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+    /* r_A1 = &retChainingCell */
+    dvmCompilerLockTemp(cUnit, r_A1);
+    MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+    /*
+     * r_A0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+     * r_A1 = &ChainingCell
+     * r4PC = callsiteDPC
+     */
+    if (dvmIsNativeMethod(calleeMethod)) {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_NATIVE_PROF :
+            TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeNative++;
+#endif
+    } else {
+        genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+            TEMPLATE_INVOKE_METHOD_CHAIN_PROF :
+            TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeMonomorphic++;
+#endif
+        /* Branch to the chaining cell */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/*
+ * Generate code to check the validity of a predicted chain and take actions
+ * based on the result.
+ *
+ * 0x2f1304c4 :  lui      s0,0x2d22(11554)            # s0 <- dalvikPC
+ * 0x2f1304c8 :  ori      s0,s0,0x2d22848c(757236876)
+ * 0x2f1304cc :  lahi/lui a1,0x2f13(12051)            # a1 <- &retChainingCell
+ * 0x2f1304d0 :  lalo/ori a1,a1,0x2f13055c(789775708)
+ * 0x2f1304d4 :  lahi/lui a2,0x2f13(12051)            # a2 <- &predictedChainingCell
+ * 0x2f1304d8 :  lalo/ori a2,a2,0x2f13056c(789775724)
+ * 0x2f1304dc :  jal      0x2f12d1ec(789762540)       # call TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+ * 0x2f1304e0 :  nop
+ * 0x2f1304e4 :  b        0x2f13056c (L0x11ec10)      # off to the predicted chain
+ * 0x2f1304e8 :  nop
+ * 0x2f1304ec :  b        0x2f13054c (L0x11fc80)      # punt to the interpreter
+ * 0x2f1304f0 :  lui      a0,0x2d22(11554)
+ * 0x2f1304f4 :  lw       a0,156(s4)                  # a0 <- this->class->vtable[methodIdx]
+ * 0x2f1304f8 :  bgtz     a1,0x2f13051c (L0x11fa40)   # if >0 don't rechain
+ * 0x2f1304fc :  nop
+ * 0x2f130500 :  lui      t9,0x2aba(10938)
+ * 0x2f130504 :  ori      t9,t9,0x2abae3f8(716891128)
+ * 0x2f130508 :  move     a1,s2
+ * 0x2f13050c :  jalr     ra,t9                       # call dvmJitToPatchPredictedChain
+ * 0x2f130510 :  nop
+ * 0x2f130514 :  lw       gp,84(sp)
+ * 0x2f130518 :  move     a0,v0
+ * 0x2f13051c :  lahi/lui a1,0x2f13(12051)            # a1 <- &retChainingCell
+ * 0x2f130520 :  lalo/ori a1,a1,0x2f13055c(789775708)
+ * 0x2f130524 :  jal      0x2f12d0c4(789762244)       # call TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x2f130528 :  nop
+ */
+static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir,
+                                   int methodIndex,
+                                   MipsLIR *retChainingCell,
+                                   MipsLIR *predChainingCell,
+                                   MipsLIR *pcrLabel)
+{
+    /*
+     * Note: all Dalvik register state should be flushed to
+     * memory by the point, so register usage restrictions no
+     * longer apply.  Lock temps to prevent them from being
+     * allocated by utility routines.
+     */
+    dvmCompilerLockAllTemps(cUnit);
+
+    /*
+     * For verbose printing, store the method pointer in operands[1] first as
+     * operands[0] will be clobbered in dvmCompilerMIR2LIR.
+     */
+    predChainingCell->operands[1] = (int) mir->meta.callsiteInfo->method;
+
+    /* "this" is already left in r_A0 by genProcessArgs* */
+
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+
+    /* r_A1 = &retChainingCell */
+    MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r_A2 = &predictedChainingCell */
+    MipsLIR *predictedChainingCell = newLIR2(cUnit, kMipsLahi, r_A2, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+    predictedChainingCell = newLIR3(cUnit, kMipsLalo, r_A2, r_A2, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+        TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+    /* return through ra - jump to the chaining cell */
+    genUnconditionalBranch(cUnit, predChainingCell);
+
+    /*
+     * null-check on "this" may have been eliminated, but we still need a PC-
+     * reconstruction label for stack overflow bailout.
+     */
+    if (pcrLabel == NULL) {
+        int dPC = (int) (cUnit->method->insns + mir->offset);
+        pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+        pcrLabel->operands[0] = dPC;
+        pcrLabel->operands[1] = mir->offset;
+        /* Insert the place holder to the growable list */
+        dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                              (intptr_t) pcrLabel);
+    }
+
+    /* return through ra+8 - punt to the interpreter */
+    genUnconditionalBranch(cUnit, pcrLabel);
+
+    /*
+     * return through ra+16 - fully resolve the callee method.
+     * r_A1 <- count
+     * r_A2 <- &predictedChainCell
+     * r_A3 <- this->class
+     * r4 <- dPC
+     * r_S4 <- this->class->vtable
+     */
+
+    /* r_A0 <- calleeMethod */
+    loadWordDisp(cUnit, r_S4, methodIndex * 4, r_A0);
+
+    /* Check if rechain limit is reached */
+    MipsLIR *bypassRechaining = opCompareBranch(cUnit, kMipsBgtz, r_A1, -1);
+
+    LOAD_FUNC_ADDR(cUnit, r_T9, (int) dvmJitToPatchPredictedChain);
+
+    genRegCopy(cUnit, r_A1, rSELF);
+
+    /*
+     * r_A0 = calleeMethod
+     * r_A2 = &predictedChainingCell
+     * r_A3 = class
+     *
+     * &returnChainingCell has been loaded into r_A1 but is not needed
+     * when patching the chaining cell and will be clobbered upon
+     * returning so it will be reconstructed again.
+     */
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    newLIR2(cUnit, kMipsMove, r_A0, r_V0);
+
+    /* r_A1 = &retChainingCell */
+    addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    bypassRechaining->generic.target = (LIR *) addrRetChain;
+    addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /*
+     * r_A0 = calleeMethod,
+     * r_A1 = &ChainingCell,
+     * r4PC = callsiteDPC,
+     */
+    genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+        TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+        TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.invokePolymorphic++;
+#endif
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* "this" pointer is already in r0 */
+static void genInvokeVirtualWholeMethod(CompilationUnit *cUnit,
+                                        MIR *mir,
+                                        void *calleeAddr,
+                                        MipsLIR *retChainingCell)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    dvmCompilerLockAllTemps(cUnit);
+
+    loadClassPointer(cUnit, r_A1, (int) callsiteInfo);
+
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A2);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    /* Branch to the slow path if classes are not equal */
+    MipsLIR *classCheck = opCompareBranch(cUnit, kMipsBne, r_A1, r_A2);
+
+    /* a0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR1(cUnit, kMipsJal, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    /* Target of slow path */
+    MipsLIR *slowPathLabel = newLIR0(cUnit, kMipsPseudoTargetLabel);
+
+    slowPathLabel->defMask = ENCODE_ALL;
+    classCheck->generic.target = (LIR *) slowPathLabel;
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+static void genInvokeSingletonWholeMethod(CompilationUnit *cUnit,
+                                          MIR *mir,
+                                          void *calleeAddr,
+                                          MipsLIR *retChainingCell)
+{
+    /* a0 = the Dalvik PC of the callsite */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+
+    newLIR1(cUnit, kMipsJal, (int) calleeAddr);
+    genUnconditionalBranch(cUnit, retChainingCell);
+
+    // FIXME
+    cUnit->printMe = true;
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+    /* a0 = dalvik pc */
+    dvmCompilerFlushAllRegs(cUnit);
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + offset));
+#if 0 /* MIPSTODO tempoary workaround unaligned access on sigma hardware
+             this can removed when we're not punting to genInterpSingleStep
+             for opcodes that haven't been activated yet */
+    loadWordDisp(cUnit, r_A0, offsetof(Object, clazz), r_A3);
+#endif
+    loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                 jitToInterpEntries.dvmJitToInterpPunt), r_A1);
+
+    opReg(cUnit, kOpBlx, r_A1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+    int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+    int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn;
+
+    // Single stepping is considered loop mode breaker
+    if (cUnit->jitMode == kJitLoop) {
+        cUnit->quitLoopMode = true;
+        return;
+    }
+
+    //If already optimized out, just ignore
+    if (mir->dalvikInsn.opcode == OP_NOP)
+        return;
+
+    //Ugly, but necessary.  Flush all Dalvik regs so Interp can find them
+    dvmCompilerFlushAllRegs(cUnit);
+
+    if ((mir->next == NULL) || (flags & flagsToCheck)) {
+       genPuntToInterp(cUnit, mir->offset);
+       return;
+    }
+    int entryAddr = offsetof(Thread,
+                             jitToInterpEntries.dvmJitToInterpSingleStep);
+    loadWordDisp(cUnit, rSELF, entryAddr, r_A2);
+    /* a0 = dalvik pc */
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+    /* a1 = dalvik pc of following instruction */
+    loadConstant(cUnit, r_A1, (int) (cUnit->method->insns + mir->next->offset));
+    opReg(cUnit, kOpBlx, r_A2);
+}
+
+/*
+ * To prevent a thread in a monitor wait from blocking the Jit from
+ * resetting the code cache, heavyweight monitor lock will not
+ * be allowed to return to an existing translation.  Instead, we will
+ * handle them by branching to a handler, which will in turn call the
+ * runtime lock routine and then branch directly back to the
+ * interpreter main loop.  Given the high cost of the heavyweight
+ * lock operation, this additional cost should be slight (especially when
+ * considering that we expect the vast majority of lock operations to
+ * use the fast-path thin lock bypass).
+ */
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir)
+{
+    bool isEnter = (mir->dalvikInsn.opcode == OP_MONITOR_ENTER);
+    genExportPC(cUnit, mir);
+    dvmCompilerFlushAllRegs(cUnit);   /* Send everything to home location */
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    loadValueDirectFixed(cUnit, rlSrc, r_A1);
+    genRegCopy(cUnit, r_A0, rSELF);
+    genNullCheck(cUnit, rlSrc.sRegLow, r_A1, mir->offset, NULL);
+    if (isEnter) {
+        /* Get dPC of next insn */
+        loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetWidthFromOpcode(OP_MONITOR_ENTER)));
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+    } else {
+        LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmUnlockObject);
+        /* Do the call */
+        opReg(cUnit, kOpBlx, r_T9);
+        newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+        /* Did we throw? */
+        MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+        loadConstant(cUnit, r_A0,
+                     (int) (cUnit->method->insns + mir->offset +
+                     dexGetWidthFromOpcode(OP_MONITOR_EXIT)));
+        genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+        MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+        target->defMask = ENCODE_ALL;
+        branchOver->generic.target = (LIR *) target;
+        dvmCompilerClobberCallRegs(cUnit);
+    }
+}
+/*#endif*/
+
+/*
+ * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
+ * punt to the interpreter.
+ */
+static void genSuspendPoll(CompilationUnit *cUnit, MIR *mir)
+{
+    int rTemp = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *ld;
+    ld = loadBaseDisp(cUnit, NULL, rSELF,
+                      offsetof(Thread, interpBreak.ctl.breakFlags),
+                      rTemp, kUnsignedByte, INVALID_SREG);
+    setMemRefType(ld, true /* isLoad */, kMustNotAlias);
+    genRegImmCheck(cUnit, kMipsCondNe, rTemp, 0, mir->offset, NULL);
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+                                       BasicBlock *bb, MipsLIR *labelList)
+{
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    int numPredecessors = dvmCountSetBits(bb->taken->predecessors);
+    /*
+     * Things could be hoisted out of the taken block into the predecessor, so
+     * make sure it is dominated by the predecessor.
+     */
+    if (numPredecessors == 1 && bb->taken->visited == false &&
+        bb->taken->blockType == kDalvikByteCode) {
+        cUnit->nextCodegenBlock = bb->taken;
+    } else {
+        /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+        genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+    }
+    return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    if ((dalvikOpcode >= OP_UNUSED_3E) && (dalvikOpcode <= OP_UNUSED_43)) {
+        ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+        return true;
+    }
+    switch (dalvikOpcode) {
+        case OP_RETURN_VOID_BARRIER:
+            dvmCompilerGenMemBarrier(cUnit, kSY);
+            // Intentional fallthrough
+        case OP_RETURN_VOID:
+            genReturnCommon(cUnit,mir);
+            break;
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_FF:
+            ALOGE("Codegen: got unused opcode %#x",dalvikOpcode);
+            return true;
+        case OP_NOP:
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST:
+        case OP_CONST_4: {
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_32: {
+            //TUNING: single routine to load constant pair for support doubles
+            //TUNING: load 0/-1 separately to avoid load dependency
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlDest;
+    RegLocation rlResult;
+    if (mir->ssaRep->numDefs == 2) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_HIGH16: {
+            loadConstantNoClobber(cUnit, rlResult.lowReg,
+                                  mir->dalvikInsn.vB << 16);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_WIDE_HIGH16: {
+            loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg,
+                                  0, mir->dalvikInsn.vB << 16);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+    /* For OP_THROW_VERIFICATION_ERROR */
+    genInterpSingleStep(cUnit, mir);
+    return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlResult;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_STRING: {
+            void *strPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+
+            if (strPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null string");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CONST_CLASS: {
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr );
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET:
+        case OP_SGET_VOLATILE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            bool isVolatile;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            /*
+             * On SMP systems, Dalvik opcodes found to be referencing
+             * volatile fields are rewritten to their _VOLATILE variant.
+             * However, this does not happen on non-SMP systems. The JIT
+             * still needs to know about volatility to avoid unsafe
+             * optimizations so we determine volatility based on either
+             * the opcode or the field access flags.
+             */
+#if ANDROID_SMP != 0
+            Opcode opcode = mir->dalvikInsn.opcode;
+            isVolatile = (opcode == OP_SGET_VOLATILE) ||
+                         (opcode == OP_SGET_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kSY);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SGET_WIDE: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg);
+            HEAP_ACCESS_SHADOW(false);
+
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_SPUT:
+        case OP_SPUT_VOLATILE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT: {
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int objHead = 0;
+            bool isVolatile;
+            bool isSputObject;
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+            Opcode opcode = mir->dalvikInsn.opcode;
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+#if ANDROID_SMP != 0
+            isVolatile = (opcode == OP_SPUT_VOLATILE) ||
+                         (opcode == OP_SPUT_OBJECT_VOLATILE);
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+
+            isSputObject = (opcode == OP_SPUT_OBJECT) ||
+                           (opcode == OP_SPUT_OBJECT_VOLATILE);
+
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr);
+            if (isSputObject) {
+                objHead = dvmCompilerAllocTemp(cUnit);
+                loadWordDisp(cUnit, tReg, OFFSETOF_MEMBER(Field, clazz), objHead);
+            }
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kSY);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit, kSY);
+            }
+            if (isSputObject) {
+                /* NOTE: marking card based sfield->clazz */
+                markCard(cUnit, rlSrc.lowReg, objHead);
+                dvmCompilerFreeTemp(cUnit, objHead);
+            }
+
+            break;
+        }
+        case OP_SPUT_WIDE: {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            int valOffset = OFFSETOF_MEMBER(StaticField, value);
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            void *fieldPtr = (void*)
+              (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            HEAP_ACCESS_SHADOW(true);
+            storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg);
+            HEAP_ACCESS_SHADOW(false);
+            break;
+        }
+        case OP_NEW_INSTANCE: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr = (ClassObject *)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            /*
+             * If it is going to throw, it should not make to the trace to begin
+             * with.  However, Alloc might throw, so we need to genExportPC()
+             */
+            assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmAllocObject);
+            loadConstant(cUnit, r_A0, (int) classPtr);
+            loadConstant(cUnit, r_A1, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_CHECK_CAST: {
+            /*
+             * Obey the calling convention and don't mess with the register
+             * usage.
+             */
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadConstant(cUnit, r_A1, (int) classPtr );
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            MipsLIR *branch1 = opCompareBranch(cUnit, kMipsBeqz, rlSrc.lowReg, -1);
+            /*
+             *  rlSrc.lowReg now contains object->clazz.  Note that
+             *  it could have been allocated r_A0, but we're okay so long
+             *  as we don't do anything desctructive until r_A0 is loaded
+             *  with clazz.
+             */
+            /* r_A0 now contains object->clazz */
+            loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r_A0);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInstanceofNonTrivial);
+            MipsLIR *branch2 = opCompareBranch(cUnit, kMipsBeq, r_A0, r_A1);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /*
+             * If null, check cast failed - punt to the interpreter.  Because
+             * interpreter will be the one throwing, we don't need to
+             * genExportPC() here.
+             */
+            genRegCopy(cUnit, r_A0, r_V0);
+            genZeroCheck(cUnit, r_V0, mir->offset, NULL);
+            /* check cast passed - branch target here */
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlResult;
+    switch (dalvikOpcode) {
+        case OP_MOVE_EXCEPTION: {
+            int exOffset = offsetof(Thread, exception);
+            int resetReg = dvmCompilerAllocTemp(cUnit);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rSELF, exOffset, rlResult.lowReg);
+            loadConstant(cUnit, resetReg, 0);
+            storeWordDisp(cUnit, rSELF, exOffset, resetReg);
+            storeValue(cUnit, rlDest, rlResult);
+           break;
+        }
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_OBJECT: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL;
+            rlSrc.fp = rlDest.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_MOVE_RESULT_WIDE: {
+            /* An inlined move result is effectively no-op */
+            if (mir->OptimizationFlags & MIR_INLINED)
+                break;
+            RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+            RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlSrc.fp = rlDest.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        }
+        case OP_RETURN_WIDE: {
+            RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+            rlDest.fp = rlSrc.fp;
+            storeValueWide(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit,mir);
+            break;
+        }
+        case OP_RETURN:
+        case OP_RETURN_OBJECT: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = LOC_DALVIK_RETURN_VAL;
+            rlDest.fp = rlSrc.fp;
+            storeValue(cUnit, rlDest, rlSrc);
+            genReturnCommon(cUnit, mir);
+            break;
+        }
+        case OP_MONITOR_EXIT:
+        case OP_MONITOR_ENTER:
+            genMonitor(cUnit, mir);
+            break;
+        case OP_THROW:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlSrc;
+    RegLocation rlResult;
+
+    if ( (opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    if (mir->ssaRep->numUses == 2)
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    else
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    if (mir->ssaRep->numDefs == 2)
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    else
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+
+    switch (opcode) {
+        case OP_DOUBLE_TO_INT:
+        case OP_INT_TO_FLOAT:
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_FLOAT:
+        case OP_FLOAT_TO_DOUBLE:
+        case OP_INT_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+        case OP_LONG_TO_DOUBLE:
+            return genConversion(cUnit, mir);
+        case OP_NEG_INT:
+        case OP_NOT_INT:
+            return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_LONG:
+        case OP_NOT_LONG:
+            return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_FLOAT:
+            return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_NEG_DOUBLE:
+            return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc);
+        case OP_MOVE_WIDE:
+            storeValueWide(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_LONG:
+            rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            //TUNING: shouldn't loadValueDirect already check for phys reg?
+            if (rlSrc.location == kLocPhysReg) {
+                genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+            } else {
+                loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
+            }
+            opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+                        rlResult.lowReg, 31);
+            storeValueWide(cUnit, rlDest, rlResult);
+            break;
+        case OP_LONG_TO_INT:
+            rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+            rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc);
+            // Intentional fallthrough
+        case OP_MOVE:
+        case OP_MOVE_OBJECT:
+            storeValue(cUnit, rlDest, rlSrc);
+            break;
+        case OP_INT_TO_BYTE:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_SHORT:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_INT_TO_CHAR:
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        case OP_ARRAY_LENGTH: {
+            int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg,
+                         mir->offset, NULL);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rlSrc.lowReg, lenOffset,
+                         rlResult.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlDest;
+    RegLocation rlResult;
+    int BBBB = mir->dalvikInsn.vB;
+    if (dalvikOpcode == OP_CONST_WIDE_16) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        //TUNING: do high separately to avoid load dependency
+        opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (dalvikOpcode == OP_CONST_16) {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+        loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+        storeValue(cUnit, rlDest, rlResult);
+    } else
+        return true;
+    return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         MipsLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    MipsOpCode opc = kMipsNop;
+    int rt = -1;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQZ:
+            opc = kMipsBeqz;
+            break;
+        case OP_IF_NEZ:
+            opc = kMipsBne;
+            rt = r_ZERO;
+            break;
+        case OP_IF_LTZ:
+            opc = kMipsBltz;
+            break;
+        case OP_IF_GEZ:
+            opc = kMipsBgez;
+            break;
+        case OP_IF_GTZ:
+            opc = kMipsBgtz;
+            break;
+        case OP_IF_LEZ:
+            opc = kMipsBlez;
+            break;
+        default:
+            ALOGE("Unexpected opcode (%d) for Fmt21t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+    genConditionalBranchMips(cUnit, opc, rlSrc.lowReg, rt, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool isPowerOfTwo(int x)
+{
+    return (x & (x - 1)) == 0;
+}
+
+// Returns true if no more than two bits are set in 'x'.
+static bool isPopCountLE2(unsigned int x)
+{
+    x &= x - 1;
+    return (x & (x - 1)) == 0;
+}
+
+// Returns the index of the lowest set bit in 'x'.
+static int lowestSetBit(unsigned int x) {
+    int bit_posn = 0;
+    while ((x & 0xf) == 0) {
+        bit_posn += 4;
+        x >>= 4;
+    }
+    while ((x & 1) == 0) {
+        bit_posn++;
+        x >>= 1;
+    }
+    return bit_posn;
+}
+
+// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyDivide(CompilationUnit *cUnit, Opcode dalvikOpcode,
+                             RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    if (lit < 2 || !isPowerOfTwo(lit)) {
+        return false;
+    }
+    int k = lowestSetBit(lit);
+    if (k >= 30) {
+        // Avoid special cases.
+        return false;
+    }
+    bool div = (dalvikOpcode == OP_DIV_INT_LIT8 || dalvikOpcode == OP_DIV_INT_LIT16);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (div) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            // Division by 2 is by far the most common division by constant.
+            opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+            opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+        }
+    } else {
+        int cReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, cReg, lit - 1);
+        int tReg1 = dvmCompilerAllocTemp(cUnit);
+        int tReg2 = dvmCompilerAllocTemp(cUnit);
+        if (lit == 2) {
+            opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        } else {
+            opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
+            opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
+            opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+            opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+        }
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyMultiply(CompilationUnit *cUnit,
+                               RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+    // Can we simplify this multiplication?
+    bool powerOfTwo = false;
+    bool popCountLE2 = false;
+    bool powerOfTwoMinusOne = false;
+    if (lit < 2) {
+        // Avoid special cases.
+        return false;
+    } else if (isPowerOfTwo(lit)) {
+        powerOfTwo = true;
+    } else if (isPopCountLE2(lit)) {
+        popCountLE2 = true;
+    } else if (isPowerOfTwo(lit + 1)) {
+        powerOfTwoMinusOne = true;
+    } else {
+        return false;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    if (powerOfTwo) {
+        // Shift.
+        opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
+                    lowestSetBit(lit));
+    } else if (popCountLE2) {
+        // Shift and add and shift.
+        int firstBit = lowestSetBit(lit);
+        int secondBit = lowestSetBit(lit ^ (1 << firstBit));
+        genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
+                                      firstBit, secondBit);
+    } else {
+        // Reverse subtract: (src << (shift + 1)) - src.
+        assert(powerOfTwoMinusOne);
+        // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1)
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
+        opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return true;
+}
+
+static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    RegLocation rlResult;
+    int lit = mir->dalvikInsn.vC;
+    OpKind op = (OpKind)0;      /* Make gcc happy */
+    int shiftOp = false;
+
+    switch (dalvikOpcode) {
+        case OP_RSUB_INT_LIT8:
+        case OP_RSUB_INT: {
+            int tReg;
+            //TUNING: add support for use of Arm rsub op
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            tReg = dvmCompilerAllocTemp(cUnit);
+            loadConstant(cUnit, tReg, lit);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+                        tReg, rlSrc.lowReg);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+
+        case OP_ADD_INT_LIT8:
+        case OP_ADD_INT_LIT16:
+            op = kOpAdd;
+            break;
+        case OP_MUL_INT_LIT8:
+        case OP_MUL_INT_LIT16: {
+            if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
+                return false;
+            }
+            op = kOpMul;
+            break;
+        }
+        case OP_AND_INT_LIT8:
+        case OP_AND_INT_LIT16:
+            op = kOpAnd;
+            break;
+        case OP_OR_INT_LIT8:
+        case OP_OR_INT_LIT16:
+            op = kOpOr;
+            break;
+        case OP_XOR_INT_LIT8:
+        case OP_XOR_INT_LIT16:
+            op = kOpXor;
+            break;
+        case OP_SHL_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsl;
+            break;
+        case OP_SHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpAsr;
+            break;
+        case OP_USHR_INT_LIT8:
+            lit &= 31;
+            shiftOp = true;
+            op = kOpLsr;
+            break;
+
+        case OP_DIV_INT_LIT8:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT8:
+        case OP_REM_INT_LIT16: {
+            if (lit == 0) {
+                /* Let the interpreter deal with div by 0 */
+                genInterpSingleStep(cUnit, mir);
+                return false;
+            }
+            if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
+                return false;
+            }
+
+            MipsOpCode opc;
+            int divReg;
+
+            if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
+                (dalvikOpcode == OP_DIV_INT_LIT16)) {
+                opc = kMipsMflo;
+                divReg = r_LO;
+            } else {
+                opc = kMipsMfhi;
+                divReg = r_HI;
+            }
+
+            rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR3(cUnit, kMipsAddiu, tReg, r_ZERO, lit);
+            newLIR4(cUnit, kMipsDiv, r_HI, r_LO, rlSrc.lowReg, tReg);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            newLIR2(cUnit, opc, rlResult.lowReg, divReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            storeValue(cUnit, rlDest, rlResult);
+            return false;
+            break;
+        }
+        default:
+            return true;
+    }
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    // Avoid shifts by literal 0 - no support in Thumb.  Change to copy
+    if (shiftOp && (lit == 0)) {
+        genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    } else {
+        opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset = -1;
+    bool isVolatile = false;
+    switch (dalvikOpcode) {
+        /*
+         * Wide volatiles currently handled via single step.
+         * Add them here if generating in-line code.
+         *     case OP_IGET_WIDE_VOLATILE:
+         *     case OP_IPUT_WIDE_VOLATILE:
+         */
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+#if ANDROID_SMP != 0
+            isVolatile = true;
+        // NOTE: intentional fallthrough
+#endif
+        case OP_IGET:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT: {
+            const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+                mir->meta.calleeMethod : cUnit->method;
+            Field *fieldPtr =
+                method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+
+            if (fieldPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null instance field");
+                dvmAbort();
+            }
+#if ANDROID_SMP != 0
+            assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr));
+#else
+            isVolatile = dvmIsVolatileField((Field *) fieldPtr);
+#endif
+            fieldOffset = ((InstField *)fieldPtr)->byteOffset;
+            break;
+        }
+        default:
+            break;
+    }
+
+    switch (dalvikOpcode) {
+        case OP_NEW_ARRAY: {
+#if 0 /* 080 triggers assert in Interp.c:1290 for out of memory exception.
+             i think the assert is in error and should be disabled. With
+             asserts disabled, 080 passes. */
+genInterpSingleStep(cUnit, mir);
+return false;
+#endif
+            // Generates a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            void *classPtr = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGE("Unexpected null class");
+                dvmAbort();
+            }
+
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r_A1);   /* Len */
+            loadConstant(cUnit, r_A0, (int) classPtr );
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmAllocArrayByClass);
+            /*
+             * "len < 0": bail to the interpreter to re-execute the
+             * instruction
+             */
+            genRegImmCheck(cUnit, kMipsCondMi, r_A1, 0, mir->offset, NULL);
+            loadConstant(cUnit, r_A2, ALLOC_DONT_TRACK);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if allocation is successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            /*
+             * OOM exception needs to be thrown here and cannot re-execute
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            break;
+        }
+        case OP_INSTANCE_OF: {
+            // May generate a call - use explicit registers
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            RegLocation rlResult;
+            ClassObject *classPtr =
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+            /*
+             * Note: It is possible that classPtr is NULL at this point,
+             * even though this instruction has been successfully interpreted.
+             * If the previous interpretation had a null source, the
+             * interpreter would not have bothered to resolve the clazz.
+             * Bail out to the interpreter in this case, and log it
+             * so that we can tell if it happens frequently.
+             */
+            if (classPtr == NULL) {
+                BAIL_LOOP_COMPILATION();
+                ALOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+                genInterpSingleStep(cUnit, mir);
+                break;
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r_V0);  /* Ref */
+            loadConstant(cUnit, r_A2, (int) classPtr );
+            /* When taken r_V0 has NULL which can be used for store directly */
+            MipsLIR *branch1 = opCompareBranch(cUnit, kMipsBeqz, r_V0, -1);
+            /* r_A1 now contains object->clazz */
+            loadWordDisp(cUnit, r_V0, offsetof(Object, clazz), r_A1);
+            /* r_A1 now contains object->clazz */
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInstanceofNonTrivial);
+            loadConstant(cUnit, r_V0, 1);                /* Assume true */
+            MipsLIR *branch2 = opCompareBranch(cUnit, kMipsBeq, r_A1, r_A2);
+            genRegCopy(cUnit, r_A0, r_A1);
+            genRegCopy(cUnit, r_A1, r_A2);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* branch target here */
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            rlResult = dvmCompilerGetReturn(cUnit);
+            storeValue(cUnit, rlDest, rlResult);
+            branch1->generic.target = (LIR *)target;
+            branch2->generic.target = (LIR *)target;
+            break;
+        }
+        case OP_IGET_WIDE:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IGET_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IGET:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
+            break;
+        case OP_IPUT_WIDE:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+            break;
+        case OP_IPUT_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
+            break;
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+            genInterpSingleStep(cUnit, mir);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    int fieldOffset =  mir->dalvikInsn.vC;
+    switch (dalvikOpcode) {
+        case OP_IGET_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+            genIGet(cUnit, mir, kWord, fieldOffset, false);
+            break;
+        case OP_IPUT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, false);
+            break;
+        case OP_IPUT_OBJECT_QUICK:
+            genIPut(cUnit, mir, kWord, fieldOffset, true, false);
+            break;
+        case OP_IGET_WIDE_QUICK:
+            genIGetWide(cUnit, mir, fieldOffset);
+            break;
+        case OP_IPUT_WIDE_QUICK:
+            genIPutWide(cUnit, mir, fieldOffset);
+            break;
+        default:
+            return true;
+    }
+    return false;
+
+}
+
+/* Compare against zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+                         MipsLIR *labelList)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    MipsConditionCode cond;
+    MipsOpCode opc = kMipsNop;
+    MipsLIR * test = NULL;
+    /* backward branch? */
+    bool backwardBranch = (bb->taken->startOffset <= mir->offset);
+
+    if (backwardBranch &&
+        (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) {
+        genSuspendPoll(cUnit, mir);
+    }
+
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+    int reg1 = rlSrc1.lowReg;
+    int reg2 = rlSrc2.lowReg;
+    int tReg;
+
+    switch (dalvikOpcode) {
+        case OP_IF_EQ:
+            opc = kMipsBeq;
+            break;
+        case OP_IF_NE:
+            opc = kMipsBne;
+            break;
+        case OP_IF_LT:
+            opc = kMipsBne;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+            reg1 = tReg;
+            reg2 = r_ZERO;
+            break;
+        case OP_IF_LE:
+            opc = kMipsBeqz;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg2, reg1);
+            reg1 = tReg;
+            reg2 = -1;
+            break;
+        case OP_IF_GT:
+            opc = kMipsBne;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg2, reg1);
+            reg1 = tReg;
+            reg2 = r_ZERO;
+            break;
+        case OP_IF_GE:
+            opc = kMipsBeqz;
+            tReg = dvmCompilerAllocTemp(cUnit);
+            test = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+            reg1 = tReg;
+            reg2 = -1;
+            break;
+        default:
+            cond = (MipsConditionCode)0;
+            ALOGE("Unexpected opcode (%d) for Fmt22t", dalvikOpcode);
+            dvmCompilerAbort(cUnit);
+    }
+
+    genConditionalBranchMips(cUnit, opc, reg1, reg2, &labelList[bb->taken->id]);
+    /* This mostly likely will be optimized away in a later phase */
+    genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+    return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+
+    switch (opcode) {
+        case OP_MOVE_16:
+        case OP_MOVE_OBJECT_16:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_OBJECT_FROM16: {
+            storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0),
+                       dvmCompilerGetSrc(cUnit, mir, 0));
+            break;
+        }
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_WIDE_FROM16: {
+            storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1),
+                           dvmCompilerGetSrcWide(cUnit, mir, 0, 1));
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    RegLocation rlSrc1;
+    RegLocation rlSrc2;
+    RegLocation rlDest;
+
+    if ((opcode >= OP_ADD_INT) && (opcode <= OP_REM_DOUBLE)) {
+        return genArithOp( cUnit, mir );
+    }
+
+    /* APUTs have 3 sources and no targets */
+    if (mir->ssaRep->numDefs == 0) {
+        if (mir->ssaRep->numUses == 3) {
+            rlDest = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+        } else {
+            assert(mir->ssaRep->numUses == 4);
+            rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3);
+        }
+    } else {
+        /* Two sources and 1 dest.  Deduce the operand sizes */
+        if (mir->ssaRep->numUses == 4) {
+            rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+            rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+        } else {
+            assert(mir->ssaRep->numUses == 2);
+            rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+        }
+        if (mir->ssaRep->numDefs == 2) {
+            rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        } else {
+            assert(mir->ssaRep->numDefs == 1);
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        }
+    }
+
+    switch (opcode) {
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+            return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        case OP_CMP_LONG:
+            genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+            break;
+        case OP_AGET_WIDE:
+            genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_AGET:
+        case OP_AGET_OBJECT:
+            genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_AGET_BOOLEAN:
+            genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_BYTE:
+            genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        case OP_AGET_CHAR:
+            genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_AGET_SHORT:
+            genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_WIDE:
+            genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+            break;
+        case OP_APUT:
+            genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_OBJECT:
+            genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2);
+            break;
+        case OP_APUT_SHORT:
+        case OP_APUT_CHAR:
+            genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+            break;
+        case OP_APUT_BYTE:
+        case OP_APUT_BOOLEAN:
+            genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+            break;
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * Find the matching case.
+ *
+ * return values:
+ * r_RESULT0 (low 32-bit): pc of the chaining cell corresponding to the resolved case,
+ *    including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES).
+ * r_RESULT1 (high 32-bit): the branch offset of the matching case (only for indexes
+ *    above MAX_CHAINED_SWITCH_CASES).
+ *
+ * Instructions around the call are:
+ *
+ * jalr &findPackedSwitchIndex
+ * nop
+ * lw gp, 84(sp) |
+ * addu          | 20 bytes for these 5 instructions
+ * move          | (NOTE: if this sequence is shortened or lengthened, then
+ * jr            |  the 20 byte offset added below in 3 places must be changed
+ * nop           |  accordingly.)
+ * chaining cell for case 0 [16 bytes]
+ * chaining cell for case 1 [16 bytes]
+ *               :
+ * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [16 bytes]
+ * chaining cell for case default [16 bytes]
+ * noChain exit
+ */
+static u8 findPackedSwitchIndex(const u2* switchData, int testVal)
+{
+    int size;
+    int firstKey;
+    const int *entries;
+    int index;
+    int jumpIndex;
+    uintptr_t caseDPCOffset = 0;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    size = switchData[1];
+    assert(size > 0);
+
+    firstKey = switchData[2];
+    firstKey |= switchData[3] << 16;
+
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = (const int*) &switchData[4];
+    assert(((u4)entries & 0x3) == 0);
+
+    index = testVal - firstKey;
+
+    /* Jump to the default cell */
+    if (index < 0 || index >= size) {
+        jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES);
+    /* Jump to the non-chaining exit point */
+    } else if (index >= MAX_CHAINED_SWITCH_CASES) {
+        jumpIndex = MAX_CHAINED_SWITCH_CASES + 1;
+#ifdef HAVE_LITTLE_ENDIAN
+        caseDPCOffset = entries[index];
+#else
+        caseDPCOffset = (unsigned int)entries[index] >> 16 | entries[index] << 16;
+#endif
+    /* Jump to the inline chaining cell */
+    } else {
+        jumpIndex = index;
+    }
+
+    return (((u8) caseDPCOffset) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+}
+
+/* See comments for findPackedSwitchIndex */
+static u8 findSparseSwitchIndex(const u2* switchData, int testVal)
+{
+    int size;
+    const int *keys;
+    const int *entries;
+    /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+    int i;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    size = switchData[1];
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const int*) &switchData[2];
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Run through the list of keys, which are guaranteed to
+     * be sorted low-to-high.
+     *
+     * Most tables have 3-4 entries.  Few have more than 10.  A binary
+     * search here is probably not useful.
+     */
+    for (i = 0; i < size; i++) {
+#ifdef HAVE_LITTLE_ENDIAN
+        int k = keys[i];
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            return (((u8) entries[i]) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+#else
+        int k = (unsigned int)keys[i] >> 16 | keys[i] << 16;
+        if (k == testVal) {
+            /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+            int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+                           i : MAX_CHAINED_SWITCH_CASES + 1;
+            int temp = (unsigned int)entries[i] >> 16 | entries[i] << 16;
+            return (((u8) temp) << 32) | (u8) (jumpIndex * CHAIN_CELL_NORMAL_SIZE + 20);
+#endif
+        } else if (k > testVal) {
+            break;
+        }
+    }
+    return MIN(size, MAX_CHAINED_SWITCH_CASES) * CHAIN_CELL_NORMAL_SIZE + 20;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+    switch (dalvikOpcode) {
+        case OP_FILL_ARRAY_DATA: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            // Making a call - use explicit registers
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            genExportPC(cUnit, mir);
+            loadValueDirectFixed(cUnit, rlSrc, r_A0);
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int)dvmInterpHandleFillArrayData);
+            loadConstant(cUnit, r_A1,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if successful */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+            break;
+        }
+        /*
+         * Compute the goto target of up to
+         * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells.
+         * See the comment before findPackedSwitchIndex for the code layout.
+         */
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH: {
+            RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            loadValueDirectFixed(cUnit, rlSrc, r_A1);
+            dvmCompilerLockAllTemps(cUnit);
+
+            if (dalvikOpcode == OP_PACKED_SWITCH) {
+                LOAD_FUNC_ADDR(cUnit, r_T9, (int)findPackedSwitchIndex);
+            } else {
+                LOAD_FUNC_ADDR(cUnit, r_T9, (int)findSparseSwitchIndex);
+            }
+            /* r_A0 <- Addr of the switch data */
+            loadConstant(cUnit, r_A0,
+               (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* pc <- computed goto target using value in RA */
+            newLIR3(cUnit, kMipsAddu, r_A0, r_RA, r_RESULT0);
+            newLIR2(cUnit, kMipsMove, r_A1, r_RESULT1);
+            newLIR1(cUnit, kMipsJr, r_A0);
+            newLIR0(cUnit, kMipsNop); /* for maintaining 20 byte offset */
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * See the example of predicted inlining listed before the
+ * genValidationForPredictedInline function. The function here takes care the
+ * branch over at 0x4858de78 and the misprediction target at 0x4858de7a.
+ */
+static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir,
+                                               BasicBlock *bb,
+                                               MipsLIR *labelList)
+{
+    BasicBlock *fallThrough = bb->fallThrough;
+
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    genUnconditionalBranch(cUnit, &labelList[fallThrough->id]);
+
+    /* Reset the register state */
+    dvmCompilerResetRegPool(cUnit);
+    dvmCompilerClobberAllRegs(cUnit);
+    dvmCompilerResetNullCheck(cUnit);
+
+    /* Target for the slow invoke path */
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    /* Hook up the target to the verification branch */
+    mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir,
+                             BasicBlock *bb, MipsLIR *labelList)
+{
+    MipsLIR *retChainingCell = NULL;
+    MipsLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    if (bb->fallThrough != NULL)
+        retChainingCell = &labelList[bb->fallThrough->id];
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /*
+         * calleeMethod = this->clazz->vtable[
+         *     method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+         * ]
+         */
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_VIRTUAL_RANGE: {
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+            int methodIndex =
+                cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+                methodIndex;
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /*
+         * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+         *                ->pResMethods[BBBB]->methodIndex]
+         */
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_SUPER_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod == cUnit->method->clazz->super->vtable[
+                                     cUnit->method->clazz->pDvmDex->
+                                       pResMethods[dInsn->vB]->methodIndex]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r_A0 = calleeMethod */
+                loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_DIRECT_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r_A0 = calleeMethod */
+            loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_STATIC_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC)
+                genProcessArgsNoRange(cUnit, mir, dInsn,
+                                      NULL /* no null check */);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn,
+                                    NULL /* no null check */);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr,
+                                              retChainingCell);
+            } else {
+                /* r_A0 = calleeMethod */
+                loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+                genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                         calleeMethod);
+            }
+            break;
+        }
+
+        /*
+         * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+         *                    BBBB, method, method->clazz->pDvmDex)
+         *
+         * The following is an example of generated code for
+         *      "invoke-interface v0"
+         *
+         * -------- dalvik offset: 0x000f @ invoke-interface (PI) v2
+         * 0x2f140c54 : lw       a0,8(s1)                    # genProcessArgsNoRange
+         * 0x2f140c58 : addiu    s4,s1,0xffffffe8(-24)
+         * 0x2f140c5c : beqz     a0,0x2f140d5c (L0x11f864)
+         * 0x2f140c60 : pref     1,0(s4)
+         * -------- BARRIER
+         * 0x2f140c64 : sw       a0,0(s4)
+         * 0x2f140c68 : addiu    s4,s4,0x0004(4)
+         * -------- BARRIER
+         * 0x2f140c6c : lui      s0,0x2d23(11555)            # dalvikPC
+         * 0x2f140c70 : ori      s0,s0,0x2d2365a6(757294502)
+         * 0x2f140c74 : lahi/lui a1,0x2f14(12052)            # a1 <- &retChainingCell
+         * 0x2f140c78 : lalo/ori a1,a1,0x2f140d38(789843256)
+         * 0x2f140c7c : lahi/lui a2,0x2f14(12052)            # a2 <- &predictedChainingCell
+         * 0x2f140c80 : lalo/ori a2,a2,0x2f140d80(789843328)
+         * 0x2f140c84 : jal      0x2f1311ec(789778924)       # call TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+         * 0x2f140c88 : nop
+         * 0x2f140c8c : b        0x2f140d80 (L0x11efc0)      # off to the predicted chain
+         * 0x2f140c90 : nop
+         * 0x2f140c94 : b        0x2f140d60 (L0x12457c)      # punt to the interpreter
+         * 0x2f140c98 : lui      a0,0x2d23(11555)
+         * 0x2f140c9c : move     s5,a1                       # prepare for dvmFindInterfaceMethodInCache
+         * 0x2f140ca0 : move     s6,a2
+         * 0x2f140ca4 : move     s7,a3
+         * 0x2f140ca8 : move     a0,a3
+         * 0x2f140cac : ori      a1,zero,0x2b42(11074)
+         * 0x2f140cb0 : lui      a2,0x2c92(11410)
+         * 0x2f140cb4 : ori      a2,a2,0x2c92adf8(747810296)
+         * 0x2f140cb8 : lui      a3,0x0009(9)
+         * 0x2f140cbc : ori      a3,a3,0x924b8(599224)
+         * 0x2f140cc0 : lui      t9,0x2ab2(10930)
+         * 0x2f140cc4 : ori      t9,t9,0x2ab2a48c(716350604)
+         * 0x2f140cc8 : jalr     ra,t9                       # call dvmFindInterfaceMethodInCache
+         * 0x2f140ccc : nop
+         * 0x2f140cd0 : lw       gp,84(sp)
+         * 0x2f140cd4 : move     a0,v0
+         * 0x2f140cd8 : bne      v0,zero,0x2f140cf0 (L0x120064)
+         * 0x2f140cdc : nop
+         * 0x2f140ce0 : lui      a0,0x2d23(11555)            # a0 <- dalvikPC
+         * 0x2f140ce4 : ori      a0,a0,0x2d2365a6(757294502)
+         * 0x2f140ce8 : jal      0x2f131720(789780256)       # call TEMPLATE_THROW_EXCEPTION_COMMON
+         * 0x2f140cec : nop
+         * 0x2f140cf0 : move     a1,s5                       # a1 <- &retChainingCell
+         * 0x2f140cf4 : bgtz     s5,0x2f140d20 (L0x120324)   # >0? don't rechain
+         * 0x2f140cf8 : nop
+         * 0x2f140cfc : lui      t9,0x2aba(10938)            # prepare for dvmJitToPatchPredictedChain
+         * 0x2f140d00 : ori      t9,t9,0x2abae3c4(716891076)
+         * 0x2f140d04 : move     a1,s2
+         * 0x2f140d08 : move     a2,s6
+         * 0x2f140d0c : move     a3,s7
+         * 0x2f140d10 : jalr     ra,t9                       # call dvmJitToPatchPredictedChain
+         * 0x2f140d14 : nop
+         * 0x2f140d18 : lw       gp,84(sp)
+         * 0x2f140d1c : move     a0,v0
+         * 0x2f140d20 : lahi/lui a1,0x2f14(12052)
+         * 0x2f140d24 : lalo/ori a1,a1,0x2f140d38(789843256) # a1 <- &retChainingCell
+         * 0x2f140d28 : jal      0x2f1310c4(789778628)       # call TEMPLATE_INVOKE_METHOD_NO_OPT
+         * 0x2f140d2c : nop
+         * 0x2f140d30 : b        0x2f140d60 (L0x12457c)
+         * 0x2f140d34 : lui      a0,0x2d23(11555)
+         * 0x2f140d38 : .align4
+         * -------- dalvik offset: 0x0012 @ move-result (PI) v1, (#0), (#0)
+         * 0x2f140d38 : lw       a2,16(s2)
+         * 0x2f140d3c : sw       a2,4(s1)
+         * 0x2f140d40 : b        0x2f140d74 (L0x1246fc)
+         * 0x2f140d44 : lw       a0,116(s2)
+         * 0x2f140d48 : undefined
+         * -------- reconstruct dalvik PC : 0x2d2365a6 @ +0x000f
+         * 0x2f140d4c : lui      a0,0x2d23(11555)
+         * 0x2f140d50 : ori      a0,a0,0x2d2365a6(757294502)
+         * 0x2f140d54 : b        0x2f140d68 (L0x12463c)
+         * 0x2f140d58 : lw       a1,108(s2)
+         * -------- reconstruct dalvik PC : 0x2d2365a6 @ +0x000f
+         * 0x2f140d5c : lui      a0,0x2d23(11555)
+         * 0x2f140d60 : ori      a0,a0,0x2d2365a6(757294502)
+         * Exception_Handling:
+         * 0x2f140d64 : lw       a1,108(s2)
+         * 0x2f140d68 : jalr     ra,a1
+         * 0x2f140d6c : nop
+         * 0x2f140d70 : .align4
+         * -------- chaining cell (hot): 0x0013
+         * 0x2f140d70 : lw       a0,116(s2)
+         * 0x2f140d74 : jalr     ra,a0
+         * 0x2f140d78 : nop
+         * 0x2f140d7c : data     0x2d2365ae(757294510)
+         * 0x2f140d80 : .align4
+         * -------- chaining cell (predicted): N/A
+         * 0x2f140d80 : data     0xe7fe(59390)
+         * 0x2f140d84 : data     0x0000(0)
+         * 0x2f140d88 : data     0x0000(0)
+         * 0x2f140d8c : data     0x0000(0)
+         * 0x2f140d90 : data     0x0000(0)
+         * -------- end of chaining cells (0x0190)
+         */
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_INTERFACE_RANGE: {
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* "this" is already left in r_A0 by genProcessArgs* */
+
+            /* r4PC = dalvikCallsite */
+            loadConstant(cUnit, r4PC,
+                         (int) (cUnit->method->insns + mir->offset));
+
+            /* r_A1 = &retChainingCell */
+            MipsLIR *addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+            addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+
+            /* r_A2 = &predictedChainingCell */
+            MipsLIR *predictedChainingCell = newLIR2(cUnit, kMipsLahi, r_A2, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+            predictedChainingCell = newLIR3(cUnit, kMipsLalo, r_A2, r_A2, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF :
+                TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+            /* return through ra - jump to the chaining cell */
+            genUnconditionalBranch(cUnit, predChainingCell);
+
+            /*
+             * null-check on "this" may have been eliminated, but we still need
+             * a PC-reconstruction label for stack overflow bailout.
+             */
+            if (pcrLabel == NULL) {
+                int dPC = (int) (cUnit->method->insns + mir->offset);
+                pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+                pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+                pcrLabel->operands[0] = dPC;
+                pcrLabel->operands[1] = mir->offset;
+                /* Insert the place holder to the growable list */
+                dvmInsertGrowableList(&cUnit->pcReconstructionList,
+                                      (intptr_t) pcrLabel);
+            }
+
+            /* return through ra+8 - punt to the interpreter */
+            genUnconditionalBranch(cUnit, pcrLabel);
+
+            /*
+             * return through ra+16 - fully resolve the callee method.
+             * r_A1 <- count
+             * r_A2 <- &predictedChainCell
+             * r_A3 <- this->class
+             * r4 <- dPC
+             * r_S4 <- this->class->vtable
+             */
+
+            /* Save count, &predictedChainCell, and class to high regs first */
+            genRegCopy(cUnit, r_S5, r_A1);
+            genRegCopy(cUnit, r_S6, r_A2);
+            genRegCopy(cUnit, r_S7, r_A3);
+
+            /* r_A0 now contains this->clazz */
+            genRegCopy(cUnit, r_A0, r_A3);
+
+            /* r_A1 = BBBB */
+            loadConstant(cUnit, r_A1, dInsn->vB);
+
+            /* r_A2 = method (caller) */
+            loadConstant(cUnit, r_A2, (int) cUnit->method);
+
+            /* r_A3 = pDvmDex */
+            loadConstant(cUnit, r_A3, (int) cUnit->method->clazz->pDvmDex);
+
+            LOAD_FUNC_ADDR(cUnit, r_T9,
+                           (intptr_t) dvmFindInterfaceMethodInCache);
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            /* r_V0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+            genRegCopy(cUnit, r_A0, r_V0);
+
+            dvmCompilerClobberCallRegs(cUnit);
+            /* generate a branch over if the interface method is resolved */
+            MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+            /*
+             * calleeMethod == NULL -> throw
+             */
+            loadConstant(cUnit, r_A0,
+                         (int) (cUnit->method->insns + mir->offset));
+            genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+            /* noreturn */
+
+            MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+            target->defMask = ENCODE_ALL;
+            branchOver->generic.target = (LIR *) target;
+
+            genRegCopy(cUnit, r_A1, r_S5);
+
+            /* Check if rechain limit is reached */
+            MipsLIR *bypassRechaining = opCompareBranch(cUnit, kMipsBgtz, r_S5, -1);
+
+            LOAD_FUNC_ADDR(cUnit, r_T9, (int) dvmJitToPatchPredictedChain);
+
+            genRegCopy(cUnit, r_A1, rSELF);
+            genRegCopy(cUnit, r_A2, r_S6);
+            genRegCopy(cUnit, r_A3, r_S7);
+
+            /*
+             * r_A0 = calleeMethod
+             * r_A2 = &predictedChainingCell
+             * r_A3 = class
+             *
+             * &returnChainingCell has been loaded into r_A1 but is not needed
+             * when patching the chaining cell and will be clobbered upon
+             * returning so it will be reconstructed again.
+             */
+            opReg(cUnit, kOpBlx, r_T9);
+            newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+            genRegCopy(cUnit, r_A0, r_V0);
+
+            /* r_A1 = &retChainingCell */
+            addrRetChain = newLIR2(cUnit, kMipsLahi, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+            bypassRechaining->generic.target = (LIR *) addrRetChain;
+            addrRetChain = newLIR3(cUnit, kMipsLalo, r_A1, r_A1, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+
+            /*
+             * r_A0 = this, r_A1 = calleeMethod,
+             * r_A1 = &ChainingCell,
+             * r4PC = callsiteDPC,
+             */
+            genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ?
+                TEMPLATE_INVOKE_METHOD_NO_OPT_PROF :
+                TEMPLATE_INVOKE_METHOD_NO_OPT);
+
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.invokePolymorphic++;
+#endif
+            /* Handle exceptions using the interpreter */
+            genTrap(cUnit, mir->offset, pcrLabel);
+            break;
+        }
+        case OP_INVOKE_OBJECT_INIT_RANGE:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE: {
+            /* Just let the interpreter deal with these */
+            genInterpSingleStep(cUnit, mir);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+                               BasicBlock *bb, MipsLIR *labelList)
+{
+    MipsLIR *pcrLabel = NULL;
+
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (mir->OptimizationFlags & MIR_INLINED)
+        return false;
+
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    switch (mir->dalvikInsn.opcode) {
+        /* calleeMethod = this->clazz->vtable[BBBB] */
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_VIRTUAL_QUICK: {
+            int methodIndex = dInsn->vB;
+            MipsLIR *retChainingCell = &labelList[bb->fallThrough->id];
+            MipsLIR *predChainingCell = &labelList[bb->taken->id];
+
+            /*
+             * If the invoke has non-null misPredBranchOver, we need to generate
+             * the non-inlined version of the invoke here to handle the
+             * mispredicted case.
+             */
+            if (mir->meta.callsiteInfo->misPredBranchOver) {
+                genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+            }
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) {
+                const Method *calleeMethod = mir->meta.callsiteInfo->method;
+                void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns);
+                assert(calleeAddr);
+                genInvokeVirtualWholeMethod(cUnit, mir, calleeAddr,
+                                            retChainingCell);
+            }
+
+            genInvokeVirtualCommon(cUnit, mir, methodIndex,
+                                   retChainingCell,
+                                   predChainingCell,
+                                   pcrLabel);
+            break;
+        }
+        /* calleeMethod = method->clazz->super->vtable[BBBB] */
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE: {
+            /* Grab the method ptr directly from what the interpreter sees */
+            const Method *calleeMethod = mir->meta.callsiteInfo->method;
+            assert(calleeMethod ==
+                   cUnit->method->clazz->super->vtable[dInsn->vB]);
+
+            if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER_QUICK)
+                genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+            else
+                genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+            /* r_A0 = calleeMethod */
+            loadConstant(cUnit, r_A0, (int) calleeMethod);
+
+            genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+                                     calleeMethod);
+            break;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+/*
+ * This operation is complex enough that we'll do it partly inline
+ * and partly with a handler.  NOTE: the handler uses hardcoded
+ * values for string object offsets and must be revisitied if the
+ * layout changes.
+ */
+static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    MipsLIR *rollback;
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r_A0);
+    loadValueDirectFixed(cUnit, rlComp, r_A1);
+    /* Test objects for NULL */
+    rollback = genNullCheck(cUnit, rlThis.sRegLow, r_A0, mir->offset, NULL);
+    genNullCheck(cUnit, rlComp.sRegLow, r_A1, mir->offset, rollback);
+    /*
+     * TUNING: we could check for object pointer equality before invoking
+     * handler. Unclear whether the gain would be worth the added code size
+     * expansion.
+     */
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return handleExecuteInlineC(cUnit, mir);
+#else
+    RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1);
+
+    loadValueDirectFixed(cUnit, rlThis, r_A0);
+    loadValueDirectFixed(cUnit, rlChar, r_A1);
+
+    RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2);
+    loadValueDirectFixed(cUnit, rlStart, r_A2);
+
+    /* Test objects for NULL */
+    genNullCheck(cUnit, rlThis.sRegLow, r_A0, mir->offset, NULL);
+    genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF);
+    storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+               dvmCompilerGetReturn(cUnit));
+    return false;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir,
+                                            bool isEmpty)
+{
+    // dst = src.length();
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
+                 rlResult.lowReg);
+    if (isEmpty) {
+        // dst = (dst == 0);
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        newLIR3(cUnit, kMipsSltu, tReg, r_ZERO, rlResult.lowReg);
+        opRegRegImm(cUnit, kOpXor, rlResult.lowReg, tReg, 1);
+    }
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, false);
+}
+
+static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir)
+{
+    return genInlinedStringIsEmptyOrLength(cUnit, mir, true);
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+    int contents = OFFSETOF_MEMBER(ArrayObject, contents);
+    RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult;
+    rlObj = loadValue(cUnit, rlObj, kCoreReg);
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+    int regMax = dvmCompilerAllocTemp(cUnit);
+    int regOff = dvmCompilerAllocTemp(cUnit);
+    int regPtr = dvmCompilerAllocTemp(cUnit);
+    MipsLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
+                                    mir->offset, NULL);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
+    loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
+    genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
+    dvmCompilerFreeTemp(cUnit, regMax);
+    opRegImm(cUnit, kOpAdd, regPtr, contents);
+    opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2's IT block also yields 3 instructions, but imposes
+     * scheduling constraints.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    int signReg = dvmCompilerAllocTemp(cUnit);
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    /*
+     * abs(x) = y<=x>>31, (x+y)^y.
+     * Thumb2 IT block allows slightly shorter sequence,
+     * but introduces a scheduling barrier.  Stick with this
+     * mechanism for now.
+     */
+    opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+    opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+    newLIR3(cUnit, kMipsSltu, tReg, rlResult.lowReg, signReg);
+    opRegRegReg(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg, signReg);
+    opRegRegReg(cUnit, kOpAdd, rlResult.highReg, rlResult.highReg, tReg);
+    opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+    opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+    dvmCompilerFreeTemp(cUnit, signReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+    storeValue(cUnit, rlDest, rlSrc);
+    return false;
+}
+
+static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    // Just move from source to destination...
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+    storeValueWide(cUnit, rlDest, rlSrc);
+    return false;
+}
+/*
+ * JITs a call to a C function.
+ * TODO: use this for faster native method invocation for simple native
+ * methods (http://b/3069458).
+ */
+static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int operation = dInsn->vB;
+    unsigned int i;
+    const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+    uintptr_t fn = (int) inLineTable[operation].func;
+    if (fn == 0) {
+        dvmCompilerAbort(cUnit);
+    }
+    dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r4PC);
+    dvmCompilerClobber(cUnit, rINST);
+    int offset = offsetof(Thread, interpSave.retval);
+    opRegRegImm(cUnit, kOpAdd, r4PC, rSELF, offset);
+    newLIR3(cUnit, kMipsSw, r4PC, 16, r_SP); /* sp has plenty of space */
+    genExportPC(cUnit, mir);
+    assert(dInsn->vA <= 4);
+    for (i=0; i < dInsn->vA; i++) {
+        loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i+r_A0);
+    }
+    LOAD_FUNC_ADDR(cUnit, r_T9, fn);
+    opReg(cUnit, kOpBlx, r_T9);
+    newLIR3(cUnit, kMipsLw, r_GP, STACK_OFFSET_GP, r_SP);
+    /* NULL? */
+    MipsLIR *branchOver = opCompareBranch(cUnit, kMipsBne, r_V0, r_ZERO);
+    loadConstant(cUnit, r_A0, (int) (cUnit->method->insns + mir->offset));
+    genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+    MipsLIR *target = newLIR0(cUnit, kMipsPseudoTargetLabel);
+    target->defMask = ENCODE_ALL;
+    branchOver->generic.target = (LIR *) target;
+    return false;
+}
+
+/*
+ * NOTE: Handles both range and non-range versions (arguments
+ * have already been normalized by this point).
+ */
+static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    assert(dInsn->opcode == OP_EXECUTE_INLINE_RANGE ||
+           dInsn->opcode == OP_EXECUTE_INLINE);
+    switch (dInsn->vB) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return false;  /* Nop */
+
+        /* These ones we potentially JIT inline. */
+
+        case INLINE_STRING_CHARAT:
+            return genInlinedStringCharAt(cUnit, mir);
+        case INLINE_STRING_LENGTH:
+            return genInlinedStringLength(cUnit, mir);
+        case INLINE_STRING_IS_EMPTY:
+            return genInlinedStringIsEmpty(cUnit, mir);
+        case INLINE_STRING_COMPARETO:
+            return genInlinedCompareTo(cUnit, mir);
+        case INLINE_STRING_FASTINDEXOF_II:
+            return genInlinedFastIndexOf(cUnit, mir);
+
+        case INLINE_MATH_ABS_INT:
+        case INLINE_STRICT_MATH_ABS_INT:
+            return genInlinedAbsInt(cUnit, mir);
+        case INLINE_MATH_ABS_LONG:
+        case INLINE_STRICT_MATH_ABS_LONG:
+            return genInlinedAbsLong(cUnit, mir);
+        case INLINE_MATH_MIN_INT:
+        case INLINE_STRICT_MATH_MIN_INT:
+            return genInlinedMinMaxInt(cUnit, mir, true);
+        case INLINE_MATH_MAX_INT:
+        case INLINE_STRICT_MATH_MAX_INT:
+            return genInlinedMinMaxInt(cUnit, mir, false);
+        case INLINE_MATH_SQRT:
+        case INLINE_STRICT_MATH_SQRT:
+            return genInlineSqrt(cUnit, mir);
+        case INLINE_MATH_ABS_FLOAT:
+        case INLINE_STRICT_MATH_ABS_FLOAT:
+            return genInlinedAbsFloat(cUnit, mir);
+        case INLINE_MATH_ABS_DOUBLE:
+        case INLINE_STRICT_MATH_ABS_DOUBLE:
+            return genInlinedAbsDouble(cUnit, mir);
+
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+        case INLINE_INT_BITS_TO_FLOAT:
+            return genInlinedIntFloatConversion(cUnit, mir);
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            return genInlinedLongDoubleConversion(cUnit, mir);
+
+        /*
+         * These ones we just JIT a call to a C function for.
+         * TODO: special-case these in the other "invoke" call paths.
+         */
+        case INLINE_STRING_EQUALS:
+        case INLINE_MATH_COS:
+        case INLINE_MATH_SIN:
+        case INLINE_FLOAT_TO_INT_BITS:
+        case INLINE_DOUBLE_TO_LONG_BITS:
+            return handleExecuteInlineC(cUnit, mir);
+    }
+    dvmCompilerAbort(cUnit);
+    return false; // Not reachable; keeps compiler happy.
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+    //TUNING: We're using core regs here - not optimal when target is a double
+    RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    loadConstantNoClobber(cUnit, rlResult.lowReg,
+                          mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+    loadConstantNoClobber(cUnit, rlResult.highReg,
+                          (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/* Chaining cell for code that may need warmup. */
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpTraceSelect),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                             unsigned int offset)
+{
+    /*
+     * Use raw instruction constructors to guarantee that the generated
+     * instructions fit the predefined cell size.
+     */
+#if defined(WITH_SELF_VERIFICATION)
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpBackwardBranch),
+        rSELF);
+#else
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal),
+        rSELF);
+#endif
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee)
+{
+    newLIR3(cUnit, kMipsLw, r_A0,
+        offsetof(Thread, jitToInterpEntries.dvmJitToInterpTraceSelect),
+        rSELF);
+    newLIR2(cUnit, kMipsJalr, r_RA, r_A0);
+    addWordData(cUnit, NULL, (int) (callee->insns));
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+    /* Should not be executed in the initial state */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_BX_PAIR_INIT);
+    /* branch delay slot nop */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_DELAY_SLOT_INIT);
+    /* To be filled: class */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_CLAZZ_INIT);
+    /* To be filled: method */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_METHOD_INIT);
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    addWordData(cUnit, NULL, PREDICTED_CHAIN_COUNTER_INIT);
+}
+
+/* Load the Dalvik PC into a0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   MipsLIR *targetLabel)
+{
+    MipsLIR **pcrLabel =
+        (MipsLIR **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+
+    /*
+     * We should never reach here through fall-through code, so insert
+     * a bomb to signal troubles immediately.
+     */
+    if (numElems) {
+        newLIR0(cUnit, kMipsUndefined);
+    }
+
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* a0 = dalvik PC */
+        loadConstant(cUnit, r_A0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+}
+
+static const char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = {
+    "kMirOpPhi",
+    "kMirOpNullNRangeUpCheck",
+    "kMirOpNullNRangeDownCheck",
+    "kMirOpLowerBound",
+    "kMirOpPunt",
+    "kMirOpCheckInlinePrediction",
+};
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int maxC = dInsn->arg[0];
+    int regLength;
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg);
+    genRegImmCheck(cUnit, kMipsCondEq, rlArray.lowReg, 0, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    regLength = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta);
+        rlIdxEnd.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+    /* Punt if "regIdxEnd < len(Array)" is false */
+    genRegRegCheck(cUnit, kMipsCondGe, rlIdxEnd.lowReg, regLength, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length);
+    const int regLength = dvmCompilerAllocTemp(cUnit);
+    const int maxC = dInsn->arg[0];
+    RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+    RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB];
+
+    /* regArray <- arrayRef */
+    rlArray = loadValue(cUnit, rlArray, kCoreReg);
+    rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg);
+    genRegImmCheck(cUnit, kMipsCondEq, rlArray.lowReg, 0, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+
+    /* regLength <- len(arrayRef) */
+    loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+    if (maxC) {
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC);
+        rlIdxInit.lowReg = tReg;
+        dvmCompilerFreeTemp(cUnit, tReg);
+    }
+
+    /* Punt if "regIdxInit < len(Array)" is false */
+    genRegRegCheck(cUnit, kMipsCondGe, rlIdxInit.lowReg, regLength, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA];
+
+    /* regIdx <- initial index value */
+    rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+
+    /* Punt if "regIdxInit + minC >= 0" is false */
+    genRegImmCheck(cUnit, kMipsCondLt, rlIdx.lowReg, -minC, 0,
+                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vC = this
+ *
+ * A predicted inlining target looks like the following, where instructions
+ * between 0x2f130d24 and 0x2f130d40 are checking if the predicted class
+ * matches "this", and the verificaion code is generated by this routine.
+ *
+ * (C) means the instruction is inlined from the callee, and (PI) means the
+ * instruction is the predicted inlined invoke, whose corresponding
+ * instructions are still generated to handle the mispredicted case.
+ *
+ * D/dalvikvm( 2377): -------- kMirOpCheckInlinePrediction
+ * D/dalvikvm( 2377): 0x2f130d24 (0020):  lw       v0,16(s1)
+ * D/dalvikvm( 2377): 0x2f130d28 (0024):  lui      v1,0x0011(17)
+ * D/dalvikvm( 2377): 0x2f130d2c (0028):  ori      v1,v1,0x11e418(1172504)
+ * D/dalvikvm( 2377): 0x2f130d30 (002c):  beqz     v0,0x2f130df0 (L0x11f1f0)
+ * D/dalvikvm( 2377): 0x2f130d34 (0030):  pref     0,0(v0)
+ * D/dalvikvm( 2377): 0x2f130d38 (0034):  lw       a0,0(v0)
+ * D/dalvikvm( 2377): 0x2f130d3c (0038):  bne      v1,a0,0x2f130d54 (L0x11f518)
+ * D/dalvikvm( 2377): 0x2f130d40 (003c):  pref     0,8(v0)
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000a @ +iget-object-quick (C) v3, v4, (#8)
+ * D/dalvikvm( 2377): 0x2f130d44 (0040):  lw       a1,8(v0)
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000a @ +invoke-virtual-quick (PI) v4
+ * D/dalvikvm( 2377): 0x2f130d48 (0044):  sw       a1,12(s1)
+ * D/dalvikvm( 2377): 0x2f130d4c (0048):  b        0x2f130e18 (L0x120150)
+ * D/dalvikvm( 2377): 0x2f130d50 (004c):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): L0x11f518:
+ * D/dalvikvm( 2377): 0x2f130d54 (0050):  lw       a0,16(s1)
+ * D/dalvikvm( 2377): 0x2f130d58 (0054):  addiu    s4,s1,0xffffffe8(-24)
+ * D/dalvikvm( 2377): 0x2f130d5c (0058):  beqz     a0,0x2f130e00 (L0x11f618)
+ * D/dalvikvm( 2377): 0x2f130d60 (005c):  pref     1,0(s4)
+ * D/dalvikvm( 2377): -------- BARRIER
+ * D/dalvikvm( 2377): 0x2f130d64 (0060):  sw       a0,0(s4)
+ * D/dalvikvm( 2377): 0x2f130d68 (0064):  addiu    s4,s4,0x0004(4)
+ * D/dalvikvm( 2377): -------- BARRIER
+ * D/dalvikvm( 2377): 0x2f130d6c (0068):  lui      s0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130d70 (006c):  ori      s0,s0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): 0x2f130d74 (0070):  lahi/lui a1,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130d78 (0074):  lalo/ori a1,a1,0x2f130ddc(789777884)
+ * D/dalvikvm( 2377): 0x2f130d7c (0078):  lahi/lui a2,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130d80 (007c):  lalo/ori a2,a2,0x2f130e24(789777956)
+ * D/dalvikvm( 2377): 0x2f130d84 (0080):  jal      0x2f12d1ec(789762540)
+ * D/dalvikvm( 2377): 0x2f130d88 (0084):  nop
+ * D/dalvikvm( 2377): 0x2f130d8c (0088):  b        0x2f130e24 (L0x11ed6c)
+ * D/dalvikvm( 2377): 0x2f130d90 (008c):  nop
+ * D/dalvikvm( 2377): 0x2f130d94 (0090):  b        0x2f130e04 (L0x11ffd0)
+ * D/dalvikvm( 2377): 0x2f130d98 (0094):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130d9c (0098):  lw       a0,44(s4)
+ * D/dalvikvm( 2377): 0x2f130da0 (009c):  bgtz     a1,0x2f130dc4 (L0x11fb98)
+ * D/dalvikvm( 2377): 0x2f130da4 (00a0):  nop
+ * D/dalvikvm( 2377): 0x2f130da8 (00a4):  lui      t9,0x2aba(10938)
+ * D/dalvikvm( 2377): 0x2f130dac (00a8):  ori      t9,t9,0x2abae3f8(716891128)
+ * D/dalvikvm( 2377): 0x2f130db0 (00ac):  move     a1,s2
+ * D/dalvikvm( 2377): 0x2f130db4 (00b0):  jalr     ra,t9
+ * D/dalvikvm( 2377): 0x2f130db8 (00b4):  nop
+ * D/dalvikvm( 2377): 0x2f130dbc (00b8):  lw       gp,84(sp)
+ * D/dalvikvm( 2377): 0x2f130dc0 (00bc):  move     a0,v0
+ * D/dalvikvm( 2377): 0x2f130dc4 (00c0):  lahi/lui a1,0x2f13(12051)
+ * D/dalvikvm( 2377): 0x2f130dc8 (00c4):  lalo/ori a1,a1,0x2f130ddc(789777884)
+ * D/dalvikvm( 2377): 0x2f130dcc (00c8):  jal      0x2f12d0c4(789762244)
+ * D/dalvikvm( 2377): 0x2f130dd0 (00cc):  nop
+ * D/dalvikvm( 2377): 0x2f130dd4 (00d0):  b        0x2f130e04 (L0x11ffd0)
+ * D/dalvikvm( 2377): 0x2f130dd8 (00d4):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130ddc (00d8): .align4
+ * D/dalvikvm( 2377): L0x11ed2c:
+ * D/dalvikvm( 2377): -------- dalvik offset: 0x000d @ move-result-object (PI) v3, (#0), (#0)
+ * D/dalvikvm( 2377): 0x2f130ddc (00d8):  lw       a2,16(s2)
+ * D/dalvikvm( 2377): 0x2f130de0 (00dc):  sw       a2,12(s1)
+ * D/dalvikvm( 2377): 0x2f130de4 (00e0):  b        0x2f130e18 (L0x120150)
+ * D/dalvikvm( 2377): 0x2f130de8 (00e4):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): 0x2f130dec (00e8):  undefined
+ * D/dalvikvm( 2377): L0x11f1f0:
+ * D/dalvikvm( 2377): -------- reconstruct dalvik PC : 0x2d228464 @ +0x000a
+ * D/dalvikvm( 2377): 0x2f130df0 (00ec):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130df4 (00f0):  ori      a0,a0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): 0x2f130df8 (00f4):  b        0x2f130e0c (L0x120090)
+ * D/dalvikvm( 2377): 0x2f130dfc (00f8):  lw       a1,108(s2)
+ * D/dalvikvm( 2377): L0x11f618:
+ * D/dalvikvm( 2377): -------- reconstruct dalvik PC : 0x2d228464 @ +0x000a
+ * D/dalvikvm( 2377): 0x2f130e00 (00fc):  lui      a0,0x2d22(11554)
+ * D/dalvikvm( 2377): 0x2f130e04 (0100):  ori      a0,a0,0x2d228464(757236836)
+ * D/dalvikvm( 2377): Exception_Handling:
+ * D/dalvikvm( 2377): 0x2f130e08 (0104):  lw       a1,108(s2)
+ * D/dalvikvm( 2377): 0x2f130e0c (0108):  jalr     ra,a1
+ * D/dalvikvm( 2377): 0x2f130e10 (010c):  nop
+ * D/dalvikvm( 2377): 0x2f130e14 (0110): .align4
+ * D/dalvikvm( 2377): L0x11edac:
+ * D/dalvikvm( 2377): -------- chaining cell (hot): 0x000e
+ * D/dalvikvm( 2377): 0x2f130e14 (0110):  lw       a0,116(s2)
+ * D/dalvikvm( 2377): 0x2f130e18 (0114):  jalr     ra,a0
+ * D/dalvikvm( 2377): 0x2f130e1c (0118):  nop
+ * D/dalvikvm( 2377): 0x2f130e20 (011c):  data     0x2d22846c(757236844)
+ * D/dalvikvm( 2377): 0x2f130e24 (0120): .align4
+ * D/dalvikvm( 2377): L0x11ed6c:
+ * D/dalvikvm( 2377): -------- chaining cell (predicted)
+ * D/dalvikvm( 2377): 0x2f130e24 (0120):  data     0xe7fe(59390)
+ * D/dalvikvm( 2377): 0x2f130e28 (0124):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e2c (0128):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e30 (012c):  data     0x0000(0)
+ * D/dalvikvm( 2377): 0x2f130e34 (0130):  data     0x0000(0)
+ */
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC];
+
+    rlThis = loadValue(cUnit, rlThis, kCoreReg);
+    int regPredictedClass = dvmCompilerAllocTemp(cUnit);
+    loadClassPointer(cUnit, regPredictedClass, (int) callsiteInfo);
+    genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset,
+                 NULL);/* null object? */
+    int regActualClass = dvmCompilerAllocTemp(cUnit);
+    loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass);
+//    opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass);
+    /*
+     * Set the misPredBranchOver target so that it will be generated when the
+     * code for the non-optimized invoke is generated.
+     */
+    callsiteInfo->misPredBranchOver = (LIR *) opCompareBranch(cUnit, kMipsBne, regPredictedClass, regActualClass);
+}
+
+/* Extended MIR instructions like PHI */
+static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    int opOffset = mir->dalvikInsn.opcode - kMirOpFirst;
+    char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+                                       false);
+    strcpy(msg, extendedMIROpNames[opOffset]);
+    newLIR1(cUnit, kMipsPseudoExtended, (int) msg);
+
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+            newLIR1(cUnit, kMipsPseudoSSARep, (int) ssaString);
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            genUnconditionalBranch(cUnit,
+                                   (MipsLIR *) cUnit->loopAnalysis->branchToPCR);
+            break;
+        }
+        case kMirOpCheckInlinePrediction: {
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+/*
+ * Create a PC-reconstruction cell for the starting offset of this trace.
+ * Since the PCR cell is placed near the end of the compiled code which is
+ * usually out of range for a conditional branch, we put two branches (one
+ * branch over to the loop body and one layover branch to the actual PCR) at the
+ * end of the entry block.
+ */
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                MipsLIR *bodyLabel)
+{
+    /* Set up the place holder to reconstruct this Dalvik PC */
+    MipsLIR *pcrLabel = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pcrLabel->opcode = kMipsPseudoPCReconstructionCell;
+    pcrLabel->operands[0] =
+        (int) (cUnit->method->insns + entry->startOffset);
+    pcrLabel->operands[1] = entry->startOffset;
+    /* Insert the place holder to the growable list */
+    dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
+
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    MipsLIR *branchToBody = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    branchToBody->opcode = kMipsB;
+    branchToBody->generic.target = (LIR *) bodyLabel;
+    setupResourceMasks(branchToBody);
+    cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+    MipsLIR *branchToPCR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    branchToPCR->opcode = kMipsB;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static bool selfVerificationPuntOps(MIR *mir)
+{
+assert(0); /* MIPSTODO port selfVerificationPuntOps() */
+    DecodedInstruction *decInsn = &mir->dalvikInsn;
+
+    /*
+     * All opcodes that can throw exceptions and use the
+     * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+     * under self-verification mode.
+     */
+    switch (decInsn->opcode) {
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_CHECK_CAST:
+        case OP_MOVE_EXCEPTION:
+        case OP_FILL_ARRAY_DATA:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+            return true;
+        default:
+            return false;
+    }
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+    /* Used to hold the labels of each block */
+    MipsLIR *labelList =
+        (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR) * cUnit->numBlocks, true);
+    MipsLIR *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellGap];
+    int i;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellGap; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].operands[0] = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellGap) {
+            if (bb->isFallThroughFromInvoke == true) {
+                /* Align this block first since it is a return chaining cell */
+                newLIR0(cUnit, kMipsPseudoPseudoAlign4);
+            }
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].opcode = kMipsPseudoEntryBlock;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb,
+                                  &labelList[bb->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].opcode = kMipsPseudoExitBlock;
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].opcode = kMipsPseudoNormalBlockLabel;
+            /* Reset the register state */
+            dvmCompilerResetRegPool(cUnit);
+            dvmCompilerClobberAllRegs(cUnit);
+            dvmCompilerResetNullCheck(cUnit);
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].opcode = kMipsPseudoChainingCellNormal;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellInvokeSingleton;
+                    labelList[i].operands[0] =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellInvokePredicted;
+                    /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    labelList[i].operands[0] = labelList[i].operands[1];
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellHot;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].opcode =
+                        kMipsPseudoPCReconstructionBlockLabel;
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].opcode = kMipsPseudoEHBlockLabel;
+                    if (cUnit->pcReconstructionList.numUsed) {
+                        loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                                     jitToInterpEntries.dvmJitToInterpPunt),
+                                     r_A1);
+                        opReg(cUnit, kOpBlx, r_A1);
+                    }
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].opcode =
+                        kMipsPseudoChainingCellBackwardBranch;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+
+        /*
+         * Try to build a longer optimization unit. Currently if the previous
+         * block ends with a goto, we continue adding instructions and don't
+         * reset the register allocation pool.
+         */
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+            for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+
+                dvmCompilerResetRegPool(cUnit);
+                if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+                    dvmCompilerClobberAllRegs(cUnit);
+                }
+
+                if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+                    dvmCompilerResetDefTracking(cUnit);
+                }
+
+                if ((int)mir->dalvikInsn.opcode >= (int)kMirOpFirst) {
+                    handleExtendedMIR(cUnit, mir);
+                    continue;
+                }
+
+                Opcode dalvikOpcode = mir->dalvikInsn.opcode;
+                InstructionFormat dalvikFormat =
+                    dexGetFormatFromOpcode(dalvikOpcode);
+                const char *note;
+                if (mir->OptimizationFlags & MIR_INLINED) {
+                    note = " (I)";
+                } else if (mir->OptimizationFlags & MIR_INLINED_PRED) {
+                    note = " (PI)";
+                } else if (mir->OptimizationFlags & MIR_CALLEE) {
+                    note = " (C)";
+                } else {
+                    note = NULL;
+                }
+
+                MipsLIR *boundaryLIR =
+                    newLIR2(cUnit, kMipsPseudoDalvikByteCodeBoundary,
+                            mir->offset,
+                            (int) dvmCompilerGetDalvikDisassembly(&mir->dalvikInsn,
+                                                                  note));
+                if (mir->ssaRep) {
+                    char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+                    newLIR1(cUnit, kMipsPseudoSSARep, (int) ssaString);
+                }
+
+                /* Remember the first LIR for this block */
+                if (headLIR == NULL) {
+                    headLIR = boundaryLIR;
+                    /* Set the first boundaryLIR as a scheduling barrier */
+                    headLIR->defMask = ENCODE_ALL;
+                }
+
+                bool notHandled;
+                /*
+                 * Debugging: screen the opcode first to see if it is in the
+                 * do[-not]-compile list
+                 */
+                bool singleStepMe = SINGLE_STEP_OP(dalvikOpcode);
+#if defined(WITH_SELF_VERIFICATION)
+              if (singleStepMe == false) {
+                  singleStepMe = selfVerificationPuntOps(mir);
+              }
+#endif
+                if (singleStepMe || cUnit->allSingleStep) {
+                    notHandled = false;
+                    genInterpSingleStep(cUnit, mir);
+                } else {
+                    opcodeCoverage[dalvikOpcode]++;
+                    switch (dalvikFormat) {
+                        case kFmt10t:
+                        case kFmt20t:
+                        case kFmt30t:
+                            notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+                                      mir, bb, labelList);
+                            break;
+                        case kFmt10x:
+                            notHandled = handleFmt10x(cUnit, mir);
+                            break;
+                        case kFmt11n:
+                        case kFmt31i:
+                            notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+                            break;
+                        case kFmt11x:
+                            notHandled = handleFmt11x(cUnit, mir);
+                            break;
+                        case kFmt12x:
+                            notHandled = handleFmt12x(cUnit, mir);
+                            break;
+                        case kFmt20bc:
+                            notHandled = handleFmt20bc(cUnit, mir);
+                            break;
+                        case kFmt21c:
+                        case kFmt31c:
+                            notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+                            break;
+                        case kFmt21h:
+                            notHandled = handleFmt21h(cUnit, mir);
+                            break;
+                        case kFmt21s:
+                            notHandled = handleFmt21s(cUnit, mir);
+                            break;
+                        case kFmt21t:
+                            notHandled = handleFmt21t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22b:
+                        case kFmt22s:
+                            notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+                            break;
+                        case kFmt22c:
+                            notHandled = handleFmt22c(cUnit, mir);
+                            break;
+                        case kFmt22cs:
+                            notHandled = handleFmt22cs(cUnit, mir);
+                            break;
+                        case kFmt22t:
+                            notHandled = handleFmt22t(cUnit, mir, bb,
+                                                      labelList);
+                            break;
+                        case kFmt22x:
+                        case kFmt32x:
+                            notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+                            break;
+                        case kFmt23x:
+                            notHandled = handleFmt23x(cUnit, mir);
+                            break;
+                        case kFmt31t:
+                            notHandled = handleFmt31t(cUnit, mir);
+                            break;
+                        case kFmt3rc:
+                        case kFmt35c:
+                            notHandled = handleFmt35c_3rc(cUnit, mir, bb,
+                                                          labelList);
+                            break;
+                        case kFmt3rms:
+                        case kFmt35ms:
+                            notHandled = handleFmt35ms_3rms(cUnit, mir,bb,
+                                                            labelList);
+                            break;
+                        case kFmt35mi:
+                        case kFmt3rmi:
+                            notHandled = handleExecuteInline(cUnit, mir);
+                            break;
+                        case kFmt51l:
+                            notHandled = handleFmt51l(cUnit, mir);
+                            break;
+                        default:
+                            notHandled = true;
+                            break;
+                    }
+                }
+                if (notHandled) {
+                    ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
+                         mir->offset,
+                         dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
+                         dalvikFormat);
+                    dvmCompilerAbort(cUnit);
+                    break;
+                }
+            }
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToBody);
+            dvmCompilerAppendLIR(cUnit,
+                                 (LIR *) cUnit->loopAnalysis->branchToPCR);
+        }
+
+        if (headLIR) {
+            /*
+             * Eliminate redundant loads/stores and delay stores into later
+             * slots
+             */
+            dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                               cUnit->lastLIRInsn);
+            /* Reset headLIR which is also the optimization boundary */
+            headLIR = NULL;
+        }
+
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+        }
+    }
+
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            /* Align this chaining cell first */
+            newLIR0(cUnit, kMipsPseudoPseudoAlign4);
+
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit, chainingBlock->startOffset);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmCompilerAbort(cUnit);
+            }
+        }
+    }
+
+    /* Mark the bottom of chaining cells */
+    cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kMipsChainingCellBottom);
+
+    /*
+     * Generate the branch to the dvmJitToInterpNoChain entry point at the end
+     * of all chaining cells for the overflow cases.
+     */
+    if (cUnit->switchOverflowPad) {
+        loadConstant(cUnit, r_A0, (int) cUnit->switchOverflowPad);
+        loadWordDisp(cUnit, rSELF, offsetof(Thread,
+                     jitToInterpEntries.dvmJitToInterpNoChain), r_A2);
+        opRegReg(cUnit, kOpAdd, r_A1, r_A1);
+        opRegRegReg(cUnit, kOpAdd, r4PC, r_A0, r_A1);
+#if defined(WITH_JIT_TUNING)
+        loadConstant(cUnit, r_A0, kSwitchOverflow);
+#endif
+        opReg(cUnit, kOpBlx, r_A2);
+    }
+
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+
+#if defined(WITH_SELF_VERIFICATION)
+    selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+    /* Print compiled opcode in this VM instance */
+    int i, start, streak;
+    char buf[1024];
+
+    streak = i = 0;
+    buf[0] = 0;
+    while (opcodeCoverage[i] == 0 && i < 256) {
+        i++;
+    }
+    if (i == 256) {
+        return;
+    }
+    for (start = i++, streak = 1; i < 256; i++) {
+        if (opcodeCoverage[i]) {
+            streak++;
+        } else {
+            if (streak == 1) {
+                sprintf(buf+strlen(buf), "%x,", start);
+            } else {
+                sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+            }
+            streak = 0;
+            while (opcodeCoverage[i] == 0 && i < 256) {
+                i++;
+            }
+            if (i < 256) {
+                streak = 1;
+                start = i;
+            }
+        }
+    }
+    if (streak) {
+        if (streak == 1) {
+            sprintf(buf+strlen(buf), "%x", start);
+        } else {
+            sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+        }
+    }
+    if (strlen(buf)) {
+        ALOGD("dalvik.vm.jit.op = %s", buf);
+    }
+}
+
+/* Common initialization routine for an architecture family */
+bool dvmCompilerArchInit()
+{
+    int i;
+
+    for (i = 0; i < kMipsLast; i++) {
+        if (EncodingMap[i].opcode != i) {
+            ALOGE("Encoding order for %s is wrong: expecting %d, seeing %d",
+                 EncodingMap[i].name, i, EncodingMap[i].opcode);
+            dvmAbort();  // OK to dvmAbort - build error
+        }
+    }
+
+    return dvmCompilerArchVariantInit();
+}
+
+void *dvmCompilerGetInterpretTemplate()
+{
+      return (void*) ((int)gDvmJit.codeCache +
+                      templateEntryOffsets[TEMPLATE_INTERPRET]);
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_MIPS;
+}
+
+/* Needed by the Assembler */
+void dvmCompilerSetupResourceMasks(MipsLIR *lir)
+{
+    setupResourceMasks(lir);
+}
+
+/* Needed by the ld/st optmizatons */
+MipsLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopyNoInsert(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+MipsLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    return genRegCopy(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                            int srcLo, int srcHi)
+{
+    genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi);
+}
+
+void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    storeBaseDisp(cUnit, rBase, displacement, rSrc, size);
+}
+
+void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi);
+}
diff --git a/vm/compiler/codegen/mips/CodegenFactory.cpp b/vm/compiler/codegen/mips/CodegenFactory.cpp
new file mode 100644
index 0000000..a1211cc
--- /dev/null
+++ b/vm/compiler/codegen/mips/CodegenFactory.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * Mips variants.  It is included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+
+/* Load a word at base + displacement.  Displacement must be word multiple */
+static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest)
+{
+    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+                        INVALID_SREG);
+}
+
+static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc)
+{
+    return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register.  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int reg1)
+{
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopy(cUnit, reg1, rlSrc.lowReg);
+    } else  if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+        loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                     reg1);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * register.  Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 int reg1)
+{
+    dvmCompilerClobber(cUnit, reg1);
+    dvmCompilerMarkInUse(cUnit, reg1);
+    loadValueDirect(cUnit, rlSrc, reg1);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s].  Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness.  That is the responsibility of the caller.
+ */
+static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                int regLo, int regHi)
+{
+    rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+    if (rlSrc.location == kLocPhysReg) {
+        genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
+                         regLo, regHi, INVALID_SREG);
+    } else {
+        assert(rlSrc.location == kLocDalvikFrame);
+            loadBaseDispWide(cUnit, NULL, rFP,
+                             dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+                             regLo, regHi, INVALID_SREG);
+    }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * registers.  Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+                                     int regLo, int regHi)
+{
+    dvmCompilerClobber(cUnit, regLo);
+    dvmCompilerClobber(cUnit, regHi);
+    dvmCompilerMarkInUse(cUnit, regLo);
+    dvmCompilerMarkInUse(cUnit, regHi);
+    loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
+}
+
+static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
+                             RegisterClass opKind)
+{
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+    } else if (rlSrc.location == kLocRetval) {
+        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+    }
+    return rlSrc;
+}
+
+static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(!rlDest.wide);
+    assert(!rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                      rlDest.lowReg, kWord);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    } else {
+        dvmCompilerResetDefLoc(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+
+static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
+                                 RegisterClass opKind)
+{
+    assert(rlSrc.wide);
+    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+    if (rlSrc.location == kLocDalvikFrame) {
+        loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+        dvmCompilerMarkLive(cUnit, rlSrc.highReg,
+                            dvmCompilerSRegHi(rlSrc.sRegLow));
+    } else if (rlSrc.location == kLocRetval) {
+        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
+                         rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
+        rlSrc.location = kLocPhysReg;
+        dvmCompilerClobber(cUnit, rlSrc.lowReg);
+        dvmCompilerClobber(cUnit, rlSrc.highReg);
+    }
+    return rlSrc;
+}
+
+static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc)
+{
+    LIR *defStart;
+    LIR *defEnd;
+    assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
+    assert(rlDest.wide);
+    assert(rlSrc.wide);
+    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+    if (rlSrc.location == kLocPhysReg) {
+        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+            dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
+            (rlDest.location == kLocPhysReg)) {
+            // Src is live or Dest has assigned reg.
+            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+            genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
+                           rlSrc.lowReg, rlSrc.highReg);
+        } else {
+            // Just re-assign the registers.  Dest gets Src's regs
+            rlDest.lowReg = rlSrc.lowReg;
+            rlDest.highReg = rlSrc.highReg;
+            dvmCompilerClobber(cUnit, rlSrc.lowReg);
+            dvmCompilerClobber(cUnit, rlSrc.highReg);
+        }
+    } else {
+        // Load Src either into promoted Dest or temps allocated for Dest
+        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+        loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
+                            rlDest.highReg);
+    }
+
+    // Dest is now live and dirty (until/if we flush it to home location)
+    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+    dvmCompilerMarkLive(cUnit, rlDest.highReg,
+                        dvmCompilerSRegHi(rlDest.sRegLow));
+    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+    dvmCompilerMarkDirty(cUnit, rlDest.highReg);
+    dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
+
+
+    if (rlDest.location == kLocRetval) {
+        storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
+                          rlDest.lowReg, rlDest.highReg);
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        dvmCompilerResetDefLocWide(cUnit, rlDest);
+        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
+            dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
+            defStart = (LIR *)cUnit->lastLIRInsn;
+            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+            assert((vReg+1) == dvmCompilerS2VReg(cUnit,
+                                     dvmCompilerSRegHi(rlDest.sRegLow)));
+            storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
+                              rlDest.highReg);
+            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+            dvmCompilerMarkClean(cUnit, rlDest.highReg);
+            defEnd = (LIR *)cUnit->lastLIRInsn;
+            dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
+        }
+    }
+}
+/*
+ * Perform null-check on a register. sReg is the ssa register being checked,
+ * and mReg is the machine register holding the actual value. If internal state
+ * indicates that sReg has been checked before the check request is ignored.
+ */
+static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+                                int dOffset, MipsLIR *pcrLabel)
+{
+    /* This particular Dalvik register has been null-checked */
+    if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
+        return pcrLabel;
+    }
+    dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
+    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              MipsConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              MipsLIR *pcrLabel)
+{
+    MipsLIR *res = NULL;
+    if (cond == kMipsCondGe) { /* signed >= case */
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
+        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
+        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    } else if (cond == kMipsCondCs) {  /* unsigned >= case */
+        int tReg = dvmCompilerAllocTemp(cUnit);
+        res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2);
+        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
+        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    } else {
+        ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond);
+        dvmAbort();
+    }
+    return res;
+}
+
+/*
+ * Perform zero-check on a register. Similar to genNullCheck but the value being
+ * checked does not have a corresponding Dalvik register.
+ */
+static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+                                int dOffset, MipsLIR *pcrLabel)
+{
+    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                                  int rBound, int dOffset, MipsLIR *pcrLabel)
+{
+    return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset,
+                            pcrLabel);
+}
+
+/*
+ * Jump to the out-of-line handler to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode)
+{
+    /*
+     * We're jumping from a trace to a template. Using jal is preferable to jalr,
+     * but we need to ensure source and target addresses allow the use of jal.
+     * This should almost always be the case, but if source and target are in
+     * different 256mb regions then use jalr.  The test below is very conservative
+     * since we don't have a source address yet, but this is ok for now given that
+     * we expect this case to be very rare. The test can be made less conservative
+     * as needed in the future in coordination with address assignment during
+     * the assembly process.
+     */
+    dvmCompilerClobberHandlerRegs(cUnit);
+    int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode];
+    int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize;
+
+    if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) {
+        newLIR1(cUnit, kMipsJal, targetAddr);
+    } else {
+        loadConstant(cUnit, r_T9, targetAddr);
+        newLIR2(cUnit, kMipsJalr, r_RA, r_T9);
+    }
+}
diff --git a/vm/compiler/codegen/mips/FP/MipsFP.cpp b/vm/compiler/codegen/mips/FP/MipsFP.cpp
new file mode 100644
index 0000000..cf44b0e
--- /dev/null
+++ b/vm/compiler/codegen/mips/FP/MipsFP.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+                                              int reg1, int reg2);
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg);
+
+/* First, flush any registers associated with this value */
+static void loadValueAddress(CompilationUnit *cUnit, RegLocation rlSrc,
+                             int rDest)
+{
+     rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
+                          dvmCompilerUpdateLoc(cUnit, rlSrc);
+     if (rlSrc.location == kLocPhysReg) {
+         if (rlSrc.wide) {
+             dvmCompilerFlushRegWideForV5TEVFP(cUnit, rlSrc.lowReg,
+                                               rlSrc.highReg);
+         } else {
+             dvmCompilerFlushRegForV5TEVFP(cUnit, rlSrc.lowReg);
+         }
+     }
+     opRegRegImm(cUnit, kOpAdd, rDest, rFP,
+                 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+#ifdef __mips_hard_float
+    RegLocation rlResult = LOC_C_RETURN_WIDE_ALT;
+#else
+    RegLocation rlResult = LOC_C_RETURN_WIDE;
+#endif
+    RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+    loadValueAddress(cUnit, rlSrc, r_A2);
+    genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+}
+
+/*
+ * TUNING: On some implementations, it is quicker to pass addresses
+ * to the handlers rather than load the operands into core registers
+ * and then move the values to FP regs in the handlers.  Other implementations
+ * may prefer passing data in registers (and the latter approach would
+ * yeild cleaner register handling - avoiding the requirement that operands
+ * be flushed to memory prior to the call).
+ */
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2)
+{
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    RegLocation rlResult;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            op = kMipsFadds;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            op = kMipsFsubs;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            op = kMipsFdivs;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            op = kMipsFmuls;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+    rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    newLIR3(cUnit, (MipsOpCode)op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    storeValue(cUnit, rlDest, rlResult);
+
+    return false;
+#else
+    TemplateOpcode opcode;
+
+    /*
+     * Don't attempt to optimize register usage since these opcodes call out to
+     * the handlers.
+     */
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_FLOAT_2ADDR:
+        case OP_ADD_FLOAT:
+            opcode = TEMPLATE_ADD_FLOAT_VFP;
+            break;
+        case OP_SUB_FLOAT_2ADDR:
+        case OP_SUB_FLOAT:
+            opcode = TEMPLATE_SUB_FLOAT_VFP;
+            break;
+        case OP_DIV_FLOAT_2ADDR:
+        case OP_DIV_FLOAT:
+            opcode = TEMPLATE_DIV_FLOAT_VFP;
+            break;
+        case OP_MUL_FLOAT_2ADDR:
+        case OP_MUL_FLOAT:
+            opcode = TEMPLATE_MUL_FLOAT_VFP;
+            break;
+        case OP_REM_FLOAT_2ADDR:
+        case OP_REM_FLOAT:
+        case OP_NEG_FLOAT: {
+            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc1, r_A1);
+    dvmCompilerClobber(cUnit, r_A1);
+    loadValueAddress(cUnit, rlSrc2, r_A2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+    }
+    return false;
+#endif
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+                             RegLocation rlDest, RegLocation rlSrc1,
+                             RegLocation rlSrc2)
+{
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    RegLocation rlResult;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            op = kMipsFaddd;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            op = kMipsFsubd;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            op = kMipsFdivd;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            op = kMipsFmuld;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+        }
+        default:
+            return true;
+    }
+    rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+    assert(rlSrc1.wide);
+    rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+    assert(rlSrc2.wide);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+    assert(rlDest.wide);
+    assert(rlResult.wide);
+    newLIR3(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc1.lowReg, rlSrc1.highReg),
+            S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    storeValueWide(cUnit, rlDest, rlResult);
+    return false;
+#else
+    TemplateOpcode opcode;
+
+    switch (mir->dalvikInsn.opcode) {
+        case OP_ADD_DOUBLE_2ADDR:
+        case OP_ADD_DOUBLE:
+            opcode = TEMPLATE_ADD_DOUBLE_VFP;
+            break;
+        case OP_SUB_DOUBLE_2ADDR:
+        case OP_SUB_DOUBLE:
+            opcode = TEMPLATE_SUB_DOUBLE_VFP;
+            break;
+        case OP_DIV_DOUBLE_2ADDR:
+        case OP_DIV_DOUBLE:
+            opcode = TEMPLATE_DIV_DOUBLE_VFP;
+            break;
+        case OP_MUL_DOUBLE_2ADDR:
+        case OP_MUL_DOUBLE:
+            opcode = TEMPLATE_MUL_DOUBLE_VFP;
+            break;
+        case OP_REM_DOUBLE_2ADDR:
+        case OP_REM_DOUBLE:
+        case OP_NEG_DOUBLE: {
+            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+                                               rlSrc2);
+        }
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc1, r_A1);
+    dvmCompilerClobber(cUnit, r_A1);
+    loadValueAddress(cUnit, rlSrc2, r_A2);
+    genDispatchToHandler(cUnit, opcode);
+    rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+    if (rlDest.location == kLocPhysReg) {
+        dvmCompilerClobber(cUnit, rlDest.lowReg);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    }
+    return false;
+#endif
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+    Opcode opcode = mir->dalvikInsn.opcode;
+    bool longSrc = false;
+    bool longDest = false;
+    RegLocation rlSrc;
+    RegLocation rlDest;
+#ifdef __mips_hard_float
+    int op = kMipsNop;
+    int srcReg;
+    RegLocation rlResult;
+
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            op = kMipsFcvtsw;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            op = kMipsFcvtsd;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kMipsFcvtds;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            op = kMipsFcvtdw;
+            break;
+        case OP_FLOAT_TO_INT:
+        case OP_DOUBLE_TO_INT:
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+        rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+        srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+        rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+        srcReg = rlSrc.lowReg;
+    }
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg), srcReg);
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+        newLIR2(cUnit, (MipsOpCode)op, rlResult.lowReg, srcReg);
+        storeValue(cUnit, rlDest, rlResult);
+    }
+    return false;
+#else
+    TemplateOpcode templateOpcode;
+    switch (opcode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_INT_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            templateOpcode = TEMPLATE_FLOAT_TO_INT_VFP;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            templateOpcode = TEMPLATE_INT_TO_DOUBLE_VFP;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            templateOpcode = TEMPLATE_DOUBLE_TO_INT_VFP;
+            break;
+        case OP_LONG_TO_DOUBLE:
+        case OP_FLOAT_TO_LONG:
+        case OP_LONG_TO_FLOAT:
+        case OP_DOUBLE_TO_LONG:
+            return genConversionPortable(cUnit, mir);
+        default:
+            return true;
+    }
+
+    if (longSrc) {
+        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    } else {
+        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    }
+
+    if (longDest) {
+        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+    } else {
+        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+    }
+    loadValueAddress(cUnit, rlDest, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc, r_A1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    if (rlDest.wide) {
+        rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+        dvmCompilerClobber(cUnit, rlDest.highReg);
+    } else {
+        rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+    }
+    dvmCompilerClobber(cUnit, rlDest.lowReg);
+    return false;
+#endif
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                     RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    TemplateOpcode templateOpcode;
+    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+    bool wide = true;
+
+    switch(mir->dalvikInsn.opcode) {
+        case OP_CMPL_FLOAT:
+            templateOpcode = TEMPLATE_CMPL_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPG_FLOAT:
+            templateOpcode = TEMPLATE_CMPG_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPL_DOUBLE:
+            templateOpcode = TEMPLATE_CMPL_DOUBLE_VFP;
+            break;
+        case OP_CMPG_DOUBLE:
+            templateOpcode = TEMPLATE_CMPG_DOUBLE_VFP;
+            break;
+        default:
+            return true;
+    }
+    loadValueAddress(cUnit, rlSrc1, r_A0);
+    dvmCompilerClobber(cUnit, r_A0);
+    loadValueAddress(cUnit, rlSrc2, r_A1);
+    genDispatchToHandler(cUnit, templateOpcode);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/mips/GlobalOptimizations.cpp b/vm/compiler/codegen/mips/GlobalOptimizations.cpp
new file mode 100644
index 0000000..189d818
--- /dev/null
+++ b/vm/compiler/codegen/mips/GlobalOptimizations.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "MipsLIR.h"
+
+/*
+ * Identify unconditional branches that jump to the immediate successor of the
+ * branch itself.
+ */
+static void applyRedundantBranchElimination(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Branch to the next instruction */
+        if (!thisLIR->flags.isNop && thisLIR->opcode == kMipsB) {
+            MipsLIR *nextLIR = thisLIR;
+
+            while (true) {
+                nextLIR = NEXT_LIR(nextLIR);
+
+                /*
+                 * Is the branch target the next instruction?
+                 */
+                if (nextLIR == (MipsLIR *) thisLIR->generic.target) {
+                    thisLIR->flags.isNop = true;
+                    break;
+                }
+
+                /*
+                 * Found real useful stuff between the branch and the target.
+                 * Need to explicitly check the lastLIRInsn here since with
+                 * method-based JIT the branch might be the last real
+                 * instruction.
+                 */
+                if (!isPseudoOpCode(nextLIR->opcode) ||
+                    (nextLIR = (MipsLIR *) cUnit->lastLIRInsn))
+                    break;
+            }
+        }
+    }
+}
+
+/*
+ * Do simple a form of copy propagation and elimination.
+ */
+static void applyCopyPropagation(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+
+    /* look for copies to possibly eliminate */
+    for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+         thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        if (thisLIR->flags.isNop || thisLIR->opcode != kMipsMove)
+            continue;
+
+        const int max_insns = 10;
+        MipsLIR *savedLIR[max_insns];
+        int srcRedefined = 0;
+        int insnCount = 0;
+        MipsLIR *nextLIR;
+
+        /* look for and record all uses of reg defined by the copy */
+        for (nextLIR = (MipsLIR *) NEXT_LIR(thisLIR);
+             nextLIR != (MipsLIR *) cUnit->lastLIRInsn;
+             nextLIR = NEXT_LIR(nextLIR)) {
+
+            if (nextLIR->flags.isNop || nextLIR->opcode == kMips32BitData)
+                continue;
+
+            if (isPseudoOpCode(nextLIR->opcode)) {
+                if (nextLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                    nextLIR->opcode == kMipsPseudoBarrier ||
+                    nextLIR->opcode == kMipsPseudoExtended ||
+                    nextLIR->opcode == kMipsPseudoSSARep)
+                    continue; /* these pseudos don't pose problems */
+                else if (nextLIR->opcode == kMipsPseudoTargetLabel ||
+                         nextLIR->opcode == kMipsPseudoEntryBlock ||
+                         nextLIR->opcode == kMipsPseudoExitBlock)
+                    insnCount = 0;  /* give up for these pseudos */
+                break; /* reached end for copy propagation */
+            }
+
+            /* Since instructions with IS_BRANCH flag set will have its */
+            /* useMask and defMask set to ENCODE_ALL, any checking of   */
+            /* these flags must come after the branching checks.        */
+
+            /* don't propagate across branch/jump and link case
+               or jump via register */
+            if (EncodingMap[nextLIR->opcode].flags & REG_DEF_LR ||
+                nextLIR->opcode == kMipsJalr ||
+                nextLIR->opcode == kMipsJr) {
+                insnCount = 0;
+                break;
+            }
+
+            /* branches with certain targets ok while others aren't */
+            if (EncodingMap[nextLIR->opcode].flags & IS_BRANCH) {
+                MipsLIR *targetLIR =  (MipsLIR *) nextLIR->generic.target;
+                if (targetLIR->opcode != kMipsPseudoEHBlockLabel &&
+                    targetLIR->opcode != kMipsPseudoChainingCellHot &&
+                    targetLIR->opcode != kMipsPseudoChainingCellNormal &&
+                    targetLIR->opcode != kMipsPseudoChainingCellInvokePredicted &&
+                    targetLIR->opcode != kMipsPseudoChainingCellInvokeSingleton &&
+                    targetLIR->opcode != kMipsPseudoPCReconstructionBlockLabel &&
+                    targetLIR->opcode != kMipsPseudoPCReconstructionCell) {
+                    insnCount = 0;
+                    break;
+                }
+                /* FIXME - for now don't propagate across any branch/jump. */
+                insnCount = 0;
+                break;
+            }
+
+            /* copy def reg used here, so record insn for copy propagation */
+            if (thisLIR->defMask & nextLIR->useMask) {
+                if (insnCount == max_insns || srcRedefined) {
+                    insnCount = 0;
+                    break; /* just give up if too many or not possible */
+                }
+                savedLIR[insnCount++] = nextLIR;
+            }
+
+            if (thisLIR->defMask & nextLIR->defMask) {
+		if (nextLIR->opcode == kMipsMovz)
+		    insnCount = 0; /* movz relies on thisLIR setting dst reg so abandon propagation*/
+                break;
+            }
+
+            /* copy src reg redefined here, so can't propagate further */
+            if (thisLIR->useMask & nextLIR->defMask) {
+                if (insnCount == 0)
+                    break; /* nothing to propagate */
+                srcRedefined = 1;
+            }
+       }
+
+        /* conditions allow propagation and copy elimination */
+        if (insnCount) {
+            int i;
+            for (i = 0; i < insnCount; i++) {
+                int flags = EncodingMap[savedLIR[i]->opcode].flags;
+                savedLIR[i]->useMask &= ~(1 << thisLIR->operands[0]);
+                savedLIR[i]->useMask |= 1 << thisLIR->operands[1];
+                if ((flags & REG_USE0) &&
+                    savedLIR[i]->operands[0] == thisLIR->operands[0])
+                    savedLIR[i]->operands[0] = thisLIR->operands[1];
+                if ((flags & REG_USE1) &&
+                    savedLIR[i]->operands[1] == thisLIR->operands[0])
+                    savedLIR[i]->operands[1] = thisLIR->operands[1];
+                if ((flags & REG_USE2) &&
+                    savedLIR[i]->operands[2] == thisLIR->operands[0])
+                    savedLIR[i]->operands[2] = thisLIR->operands[1];
+                if ((flags & REG_USE3) &&
+                    savedLIR[i]->operands[3] == thisLIR->operands[0])
+                    savedLIR[i]->operands[3] = thisLIR->operands[1];
+            }
+            thisLIR->flags.isNop = true;
+        }
+    }
+}
+
+#ifdef __mips_hard_float
+/*
+ * Look for pairs of mov.s instructions that can be combined into mov.d
+ */
+static void mergeMovs(CompilationUnit *cUnit)
+{
+  MipsLIR *movsLIR = NULL;
+  MipsLIR *thisLIR;
+
+  for (thisLIR = (MipsLIR *) cUnit->firstLIRInsn;
+       thisLIR != (MipsLIR *) cUnit->lastLIRInsn;
+       thisLIR = NEXT_LIR(thisLIR)) {
+    if (thisLIR->flags.isNop)
+      continue;
+
+    if (isPseudoOpCode(thisLIR->opcode)) {
+      if (thisLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                thisLIR->opcode == kMipsPseudoExtended ||
+	  thisLIR->opcode == kMipsPseudoSSARep)
+	continue;  /* ok to move across these pseudos */
+      movsLIR = NULL; /* don't merge across other pseudos */
+      continue;
+    }
+
+    /* merge pairs of mov.s instructions */
+    if (thisLIR->opcode == kMipsFmovs) {
+      if (movsLIR == NULL)
+	movsLIR = thisLIR;
+      else if (((movsLIR->operands[0] & 1) == 0) &&
+	       ((movsLIR->operands[1] & 1) == 0) &&
+	       ((movsLIR->operands[0] + 1) == thisLIR->operands[0]) &&
+	       ((movsLIR->operands[1] + 1) == thisLIR->operands[1])) {
+	/* movsLIR is handling even register - upgrade to mov.d */
+	movsLIR->opcode = kMipsFmovd;
+	movsLIR->operands[0] = S2D(movsLIR->operands[0], movsLIR->operands[0]+1);
+	movsLIR->operands[1] = S2D(movsLIR->operands[1], movsLIR->operands[1]+1);
+	thisLIR->flags.isNop = true;
+	movsLIR = NULL;
+      }
+      else if (((movsLIR->operands[0] & 1) == 1) &&
+	       ((movsLIR->operands[1] & 1) == 1) &&
+	       ((movsLIR->operands[0] - 1) == thisLIR->operands[0]) &&
+	       ((movsLIR->operands[1] - 1) == thisLIR->operands[1])) {
+	/* thissLIR is handling even register - upgrade to mov.d */
+	thisLIR->opcode = kMipsFmovd;
+	thisLIR->operands[0] = S2D(thisLIR->operands[0], thisLIR->operands[0]+1);
+	thisLIR->operands[1] = S2D(thisLIR->operands[1], thisLIR->operands[1]+1);
+	movsLIR->flags.isNop = true;
+	movsLIR = NULL;
+      }
+      else
+	/* carry on searching from here */
+	movsLIR = thisLIR;
+      continue;
+    }
+
+    /* intervening instruction - start search from scratch */
+    movsLIR = NULL;
+  }
+}
+#endif
+
+
+/*
+ * Look back first and then ahead to try to find an instruction to move into
+ * the branch delay slot.  If the analysis can be done cheaply enough, it may be
+ * be possible to tune this routine to be more beneficial (e.g., being more
+ * particular about what instruction is speculated).
+ */
+static MipsLIR *delaySlotLIR(MipsLIR *firstLIR, MipsLIR *branchLIR)
+{
+    int isLoad;
+    int loadVisited = 0;
+    int isStore;
+    int storeVisited = 0;
+    u8 useMask = branchLIR->useMask;
+    u8 defMask = branchLIR->defMask;
+    MipsLIR *thisLIR;
+    MipsLIR *newLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+
+    for (thisLIR = PREV_LIR(branchLIR);
+         thisLIR != firstLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        if (thisLIR->flags.isNop)
+            continue;
+
+        if (isPseudoOpCode(thisLIR->opcode)) {
+            if (thisLIR->opcode == kMipsPseudoDalvikByteCodeBoundary ||
+                thisLIR->opcode == kMipsPseudoExtended ||
+                thisLIR->opcode == kMipsPseudoSSARep)
+                continue;  /* ok to move across these pseudos */
+            break; /* don't move across all other pseudos */
+        }
+
+        /* give up on moving previous instruction down into slot */
+        if (thisLIR->opcode == kMipsNop ||
+            thisLIR->opcode == kMips32BitData ||
+            EncodingMap[thisLIR->opcode].flags & IS_BRANCH)
+            break;
+
+        /* don't reorder loads/stores (the alias info could
+           possibly be used to allow as a future enhancement) */
+        isLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        isStore = EncodingMap[thisLIR->opcode].flags & IS_STORE;
+
+        if (!(thisLIR->useMask & defMask) &&
+            !(thisLIR->defMask & useMask) &&
+            !(thisLIR->defMask & defMask) &&
+            !(isLoad && storeVisited) &&
+            !(isStore && loadVisited) &&
+            !(isStore && storeVisited)) {
+            *newLIR = *thisLIR;
+            thisLIR->flags.isNop = true;
+            return newLIR; /* move into delay slot succeeded */
+        }
+
+        loadVisited |= isLoad;
+        storeVisited |= isStore;
+
+        /* accumulate def/use constraints */
+        useMask |= thisLIR->useMask;
+        defMask |= thisLIR->defMask;
+    }
+
+    /* for unconditional branches try to copy the instruction at the
+       branch target up into the delay slot and adjust the branch */
+    if (branchLIR->opcode == kMipsB) {
+        MipsLIR *targetLIR;
+        for (targetLIR = (MipsLIR *) branchLIR->generic.target;
+             targetLIR;
+             targetLIR = NEXT_LIR(targetLIR)) {
+            if (!targetLIR->flags.isNop &&
+                (!isPseudoOpCode(targetLIR->opcode) || /* can't pull predicted up */
+                 targetLIR->opcode == kMipsPseudoChainingCellInvokePredicted))
+                break; /* try to get to next real op at branch target */
+        }
+        if (targetLIR && !isPseudoOpCode(targetLIR->opcode) &&
+            !(EncodingMap[targetLIR->opcode].flags & IS_BRANCH)) {
+            *newLIR = *targetLIR;
+            branchLIR->generic.target = (LIR *) NEXT_LIR(targetLIR);
+            return newLIR;
+        }
+    } else if (branchLIR->opcode >= kMipsBeq && branchLIR->opcode <= kMipsBne) {
+        /* for conditional branches try to fill branch delay slot
+           via speculative execution when safe */
+        MipsLIR *targetLIR;
+        for (targetLIR = (MipsLIR *) branchLIR->generic.target;
+             targetLIR;
+             targetLIR = NEXT_LIR(targetLIR)) {
+            if (!targetLIR->flags.isNop && !isPseudoOpCode(targetLIR->opcode))
+                break; /* try to get to next real op at branch target */
+        }
+
+        MipsLIR *nextLIR;
+        for (nextLIR = NEXT_LIR(branchLIR);
+             nextLIR;
+             nextLIR = NEXT_LIR(nextLIR)) {
+            if (!nextLIR->flags.isNop && !isPseudoOpCode(nextLIR->opcode))
+                break; /* try to get to next real op for fall thru */
+        }
+
+        if (nextLIR && targetLIR) {
+            int flags = EncodingMap[nextLIR->opcode].flags;
+            int isLoad = flags & IS_LOAD;
+
+            /* common branch and fall thru to normal chaining cells case */
+            if (isLoad && nextLIR->opcode == targetLIR->opcode &&
+                nextLIR->operands[0] == targetLIR->operands[0] &&
+                nextLIR->operands[1] == targetLIR->operands[1] &&
+                nextLIR->operands[2] == targetLIR->operands[2]) {
+                *newLIR = *targetLIR;
+                branchLIR->generic.target = (LIR *) NEXT_LIR(targetLIR);
+                return newLIR;
+            }
+
+            /* try prefetching (maybe try speculating instructions along the
+               trace like dalvik frame load which is common and may be safe) */
+            int isStore = flags & IS_STORE;
+            if (isLoad || isStore) {
+                newLIR->opcode = kMipsPref;
+                newLIR->operands[0] = isLoad ? 0 : 1;
+                newLIR->operands[1] = nextLIR->operands[1];
+                newLIR->operands[2] = nextLIR->operands[2];
+                newLIR->defMask = nextLIR->defMask;
+                newLIR->useMask = nextLIR->useMask;
+                return newLIR;
+            }
+        }
+    }
+
+    /* couldn't find a useful instruction to move into the delay slot */
+    newLIR->opcode = kMipsNop;
+    return newLIR;
+}
+
+/*
+ * The branch delay slot has been ignored thus far.  This is the point where
+ * a useful instruction is moved into it or a nop is inserted.  Leave existing
+ * NOPs alone -- these came from sparse and packed switch ops and are needed
+ * to maintain the proper offset to the jump table.
+ */
+static void introduceBranchDelaySlot(CompilationUnit *cUnit)
+{
+    MipsLIR *thisLIR;
+    MipsLIR *firstLIR =(MipsLIR *) cUnit->firstLIRInsn;
+    MipsLIR *lastLIR =(MipsLIR *) cUnit->lastLIRInsn;
+
+    for (thisLIR = lastLIR; thisLIR != firstLIR; thisLIR = PREV_LIR(thisLIR)) {
+        if (thisLIR->flags.isNop ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_BRANCH)) {
+            continue;
+        } else if (thisLIR == lastLIR) {
+            dvmCompilerAppendLIR(cUnit,
+                (LIR *) delaySlotLIR(firstLIR, thisLIR));
+        } else if (NEXT_LIR(thisLIR)->opcode != kMipsNop) {
+            dvmCompilerInsertLIRAfter((LIR *) thisLIR,
+                (LIR *) delaySlotLIR(firstLIR, thisLIR));
+        }
+    }
+
+    if (!thisLIR->flags.isNop &&
+        !isPseudoOpCode(thisLIR->opcode) &&
+        EncodingMap[thisLIR->opcode].flags & IS_BRANCH) {
+        /* nothing available to move, so insert nop */
+        MipsLIR *nopLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+        nopLIR->opcode = kMipsNop;
+        dvmCompilerInsertLIRAfter((LIR *) thisLIR, (LIR *) nopLIR);
+    }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+    applyRedundantBranchElimination(cUnit);
+    applyCopyPropagation(cUnit);
+#ifdef __mips_hard_float
+    mergeMovs(cUnit);
+#endif
+    introduceBranchDelaySlot(cUnit);
+}
diff --git a/vm/compiler/codegen/mips/LocalOptimizations.cpp b/vm/compiler/codegen/mips/LocalOptimizations.cpp
new file mode 100644
index 0000000..1ef0d17
--- /dev/null
+++ b/vm/compiler/codegen/mips/LocalOptimizations.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+
+#define DEBUG_OPT(X)
+
+/* Check RAW, WAR, and WAR dependency on the register operands */
+#define CHECK_REG_DEP(use, def, check) ((def & check->useMask) || \
+                                        ((use | def) & check->defMask))
+
+/* Scheduler heuristics */
+#define MAX_HOIST_DISTANCE 20
+#define LDLD_DISTANCE 4
+#define LD_LATENCY 2
+
+static inline bool isDalvikRegisterClobbered(MipsLIR *lir1, MipsLIR *lir2)
+{
+    int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
+    int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
+    int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
+    int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
+
+    return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+#if 0
+/* Debugging utility routine */
+static void dumpDependentInsnPair(MipsLIR *thisLIR, MipsLIR *checkLIR,
+                                  const char *optimization)
+{
+    LOGD("************ %s ************", optimization);
+    dvmDumpLIRInsn((LIR *) thisLIR, 0);
+    dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/* Convert a more expensive instruction (ie load) into a move */
+static void convertMemOpIntoMove(CompilationUnit *cUnit, MipsLIR *origLIR,
+                                 int dest, int src)
+{
+    /* Insert a move to replace the load */
+    MipsLIR *moveLIR;
+    moveLIR = dvmCompilerRegCopyNoInsert( cUnit, dest, src);
+    /*
+     * Insert the converted instruction after the original since the
+     * optimization is scannng in the top-down order and the new instruction
+     * will need to be re-checked (eg the new dest clobbers the src used in
+     * thisLIR).
+     */
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) moveLIR);
+}
+
+/*
+ * Perform a pass of top-down walk, from the second-last instruction in the
+ * superblock, to eliminate redundant loads and stores.
+ *
+ * An earlier load can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * An earlier store can eliminate a later load iff
+ *   1) They are must-aliases
+ *   2) The native register is not clobbered in between
+ *   3) The memory location is not written to in between
+ *
+ * A later store can be eliminated by an earlier store iff
+ *   1) They are must-aliases
+ *   2) The memory location is not written to in between
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+                                      MipsLIR *headLIR,
+                                      MipsLIR *tailLIR)
+{
+    MipsLIR *thisLIR;
+
+    if (headLIR == tailLIR) return;
+
+    for (thisLIR = PREV_LIR(tailLIR);
+         thisLIR != headLIR;
+         thisLIR = PREV_LIR(thisLIR)) {
+        int sinkDistance = 0;
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & (IS_LOAD | IS_STORE))) {
+            continue;
+        }
+
+        int nativeRegId = thisLIR->operands[0];
+        bool isThisLIRLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD;
+        MipsLIR *checkLIR;
+        /* Use the mem mask to determine the rough memory location */
+        u8 thisMemMask = (thisLIR->useMask | thisLIR->defMask) & ENCODE_MEM;
+
+        /*
+         * Currently only eliminate redundant ld/st for constant and Dalvik
+         * register accesses.
+         */
+        if (!(thisMemMask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue;
+
+        /*
+         * Add r15 (pc) to the resource mask to prevent this instruction
+         * from sinking past branch instructions. Also take out the memory
+         * region bits since stopMask is used to check data/control
+         * dependencies.
+         */
+        u8 stopUseRegMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                            ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        for (checkLIR = NEXT_LIR(thisLIR);
+             checkLIR != tailLIR;
+             checkLIR = NEXT_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = (checkLIR->useMask | checkLIR->defMask) &
+                              ENCODE_MEM;
+            u8 aliasCondition = thisMemMask & checkMemMask;
+            bool stopHere = false;
+
+            /*
+             * Potential aliases seen - check the alias relations
+             */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                bool isCheckLIRLoad = EncodingMap[checkLIR->opcode].flags &
+                                      IS_LOAD;
+                if  (aliasCondition == ENCODE_LITERAL) {
+                    /*
+                     * Should only see literal loads in the instruction
+                     * stream.
+                     */
+                    assert(!(EncodingMap[checkLIR->opcode].flags &
+                             IS_STORE));
+                    /* Same value && same register type */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo &&
+                        REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId)){
+                        /*
+                         * Different destination register - insert
+                         * a move
+                         */
+                        if (checkLIR->operands[0] != nativeRegId) {
+                            convertMemOpIntoMove(cUnit, checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                        }
+                        checkLIR->flags.isNop = true;
+                    }
+                } else if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias */
+                    if (checkLIR->aliasInfo == thisLIR->aliasInfo) {
+                        /* Only optimize compatible registers */
+                        bool regCompatible =
+                            REGTYPE(checkLIR->operands[0]) ==
+                            REGTYPE(nativeRegId);
+                        if ((isThisLIRLoad && isCheckLIRLoad) ||
+                            (!isThisLIRLoad && isCheckLIRLoad)) {
+                            /* RAR or RAW */
+                            if (regCompatible) {
+                                /*
+                                 * Different destination register -
+                                 * insert a move
+                                 */
+                                if (checkLIR->operands[0] !=
+                                    nativeRegId) {
+                                    convertMemOpIntoMove(cUnit,
+                                                 checkLIR,
+                                                 checkLIR->operands[0],
+                                                 nativeRegId);
+                                }
+                                checkLIR->flags.isNop = true;
+                            } else {
+                                /*
+                                 * Destinaions are of different types -
+                                 * something complicated going on so
+                                 * stop looking now.
+                                 */
+                                stopHere = true;
+                            }
+                        } else if (isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAR - register value is killed */
+                            stopHere = true;
+                        } else if (!isThisLIRLoad && !isCheckLIRLoad) {
+                            /* WAW - nuke the earlier store */
+                            thisLIR->flags.isNop = true;
+                            stopHere = true;
+                        }
+                    /* Partial overlap */
+                    } else if (isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        /*
+                         * It is actually ok to continue if checkLIR
+                         * is a read. But it is hard to make a test
+                         * case for this so we just stop here to be
+                         * conservative.
+                         */
+                        stopHere = true;
+                    }
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    break;
+                /* The checkLIR has been transformed - check the next one */
+                } else if (checkLIR->flags.isNop) {
+                    continue;
+                }
+            }
+
+
+            /*
+             * this and check LIRs have no memory dependency. Now check if
+             * their register operands have any RAW, WAR, and WAW
+             * dependencies. If so, stop looking.
+             */
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                "REG CLOBBERED"));
+                /* Only sink store instructions */
+                if (sinkDistance && !isThisLIRLoad) {
+                    MipsLIR *newStoreLIR =
+                        (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+                    *newStoreLIR = *thisLIR;
+                    /*
+                     * Stop point found - insert *before* the checkLIR
+                     * since the instruction list is scanned in the
+                     * top-down order.
+                     */
+                    dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+                                               (LIR *) newStoreLIR);
+                    thisLIR->flags.isNop = true;
+                }
+                break;
+            } else if (!checkLIR->flags.isNop) {
+                sinkDistance++;
+            }
+        }
+    }
+}
+
+/*
+ * Perform a pass of bottom-up walk, from the second instruction in the
+ * superblock, to try to hoist loads to earlier slots.
+ */
+static void applyLoadHoisting(CompilationUnit *cUnit,
+                              MipsLIR *headLIR,
+                              MipsLIR *tailLIR)
+{
+    MipsLIR *thisLIR, *checkLIR;
+    /*
+     * Store the list of independent instructions that can be hoisted past.
+     * Will decide the best place to insert later.
+     */
+    MipsLIR *prevInstList[MAX_HOIST_DISTANCE];
+
+    /* Empty block */
+    if (headLIR == tailLIR) return;
+
+    /* Start from the second instruction */
+    for (thisLIR = NEXT_LIR(headLIR);
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+
+        /* Skip non-interesting instructions */
+        if ((thisLIR->flags.isNop == true) ||
+            isPseudoOpCode(thisLIR->opcode) ||
+            !(EncodingMap[thisLIR->opcode].flags & IS_LOAD)) {
+            continue;
+        }
+
+        u8 stopUseAllMask = thisLIR->useMask;
+
+        /*
+         * Branches for null/range checks are marked with the true resource
+         * bits, and loads to Dalvik registers, constant pools, and non-alias
+         * locations are safe to be hoisted. So only mark the heap references
+         * conservatively here.
+         */
+        if (stopUseAllMask & ENCODE_HEAP_REF) {
+            stopUseAllMask |= ENCODE_REG_PC;
+        }
+
+        /* Similar as above, but just check for pure register dependency */
+        u8 stopUseRegMask = stopUseAllMask & ~ENCODE_MEM;
+        u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM;
+
+        int nextSlot = 0;
+        bool stopHere = false;
+
+        /* Try to hoist the load to a good spot */
+        for (checkLIR = PREV_LIR(thisLIR);
+             checkLIR != headLIR;
+             checkLIR = PREV_LIR(checkLIR)) {
+
+            /*
+             * Skip already dead instructions (whose dataflow information is
+             * outdated and misleading).
+             */
+            if (checkLIR->flags.isNop) continue;
+
+            u8 checkMemMask = checkLIR->defMask & ENCODE_MEM;
+            u8 aliasCondition = stopUseAllMask & checkMemMask;
+            stopHere = false;
+
+            /* Potential WAR alias seen - check the exact relation */
+            if (checkMemMask != ENCODE_MEM && aliasCondition != 0) {
+                /* We can fully disambiguate Dalvik references */
+                if (aliasCondition == ENCODE_DALVIK_REG) {
+                    /* Must alias or partually overlap */
+                    if ((checkLIR->aliasInfo == thisLIR->aliasInfo) ||
+                        isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                        stopHere = true;
+                    }
+                /* Conservatively treat all heap refs as may-alias */
+                } else {
+                    assert(aliasCondition == ENCODE_HEAP_REF);
+                    stopHere = true;
+                }
+                /* Memory content may be updated. Stop looking now. */
+                if (stopHere) {
+                    prevInstList[nextSlot++] = checkLIR;
+                    break;
+                }
+            }
+
+            if (stopHere == false) {
+                stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask,
+                                         checkLIR);
+            }
+
+            /*
+             * Store the dependent or non-pseudo/indepedent instruction to the
+             * list.
+             */
+            if (stopHere || !isPseudoOpCode(checkLIR->opcode)) {
+                prevInstList[nextSlot++] = checkLIR;
+                if (nextSlot == MAX_HOIST_DISTANCE) break;
+            }
+
+            /* Found a new place to put the load - move it here */
+            if (stopHere == true) {
+                DEBUG_OPT(dumpDependentInsnPair(checkLIR, thisLIR
+                                                "HOIST STOP"));
+                break;
+            }
+        }
+
+        /*
+         * Reached the top - use headLIR as the dependent marker as all labels
+         * are barriers.
+         */
+        if (stopHere == false && nextSlot < MAX_HOIST_DISTANCE) {
+            prevInstList[nextSlot++] = headLIR;
+        }
+
+        /*
+         * At least one independent instruction is found. Scan in the reversed
+         * direction to find a beneficial slot.
+         */
+        if (nextSlot >= 2) {
+            int firstSlot = nextSlot - 2;
+            int slot;
+            MipsLIR *depLIR = prevInstList[nextSlot-1];
+            /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
+            if (!isPseudoOpCode(depLIR->opcode) &&
+                (EncodingMap[depLIR->opcode].flags & IS_LOAD)) {
+                firstSlot -= LDLD_DISTANCE;
+            }
+            /*
+             * Make sure we check slot >= 0 since firstSlot may be negative
+             * when the loop is first entered.
+             */
+            for (slot = firstSlot; slot >= 0; slot--) {
+                MipsLIR *curLIR = prevInstList[slot];
+                MipsLIR *prevLIR = prevInstList[slot+1];
+
+                /*
+                 * Check the highest instruction.
+                 * ENCODE_ALL represents a scheduling barrier.
+                 */
+                if (prevLIR->defMask == ENCODE_ALL) {
+                    /*
+                     * If the first instruction is a load, don't hoist anything
+                     * above it since it is unlikely to be beneficial.
+                     */
+                    if (EncodingMap[curLIR->opcode].flags & IS_LOAD) continue;
+                    /*
+                     * Need to unconditionally break here even if the hoisted
+                     * distance is greater than LD_LATENCY (ie more than enough
+                     * cycles are inserted to hide the load latency) since theu
+                     * subsequent code doesn't expect to compare against a
+                     * pseudo opcode (whose opcode value is negative).
+                     */
+                    break;
+                }
+
+                /*
+                 * NOTE: now prevLIR is guaranteed to be a non-pseudo
+                 * instruction (ie accessing EncodingMap[prevLIR->opcode] is
+                 * safe).
+                 *
+                 * Try to find two instructions with load/use dependency until
+                 * the remaining instructions are less than LD_LATENCY.
+                 */
+                if (((curLIR->useMask & prevLIR->defMask) &&
+                     (EncodingMap[prevLIR->opcode].flags & IS_LOAD)) ||
+                    (slot < LD_LATENCY)) {
+                    break;
+                }
+            }
+
+            /* Found a slot to hoist to */
+            if (slot >= 0) {
+                MipsLIR *curLIR = prevInstList[slot];
+                MipsLIR *newLoadLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR),
+                                                               true);
+                *newLoadLIR = *thisLIR;
+                /*
+                 * Insertion is guaranteed to succeed since checkLIR
+                 * is never the first LIR on the list
+                 */
+                dvmCompilerInsertLIRBefore((LIR *) curLIR,
+                                           (LIR *) newLoadLIR);
+                thisLIR->flags.isNop = true;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
+                                        LIR *tailLIR)
+{
+    if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
+        applyLoadStoreElimination(cUnit, (MipsLIR *) headLIR,
+                                  (MipsLIR *) tailLIR);
+    }
+    if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
+        applyLoadHoisting(cUnit, (MipsLIR *) headLIR, (MipsLIR *) tailLIR);
+    }
+}
diff --git a/vm/compiler/codegen/mips/Mips32/Factory.cpp b/vm/compiler/codegen/mips/Mips32/Factory.cpp
new file mode 100644
index 0000000..c6d7775
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Factory.cpp
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2,
+                          r_T3, r_T4, r_T5, r_T6, r_T7, r_T8, r_T9, r_S0, r_S4};
+#ifdef __mips_hard_float
+static int fpTemps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
+                        r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
+#endif
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg,
+                      int highReg);
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
+static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest);
+static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc);
+static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              MipsConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              MipsLIR *pcrLabel);
+static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
+
+#ifdef __mips_hard_float
+static MipsLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR* res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    } else {
+        /* must be both DOUBLE or both not DOUBLE */
+        assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
+        if (DOUBLEREG(rDest)) {
+            res->opcode = kMipsFmovd;
+        } else {
+            if (SINGLEREG(rDest)) {
+                if (SINGLEREG(rSrc)) {
+                    res->opcode = kMipsFmovs;
+                } else {
+                    /* note the operands are swapped for the mtc1 instr */
+                    res->opcode = kMipsMtc1;
+                    res->operands[0] = rSrc;
+                    res->operands[1] = rDest;
+                }
+            } else {
+                assert(SINGLEREG(rSrc));
+                res->opcode = kMipsMfc1;
+            }
+        }
+    }
+    setupResourceMasks(res);
+    return res;
+}
+#endif
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.  If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static MipsLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+                                     int value)
+{
+    MipsLIR *res;
+
+#ifdef __mips_hard_float
+    int rDestSave = rDest;
+    int isFpReg = FPREG(rDest);
+    if (isFpReg) {
+        assert(SINGLEREG(rDest));
+        rDest = dvmCompilerAllocTemp(cUnit);
+    }
+#endif
+
+    /* See if the value can be constructed cheaply */
+    if (value == 0) {
+        res = newLIR2(cUnit, kMipsMove, rDest, r_ZERO);
+    } else if ((value > 0) && (value <= 65535)) {
+        res = newLIR3(cUnit, kMipsOri, rDest, r_ZERO, value);
+    } else if ((value < 0) && (value >= -32768)) {
+        res = newLIR3(cUnit, kMipsAddiu, rDest, r_ZERO, value);
+    } else {
+        res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
+        if (value & 0xffff)
+	    newLIR3(cUnit, kMipsOri, rDest, rDest, value);
+    }
+
+#ifdef __mips_hard_float
+    if (isFpReg) {
+        newLIR2(cUnit, kMipsMtc1, rDest, rDestSave);
+        dvmCompilerFreeTemp(cUnit, rDest);
+    }
+#endif
+
+    return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+/*
+ * Load a class pointer value into a fixed or temp register.  Target
+ * register is clobbered, and marked inUse.
+ */
+static MipsLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
+{
+    MipsLIR *res;
+    if (dvmCompilerIsTemp(cUnit, rDest)) {
+        dvmCompilerClobber(cUnit, rDest);
+        dvmCompilerMarkInUse(cUnit, rDest);
+    }
+    res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
+    if (value & 0xffff)
+        newLIR3(cUnit, kMipsOri, rDest, rDest, value);
+    return res;
+}
+
+static MipsLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpUncondBr:
+            opcode = kMipsB;
+            break;
+        default:
+            ALOGE("Jit: bad case in opNone");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR0(cUnit, opcode);
+    return res;
+}
+
+static MipsLIR *opCompareBranch(CompilationUnit *cUnit, MipsOpCode opc, int rs, int rt)
+{
+    MipsLIR *res;
+    if (rt < 0) {
+      assert(opc >= kMipsBeqz && opc <= kMipsBnez);
+      res = newLIR1(cUnit, opc, rs);
+    } else  {
+      assert(opc == kMipsBeq || opc == kMipsBne);
+      res = newLIR2(cUnit, opc, rs, rt);
+    }
+    return res;
+}
+
+static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
+
+static MipsLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpBlx:
+            opcode = kMipsJalr;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR2(cUnit, opcode, r_RA, rDestSrc);
+}
+
+static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value);
+static MipsLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value)
+{
+    MipsLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (absValue & 0xff) == absValue;
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpAdd:
+            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+            break;
+        case kOpSub:
+            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    if (shortForm)
+        res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
+    else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = loadConstant(cUnit, rScratch, value);
+        if (op == kOpCmp)
+            newLIR2(cUnit, opcode, rDestSrc1, rScratch);
+        else
+            newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
+    }
+    return res;
+}
+
+static MipsLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    MipsOpCode opcode = kMipsNop;
+    switch (op) {
+        case kOpAdd:
+            opcode = kMipsAddu;
+            break;
+        case kOpSub:
+            opcode = kMipsSubu;
+            break;
+        case kOpAnd:
+            opcode = kMipsAnd;
+            break;
+        case kOpMul:
+            opcode = kMipsMul;
+            break;
+        case kOpOr:
+            opcode = kMipsOr;
+            break;
+        case kOpXor:
+            opcode = kMipsXor;
+            break;
+        case kOpLsl:
+            opcode = kMipsSllv;
+            break;
+        case kOpLsr:
+            opcode = kMipsSrlv;
+            break;
+        case kOpAsr:
+            opcode = kMipsSrav;
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
+}
+
+static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value)
+{
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = true;
+
+    switch(op) {
+        case kOpAdd:
+            if (IS_SIMM16(value)) {
+                opcode = kMipsAddiu;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsAddu;
+            }
+            break;
+        case kOpSub:
+            if (IS_SIMM16((-value))) {
+                value = -value;
+                opcode = kMipsAddiu;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsSubu;
+            }
+            break;
+        case kOpLsl:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSll;
+                break;
+        case kOpLsr:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSrl;
+                break;
+        case kOpAsr:
+                assert(value >= 0 && value <= 31);
+                opcode = kMipsSra;
+                break;
+        case kOpAnd:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsAndi;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsAnd;
+            }
+            break;
+        case kOpOr:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsOri;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsOr;
+            }
+            break;
+        case kOpXor:
+            if (IS_UIMM16((value))) {
+                opcode = kMipsXori;
+            }
+            else {
+                shortForm = false;
+                opcode = kMipsXor;
+            }
+            break;
+        case kOpMul:
+            shortForm = false;
+            opcode = kMipsMul;
+            break;
+        default:
+            ALOGE("Jit: bad case in opRegRegImm");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+
+    if (shortForm)
+        res = newLIR3(cUnit, opcode, rDest, rSrc1, value);
+    else {
+        if (rDest != rSrc1) {
+            res = loadConstant(cUnit, rDest, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
+        } else {
+            int rScratch = dvmCompilerAllocTemp(cUnit);
+            res = loadConstant(cUnit, rScratch, value);
+            newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
+        }
+    }
+    return res;
+}
+
+static MipsLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    MipsOpCode opcode = kMipsNop;
+    MipsLIR *res;
+    switch (op) {
+        case kOpMov:
+            opcode = kMipsMove;
+            break;
+        case kOpMvn:
+            return newLIR3(cUnit, kMipsNor, rDestSrc1, rSrc2, r_ZERO);
+        case kOpNeg:
+            return newLIR3(cUnit, kMipsSubu, rDestSrc1, r_ZERO, rSrc2);
+        case kOpAdd:
+        case kOpAnd:
+        case kOpMul:
+        case kOpOr:
+        case kOpSub:
+        case kOpXor:
+            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+        case kOp2Byte:
+#if __mips_isa_rev>=2
+            res = newLIR2(cUnit, kMipsSeb, rDestSrc1, rSrc2);
+#else
+            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
+            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
+#endif
+            return res;
+        case kOp2Short:
+#if __mips_isa_rev>=2
+            res = newLIR2(cUnit, kMipsSeh, rDestSrc1, rSrc2);
+#else
+            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
+#endif
+            return res;
+        case kOp2Char:
+             return newLIR3(cUnit, kMipsAndi, rDestSrc1, rSrc2, 0xFFFF);
+        default:
+            ALOGE("Jit: bad case in opRegReg");
+            dvmCompilerAbort(cUnit);
+            break;
+    }
+    return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
+}
+
+static MipsLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+                                     int rDestHi, int valLo, int valHi)
+{
+    MipsLIR *res;
+    res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+    loadConstantNoClobber(cUnit, rDestHi, valHi);
+    return res;
+}
+
+/* Load value from base + scaled index. */
+static MipsLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
+{
+    MipsLIR *first = NULL;
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+
+#ifdef __mips_hard_float
+    if (FPREG(rDest)) {
+        assert(SINGLEREG(rDest));
+        assert((size == kWord) || (size == kSingle));
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+#endif
+
+    if (!scale) {
+        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
+    } else {
+        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
+        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
+    }
+
+    switch (size) {
+#ifdef __mips_hard_float
+        case kSingle:
+            opcode = kMipsFlwc1;
+            break;
+#endif
+        case kWord:
+            opcode = kMipsLw;
+            break;
+        case kUnsignedHalf:
+            opcode = kMipsLhu;
+            break;
+        case kSignedHalf:
+            opcode = kMipsLh;
+            break;
+        case kUnsignedByte:
+            opcode = kMipsLbu;
+            break;
+        case kSignedByte:
+            opcode = kMipsLb;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+
+    res = newLIR3(cUnit, opcode, rDest, 0, tReg);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    dvmCompilerFreeTemp(cUnit, tReg);
+    return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+static MipsLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
+{
+    MipsLIR *first = NULL;
+    MipsLIR *res;
+    MipsOpCode opcode = kMipsNop;
+    int rNewIndex = rIndex;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+
+#ifdef __mips_hard_float
+    if (FPREG(rSrc)) {
+        assert(SINGLEREG(rSrc));
+        assert((size == kWord) || (size == kSingle));
+        size = kSingle;
+    } else {
+        if (size == kSingle)
+            size = kWord;
+    }
+#endif
+
+    if (!scale) {
+        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
+    } else {
+        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
+        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
+    }
+
+    switch (size) {
+#ifdef __mips_hard_float
+        case kSingle:
+            opcode = kMipsFswc1;
+            break;
+#endif
+        case kWord:
+            opcode = kMipsSw;
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kMipsSh;
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kMipsSb;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opcode, rSrc, 0, tReg);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    dvmCompilerFreeTemp(cUnit, rNewIndex);
+    return first;
+}
+
+static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    int i;
+    int loadCnt = 0;
+    MipsLIR *res = NULL ;
+    genBarrier(cUnit);
+
+    for (i = 0; i < 8; i++, rMask >>= 1) {
+        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
+            newLIR3(cUnit, kMipsLw, i+r_A0, loadCnt*4, rBase);
+            loadCnt++;
+        }
+    }
+
+    if (loadCnt) {/* increment after */
+        newLIR3(cUnit, kMipsAddiu, rBase, rBase, loadCnt*4);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res; /* NULL always returned which should be ok since no callers use it */
+}
+
+static MipsLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    int i;
+    int storeCnt = 0;
+    MipsLIR *res = NULL ;
+    genBarrier(cUnit);
+
+    for (i = 0; i < 8; i++, rMask >>= 1) {
+        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
+            newLIR3(cUnit, kMipsSw, i+r_A0, storeCnt*4, rBase);
+            storeCnt++;
+        }
+    }
+
+    if (storeCnt) { /* increment after */
+        newLIR3(cUnit, kMipsAddiu, rBase, rBase, storeCnt*4);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->flags.insertWrapper = true;
+#endif
+    genBarrier(cUnit);
+    return res; /* NULL always returned which should be ok since no callers use it */
+}
+
+static MipsLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDest, int rDestHi,
+                                OpSize size, int sReg)
+/*
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated sReg and MIR).  If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps.  If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+    MipsLIR *res;
+    MipsLIR *load = NULL;
+    MipsLIR *load2 = NULL;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = IS_SIMM16(displacement);
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            opcode = kMipsLw;
+#ifdef __mips_hard_float
+            if (FPREG(rDest)) {
+                opcode = kMipsFlwc1;
+                if (DOUBLEREG(rDest)) {
+                    rDest = rDest - FP_DOUBLE;
+                } else {
+                    assert(FPREG(rDestHi));
+                    assert(rDest == (rDestHi - 1));
+                }
+                rDestHi = rDest + 1;
+            }
+#endif
+            shortForm = IS_SIMM16_2WORD(displacement);
+            assert((displacement & 0x3) == 0);
+            break;
+        case kWord:
+        case kSingle:
+            opcode = kMipsLw;
+#ifdef __mips_hard_float
+            if (FPREG(rDest)) {
+                opcode = kMipsFlwc1;
+                assert(SINGLEREG(rDest));
+            }
+#endif
+            assert((displacement & 0x3) == 0);
+            break;
+        case kUnsignedHalf:
+            opcode = kMipsLhu;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kSignedHalf:
+            opcode = kMipsLh;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kUnsignedByte:
+            opcode = kMipsLbu;
+            break;
+        case kSignedByte:
+            opcode = kMipsLb;
+            break;
+        default:
+            ALOGE("Jit: bad case in loadBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+
+    if (shortForm) {
+        if (!pair) {
+            load = res = newLIR3(cUnit, opcode, rDest, displacement, rBase);
+        } else {
+            load = res = newLIR3(cUnit, opcode, rDest, displacement + LOWORD_OFFSET, rBase);
+            load2 = newLIR3(cUnit, opcode, rDestHi, displacement + HIWORD_OFFSET, rBase);
+        }
+    } else {
+        if (pair) {
+            int rTmp = dvmCompilerAllocFreeTemp(cUnit);
+            res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
+            load = newLIR3(cUnit, opcode, rDest, LOWORD_OFFSET, rTmp);
+            load2 = newLIR3(cUnit, opcode, rDestHi, HIWORD_OFFSET, rTmp);
+            dvmCompilerFreeTemp(cUnit, rTmp);
+        } else {
+            int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
+                                        : rDest;
+            res = loadConstant(cUnit, rTmp, displacement);
+            load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
+            if (rTmp != rDest)
+                dvmCompilerFreeTemp(cUnit, rTmp);
+        }
+    }
+
+    if (rBase == rFP) {
+        if (load != NULL)
+            annotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+                                    true /* isLoad */);
+        if (load2 != NULL)
+            annotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
+                                    true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (load != NULL && cUnit->heapMemOp)
+        load->flags.insertWrapper = true;
+    if (load2 != NULL && cUnit->heapMemOp)
+        load2->flags.insertWrapper = true;
+#endif
+    return load;
+}
+
+static MipsLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+                            size, sReg);
+}
+
+static MipsLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+                                int displacement, int rDestLo, int rDestHi,
+                                int sReg)
+{
+    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+                            kLong, sReg);
+}
+
+static MipsLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrc, int rSrcHi,
+                                 OpSize size)
+{
+    MipsLIR *res;
+    MipsLIR *store = NULL;
+    MipsLIR *store2 = NULL;
+    MipsOpCode opcode = kMipsNop;
+    bool shortForm = IS_SIMM16(displacement);
+    bool pair = false;
+
+    switch (size) {
+        case kLong:
+        case kDouble:
+            pair = true;
+            opcode = kMipsSw;
+#ifdef __mips_hard_float
+            if (FPREG(rSrc)) {
+                opcode = kMipsFswc1;
+                if (DOUBLEREG(rSrc)) {
+                    rSrc = rSrc - FP_DOUBLE;
+                } else {
+                    assert(FPREG(rSrcHi));
+                    assert(rSrc == (rSrcHi - 1));
+                }
+                rSrcHi = rSrc + 1;
+            }
+#endif
+            shortForm = IS_SIMM16_2WORD(displacement);
+            assert((displacement & 0x3) == 0);
+            break;
+        case kWord:
+        case kSingle:
+            opcode = kMipsSw;
+#ifdef __mips_hard_float
+            if (FPREG(rSrc)) {
+                opcode = kMipsFswc1;
+                assert(SINGLEREG(rSrc));
+            }
+#endif
+            assert((displacement & 0x3) == 0);
+            break;
+        case kUnsignedHalf:
+        case kSignedHalf:
+            opcode = kMipsSh;
+            assert((displacement & 0x1) == 0);
+            break;
+        case kUnsignedByte:
+        case kSignedByte:
+            opcode = kMipsSb;
+            break;
+        default:
+            ALOGE("Jit: bad case in storeBaseIndexedBody");
+            dvmCompilerAbort(cUnit);
+    }
+
+    if (shortForm) {
+        if (!pair) {
+            store = res = newLIR3(cUnit, opcode, rSrc, displacement, rBase);
+        } else {
+            store = res = newLIR3(cUnit, opcode, rSrc, displacement + LOWORD_OFFSET, rBase);
+            store2 = newLIR3(cUnit, opcode, rSrcHi, displacement + HIWORD_OFFSET, rBase);
+        }
+    } else {
+        int rScratch = dvmCompilerAllocTemp(cUnit);
+        res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
+        if (!pair) {
+            store =  newLIR3(cUnit, opcode, rSrc, 0, rScratch);
+        } else {
+            store =  newLIR3(cUnit, opcode, rSrc, LOWORD_OFFSET, rScratch);
+            store2 = newLIR3(cUnit, opcode, rSrcHi, HIWORD_OFFSET, rScratch);
+        }
+        dvmCompilerFreeTemp(cUnit, rScratch);
+    }
+
+    if (rBase == rFP) {
+        if (store != NULL)
+            annotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+                                    false /* isLoad */);
+        if (store2 != NULL)
+            annotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
+                                    false /* isLoad */);
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (store != NULL && cUnit->heapMemOp)
+        store->flags.insertWrapper = true;
+    if (store2 != NULL && cUnit->heapMemOp)
+        store2->flags.insertWrapper = true;
+#endif
+    return res;
+}
+
+static MipsLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static MipsLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+                                 int displacement, int rSrcLo, int rSrcHi)
+{
+    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    storeWordDisp(cUnit, base, LOWORD_OFFSET, lowReg);
+    storeWordDisp(cUnit, base, HIWORD_OFFSET, highReg);
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+    loadWordDisp(cUnit, base, LOWORD_OFFSET , lowReg);
+    loadWordDisp(cUnit, base, HIWORD_OFFSET , highReg);
+}
+
+static MipsLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR* res;
+    MipsOpCode opcode;
+#ifdef __mips_hard_float
+    if (FPREG(rDest) || FPREG(rSrc))
+        return fpRegCopy(cUnit, rDest, rSrc);
+#endif
+    res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    opcode = kMipsMove;
+    assert(LOWREG(rDest) && LOWREG(rSrc));
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    res->opcode = opcode;
+    setupResourceMasks(res);
+    if (rDest == rSrc) {
+        res->flags.isNop = true;
+    }
+    return res;
+}
+
+static MipsLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    MipsLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+    dvmCompilerAppendLIR(cUnit, (LIR*)res);
+    return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+                           int srcLo, int srcHi)
+{
+#ifdef __mips_hard_float
+    bool destFP = FPREG(destLo) && FPREG(destHi);
+    bool srcFP = FPREG(srcLo) && FPREG(srcHi);
+    assert(FPREG(srcLo) == FPREG(srcHi));
+    assert(FPREG(destLo) == FPREG(destHi));
+    if (destFP) {
+        if (srcFP) {
+            genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
+        } else {
+           /* note the operands are swapped for the mtc1 instr */
+            newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
+            newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
+        }
+    } else {
+        if (srcFP) {
+            newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
+            newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
+        } else {
+            // Handle overlap
+            if (srcHi == destLo) {
+                genRegCopy(cUnit, destHi, srcHi);
+                genRegCopy(cUnit, destLo, srcLo);
+            } else {
+                genRegCopy(cUnit, destLo, srcLo);
+                genRegCopy(cUnit, destHi, srcHi);
+            }
+        }
+    }
+#else
+    // Handle overlap
+    if (srcHi == destLo) {
+        genRegCopy(cUnit, destHi, srcHi);
+        genRegCopy(cUnit, destLo, srcLo);
+    } else {
+        genRegCopy(cUnit, destLo, srcLo);
+        genRegCopy(cUnit, destHi, srcHi);
+    }
+#endif
+}
+
+static inline MipsLIR *genRegImmCheck(CompilationUnit *cUnit,
+                                     MipsConditionCode cond, int reg,
+                                     int checkValue, int dOffset,
+                                     MipsLIR *pcrLabel)
+{
+    MipsLIR *branch = NULL;
+
+    if (checkValue == 0) {
+        MipsOpCode opc = kMipsNop;
+        if (cond == kMipsCondEq) {
+            opc = kMipsBeqz;
+	} else if (cond == kMipsCondNe) {
+            opc = kMipsBnez;
+        } else if (cond == kMipsCondLt || cond == kMipsCondMi) {
+            opc = kMipsBltz;
+        } else if (cond == kMipsCondLe) {
+            opc = kMipsBlez;
+        } else if (cond == kMipsCondGt) {
+            opc = kMipsBgtz;
+        } else if (cond == kMipsCondGe) {
+            opc = kMipsBgez;
+        } else {
+            ALOGE("Jit: bad case in genRegImmCheck");
+            dvmCompilerAbort(cUnit);
+        }
+        branch = opCompareBranch(cUnit, opc, reg, -1);
+    } else if (IS_SIMM16(checkValue)) {
+        if (cond == kMipsCondLt) {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR3(cUnit, kMipsSlti, tReg, reg, checkValue);
+            branch = opCompareBranch(cUnit, kMipsBne, tReg, r_ZERO);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        } else {
+            ALOGE("Jit: bad case in genRegImmCheck");
+            dvmCompilerAbort(cUnit);
+        }
+    } else {
+        ALOGE("Jit: bad case in genRegImmCheck");
+        dvmCompilerAbort(cUnit);
+    }
+
+    if (cUnit->jitMode == kJitMethod) {
+        BasicBlock *bb = cUnit->curBlock;
+        if (bb->taken) {
+            MipsLIR  *exceptionLabel = (MipsLIR *) cUnit->blockLabelList;
+            exceptionLabel += bb->taken->id;
+            branch->generic.target = (LIR *) exceptionLabel;
+            return exceptionLabel;
+        } else {
+            ALOGE("Catch blocks not handled yet");
+            dvmAbort();
+            return NULL;
+        }
+    } else {
+        return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+    }
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
+                                         MipsLIR *origLIR) {
+// DOUGLAS - this still needs to be implemented for MIPS.
+#if 0
+    /*
+     * We need two separate pushes, since we want r5 to be pushed first.
+     * Store multiple will push LR first.
+     */
+    MipsLIR *pushFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pushFP->opcode = kThumbPush;
+    pushFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(pushFP);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);
+
+    MipsLIR *pushLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    pushLR->opcode = kThumbPush;
+    /* Thumb push can handle LR, but is encoded differently at bit 8 */
+    pushLR->operands[0] = 1 << 8;
+    setupResourceMasks(pushLR);
+    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
+#endif
+}
+
+static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
+                                         MipsLIR *origLIR) {
+// DOUGLAS - this still needs to be implemented for MIPS.
+#if 0
+    /*
+     * Since Thumb cannot pop memory content into LR, we have to pop LR
+     * to a temp first (r5 in this case). Then we move r5 to LR, then pop the
+     * original r5 from stack.
+     */
+    /* Pop memory content(LR) into r5 first */
+    MipsLIR *popForLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    popForLR->opcode = kThumbPop;
+    popForLR->operands[0] = 1 << r5FP;
+    setupResourceMasks(popForLR);
+    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);
+
+    MipsLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
+    dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);
+
+    /* Now restore the original r5 */
+    MipsLIR *popFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
+    popFP->opcode = kThumbPop;
+    popFP->operands[0] = 1 << r5FP;
+    setupResourceMasks(popFP);
+    dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
+#endif
+}
+#endif
diff --git a/vm/compiler/codegen/mips/Mips32/Gen.cpp b/vm/compiler/codegen/mips/Mips32/Gen.cpp
new file mode 100644
index 0000000..29c7c5f
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Gen.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Mips ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Reserve 8 bytes at the beginning of the trace
+ *        +----------------------------+
+ *        | prof count addr (4 bytes)  |
+ *        +----------------------------+
+ *        | chain cell offset (4 bytes)|
+ *        +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (24 bytes)
+ *       lahi  a0, addr    # get ptr to prof count addr into a0
+ *       lalo  a0, addr
+ *       lw    a0, 0(a0)   # read prof count addr into a0
+ *       lw    a1, 0(a0)   # read prof count into a1
+ *       addiu a1, a1, 1   # increment count
+ *       sw    a1, 0(a0)   # store count
+ *
+ * For periodic profiling (8 bytes)
+ *       call  TEMPLATE_PERIODIC_PROFILING
+ *       nop
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+    assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+    MipsLIR *executionCount = newLIR1(cUnit, kMips32BitData, addr);
+    cUnit->chainCellOffsetLIR =
+        (LIR *) newLIR1(cUnit, kMips32BitData, CHAIN_CELL_OFFSET_TAG);
+    cUnit->headerSize = 8;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        MipsLIR *loadAddr = newLIR2(cUnit, kMipsLahi, r_A0, 0);
+        loadAddr->generic.target = (LIR *) executionCount;
+        loadAddr = newLIR3(cUnit, kMipsLalo, r_A0, r_A0, 0);
+        loadAddr ->generic.target = (LIR *) executionCount;
+        newLIR3(cUnit, kMipsLw, r_A0, 0, r_A0);
+        newLIR3(cUnit, kMipsLw, r_A1, 0, r_A0);
+        newLIR3(cUnit, kMipsAddiu, r_A1, r_A1, 1);
+        newLIR3(cUnit, kMipsSw, r_A1, 0, r_A0);
+        return 24;
+    } else {
+        int opcode = TEMPLATE_PERIODIC_PROFILING;
+        newLIR1(cUnit, kMipsJal,
+            (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+        newLIR0(cUnit, kMipsNop); /* delay slot */
+        return 8;
+    }
+}
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+                        RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
+                rlSrc.lowReg, 0x80000000);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+                         RegLocation rlSrc)
+{
+    RegLocation rlResult;
+    rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+    opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
+                        0x80000000);
+    genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+    genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static bool partialOverlap(int sreg1, int sreg2)
+{
+    return abs(sreg1 - sreg2) == 1;
+}
+
+static void withCarryHelper(CompilationUnit *cUnit, MipsOpCode opc,
+                            RegLocation rlDest, RegLocation rlSrc1,
+                            RegLocation rlSrc2, int sltuSrc1, int sltuSrc2)
+{
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    newLIR3(cUnit, opc, rlDest.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+    newLIR3(cUnit, kMipsSltu, tReg, sltuSrc1, sltuSrc2);
+    newLIR3(cUnit, opc, rlDest.highReg, rlSrc1.highReg, rlSrc2.highReg);
+    newLIR3(cUnit, opc, rlDest.highReg, rlDest.highReg, tReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+                         OpKind secondOp, RegLocation rlDest,
+                         RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    int carryOp = (secondOp == kOpAdc || secondOp == kOpSbc);
+
+    if (partialOverlap(rlSrc1.sRegLow,rlSrc2.sRegLow) ||
+        partialOverlap(rlSrc1.sRegLow,rlDest.sRegLow) ||
+        partialOverlap(rlSrc2.sRegLow,rlDest.sRegLow)) {
+        // Rare case - not enough registers to properly handle
+        genInterpSingleStep(cUnit, mir);
+    } else if (rlDest.sRegLow == rlSrc1.sRegLow) {
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlResult.lowReg, rlSrc2.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlResult.highReg, rlSrc2.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlResult, rlSrc2,
+                            rlResult.lowReg, rlSrc2.lowReg);
+        } else {
+            int tReg = dvmCompilerAllocTemp(cUnit);
+            newLIR2(cUnit, kMipsMove, tReg, rlResult.lowReg);
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlResult, rlSrc2,
+                            tReg, rlResult.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else if (rlDest.sRegLow == rlSrc2.sRegLow) {
+        rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlResult.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlResult.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlSrc1, rlResult,
+                            rlResult.lowReg, rlSrc1.lowReg);
+        } else {
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlSrc1, rlResult,
+                            rlSrc1.lowReg, rlResult.lowReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    } else {
+        rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+        rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+        if (!carryOp) {
+            opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+            opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, rlSrc2.highReg);
+        } else if (secondOp == kOpAdc) {
+            withCarryHelper(cUnit, kMipsAddu, rlResult, rlSrc1, rlSrc2,
+                            rlResult.lowReg, rlSrc1.lowReg);
+        } else {
+            withCarryHelper(cUnit, kMipsSubu, rlResult, rlSrc1, rlSrc2,
+                            rlSrc1.lowReg, rlResult.lowReg);
+        }
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+    int numTemps = sizeof(coreTemps)/sizeof(int);
+    RegisterPool *pool = (RegisterPool *) dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps =
+            (RegisterInfo *) dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+#ifdef __mips_hard_float
+    int numFPTemps = sizeof(fpTemps)/sizeof(int);
+    pool->numFPTemps = numFPTemps;
+    pool->FPTemps =
+            (RegisterInfo *) dvmCompilerNew(numFPTemps * sizeof(*pool->FPTemps), true);
+    dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+#else
+    pool->numFPTemps = 0;
+    pool->FPTemps = NULL;
+    dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+#endif
+    pool->nullCheckedRegs =
+        dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static MipsLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+    MipsLIR *res;
+    int rDPC = dvmCompilerAllocTemp(cUnit);
+    int rAddr = dvmCompilerAllocTemp(cUnit);
+    int offset = offsetof(StackSaveArea, xtra.currentPc);
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    newLIR3(cUnit, kMipsAddiu, rAddr, rFP, -(sizeof(StackSaveArea) - offset));
+    storeWordDisp( cUnit, rAddr, 0, rDPC);
+    return res;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+    genMonitorPortable(cUnit, mir);
+}
+
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+                       RegLocation rlSrc1, RegLocation rlSrc2)
+{
+    RegLocation rlResult;
+    loadValueDirectWideFixed(cUnit, rlSrc1, r_ARG0, r_ARG1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r_ARG2, r_ARG3);
+    genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+    rlResult = dvmCompilerGetReturn(cUnit);
+    storeValue(cUnit, rlDest, rlResult);
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    int reg0 = loadValue(cUnit, rlSrc, kCoreReg).lowReg;
+#if __mips_isa_rev>=2
+    newLIR4(cUnit, kMipsExt, reg0, reg0, 0, 31-1 /* size-1 */);
+#else
+    newLIR2(cUnit, kMipsSll, reg0, 1);
+    newLIR2(cUnit, kMipsSrl, reg0, 1);
+#endif
+    storeWordDisp(cUnit, rSELF, offset, reg0);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reg0);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+    RegLocation regSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+    int reglo = regSrc.lowReg;
+    int reghi = regSrc.highReg;
+    storeWordDisp(cUnit, rSELF, offset + LOWORD_OFFSET, reglo);
+#if __mips_isa_rev>=2
+    newLIR4(cUnit, kMipsExt, reghi, reghi, 0, 31-1 /* size-1 */);
+#else
+    newLIR2(cUnit, kMipsSll, reghi, 1);
+    newLIR2(cUnit, kMipsSrl, reghi, 1);
+#endif
+    storeWordDisp(cUnit, rSELF, offset + HIWORD_OFFSET, reghi);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reghi);
+    return false;
+}
+
+/* No select in thumb, so we need to branch.  Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    int offset = offsetof(Thread, interpSave.retval);
+    RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+    RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+    int reg0 = loadValue(cUnit, rlSrc1, kCoreReg).lowReg;
+    int reg1 = loadValue(cUnit, rlSrc2, kCoreReg).lowReg;
+    int tReg = dvmCompilerAllocTemp(cUnit);
+    if (isMin) {
+       newLIR3(cUnit, kMipsSlt, tReg, reg0, reg1);
+    }
+    else {
+       newLIR3(cUnit, kMipsSlt, tReg, reg1, reg0);
+    }
+    newLIR3(cUnit, kMipsMovz, reg0, reg1, tReg);
+    dvmCompilerFreeTemp(cUnit, tReg);
+    newLIR3(cUnit, kMipsSw, reg0, offset, rSELF);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit,reg0);
+    return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+        RegLocation rlSrc, RegLocation rlResult, int lit,
+        int firstBit, int secondBit)
+{
+    // We can't implement "add src, src, src, lsl#shift" on Thumb, so we have
+    // to do a regular multiply.
+    opRegRegImm(cUnit, kOpMul, rlResult.lowReg, rlSrc.lowReg, lit);
+}
diff --git a/vm/compiler/codegen/mips/Mips32/Ralloc.cpp b/vm/compiler/codegen/mips/Mips32/Ralloc.cpp
new file mode 100644
index 0000000..6810131
--- /dev/null
+++ b/vm/compiler/codegen/mips/Mips32/Ralloc.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains codegen for the Mips ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Alloc a pair of core registers, or a double.  Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit, bool fpHint,
+                                  int regClass)
+{
+    int highReg;
+    int lowReg;
+    int res = 0;
+
+#ifdef __mips_hard_float
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
+        lowReg = dvmCompilerAllocTempDouble(cUnit);
+        highReg = lowReg + 1;
+        res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+        return res;
+    }
+#endif
+
+    lowReg = dvmCompilerAllocTemp(cUnit);
+    highReg = dvmCompilerAllocTemp(cUnit);
+    res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+    return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
+{
+#ifdef __mips_hard_float
+    if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
+{
+        return dvmCompilerAllocTempFloat(cUnit);
+}
+#endif
+    return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/mips/MipsLIR.h b/vm/compiler/codegen/mips/MipsLIR.h
new file mode 100644
index 0000000..de40755
--- /dev/null
+++ b/vm/compiler/codegen/mips/MipsLIR.h
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
+
+#include "Dalvik.h"
+#include "compiler/CompilerInternals.h"
+
+/*
+ * zero is always the value 0
+ * at is scratch for Jit (normally used as temp reg by assembler)
+ * v0, v1 are scratch for Jit (normally hold subroutine return values)
+ * a0-a3 are scratch for Jit (normally hold subroutine arguments)
+ * t0-t7 are scratch for Jit
+ * t8 is scratch for Jit
+ * t9 is scratch for Jit (normally used for function calls)
+ * s0 (rFP) is reserved [holds Dalvik frame pointer]
+ * s1 (rSELF) is reserved [holds current &Thread]
+ * s2 (rINST) is scratch for Jit
+ * s3 (rIBASE) is scratch for Jit
+ * s4-s7 are scratch for Jit
+ * k0, k1 are reserved for use by interrupt handlers
+ * gp is reserved for global pointer
+ * sp is reserved
+ * s8 is scratch for Jit
+ * ra is scratch for Jit (normally holds the return addr)
+ *
+ * Preserved across C calls: s0-s8
+ * Trashed across C calls: at, v0-v1, a0-a3, t0-t9, gp, ra
+ *
+ * Floating pointer registers
+ * NOTE: there are 32 fp registers (16 df pairs), but current Jit code
+ *       only support 16 fp registers (8 df pairs).
+ * f0-f15
+ * df0-df7, where df0={f0,f1}, df1={f2,f3}, ... , df7={f14,f15}
+ *
+ * f0-f15 (df0-df7) trashed across C calls
+ *
+ * For mips32 code use:
+ *      a0-a3 to hold operands
+ *      v0-v1 to hold results
+ *      t0-t9 for temps
+ *
+ * All jump/branch instructions have a delay slot after it.
+ *
+ */
+
+/* Offset to distingish FP regs */
+#define FP_REG_OFFSET 32
+/* Offset to distinguish DP FP regs */
+#define FP_DOUBLE 64
+/* Offset to distingish the extra regs */
+#define EXTRA_REG_OFFSET 128
+/* Reg types */
+#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
+#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
+#define EXTRAREG(x) ((x & EXTRA_REG_OFFSET) == EXTRA_REG_OFFSET)
+#define LOWREG(x) ((x & 0x1f) == x)
+#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
+#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area.  Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define S2D(x,y) ((x) | FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define FP_REG_MASK (FP_REG_OFFSET-1)
+/* non-existent Dalvik register */
+#define vNone   (-1)
+/* non-existant physical register */
+#define rNone   (-1)
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define LOWORD_OFFSET 0
+#define HIWORD_OFFSET 4
+#define r_ARG0 r_A0
+#define r_ARG1 r_A1
+#define r_ARG2 r_A2
+#define r_ARG3 r_A3
+#define r_RESULT0 r_V0
+#define r_RESULT1 r_V1
+#else
+#define LOWORD_OFFSET 4
+#define HIWORD_OFFSET 0
+#define r_ARG0 r_A1
+#define r_ARG1 r_A0
+#define r_ARG2 r_A3
+#define r_ARG3 r_A2
+#define r_RESULT0 r_V1
+#define r_RESULT1 r_V0
+#endif
+
+/* These are the same for both big and little endian. */
+#define r_FARG0 r_F12
+#define r_FARG1 r_F13
+#define r_FRESULT0 r_F0
+#define r_FRESULT1 r_F1
+
+/* RegisterLocation templates return values (r_V0, or r_V0/r_V1) */
+#define LOC_C_RETURN {kLocPhysReg, 0, 0, r_V0, 0, -1}
+#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, r_RESULT0, r_RESULT1, -1}
+#define LOC_C_RETURN_ALT {kLocPhysReg, 0, 1, r_F0, 0, -1}
+#define LOC_C_RETURN_WIDE_ALT {kLocPhysReg, 1, 1, r_FRESULT0, r_FRESULT1, -1}
+/* RegisterLocation templates for interpState->retVal; */
+#define LOC_DALVIK_RETURN_VAL {kLocRetval, 0, 0, 0, 0, -1}
+#define LOC_DALVIK_RETURN_VAL_WIDE {kLocRetval, 1, 0, 0, 0, -1}
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+typedef struct RegisterInfo {
+    int reg;                    // Reg number
+    bool inUse;                 // Has it been allocated?
+    bool pair;                  // Part of a register pair?
+    int partner;                // If pair, other reg of pair
+    bool live;                  // Is there an associated SSA name?
+    bool dirty;                 // If live, is it dirty?
+    int sReg;                   // Name of live value
+    struct LIR *defStart;       // Starting inst in last def sequence
+    struct LIR *defEnd;         // Ending inst in last def sequence
+} RegisterInfo;
+
+typedef struct RegisterPool {
+    BitVector *nullCheckedRegs; // Track which registers have been null-checked
+    int numCoreTemps;
+    RegisterInfo *coreTemps;
+    int nextCoreTemp;
+    int numFPTemps;
+    RegisterInfo *FPTemps;
+    int nextFPTemp;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+    kGPReg0     = 0,
+    kRegSP      = 29,
+    kRegLR      = 31,
+    kFPReg0     = 32, /* only 16 fp regs supported currently */
+    kFPRegEnd   = 48,
+    kRegHI      = kFPRegEnd,
+    kRegLO,
+    kRegPC,
+    kRegEnd     = 51,
+    kCCode      = kRegEnd,
+    kFPStatus,          // FP status word
+    // The following four bits are for memory disambiguation
+    kDalvikReg,         // 1 Dalvik Frame (can be fully disambiguated)
+    kLiteral,           // 2 Literal pool (can be fully disambiguated)
+    kHeapRef,           // 3 Somewhere on the heap (alias with any other heap)
+    kMustNotAlias,      // 4 Guaranteed to be non-alias (eg *(r6+x))
+} ResourceEncodingPos;
+
+#define ENCODE_REG_LIST(N)      ((u8) N)
+#define ENCODE_REG_SP           (1ULL << kRegSP)
+#define ENCODE_REG_LR           (1ULL << kRegLR)
+#define ENCODE_REG_PC           (1ULL << kRegPC)
+#define ENCODE_CCODE            (1ULL << kCCode)
+#define ENCODE_FP_STATUS        (1ULL << kFPStatus)
+
+/* Abstract memory locations */
+#define ENCODE_DALVIK_REG       (1ULL << kDalvikReg)
+#define ENCODE_LITERAL          (1ULL << kLiteral)
+#define ENCODE_HEAP_REF         (1ULL << kHeapRef)
+#define ENCODE_MUST_NOT_ALIAS   (1ULL << kMustNotAlias)
+
+#define ENCODE_ALL              (~0ULL)
+#define ENCODE_MEM              (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
+                                 ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+#define DECODE_ALIAS_INFO_REG(X)        (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE(X)       ((X & 0x80000000) ? 1 : 0)
+
+typedef enum OpSize {
+    kWord,
+    kLong,
+    kSingle,
+    kDouble,
+    kUnsignedHalf,
+    kSignedHalf,
+    kUnsignedByte,
+    kSignedByte,
+} OpSize;
+
+typedef enum OpKind {
+    kOpMov,
+    kOpMvn,
+    kOpCmp,
+    kOpLsl,
+    kOpLsr,
+    kOpAsr,
+    kOpRor,
+    kOpNot,
+    kOpAnd,
+    kOpOr,
+    kOpXor,
+    kOpNeg,
+    kOpAdd,
+    kOpAdc,
+    kOpSub,
+    kOpSbc,
+    kOpRsub,
+    kOpMul,
+    kOpDiv,
+    kOpRem,
+    kOpBic,
+    kOpCmn,
+    kOpTst,
+    kOpBkpt,
+    kOpBlx,
+    kOpPush,
+    kOpPop,
+    kOp2Char,
+    kOp2Short,
+    kOp2Byte,
+    kOpCondBr,
+    kOpUncondBr,
+} OpKind;
+
+/*
+ * Annotate special-purpose core registers:
+ *
+ * rPC, rFP, and rSELF are for architecture-independent code to use.
+ */
+typedef enum NativeRegisterPool {
+    r_ZERO = 0,
+    r_AT = 1,
+    r_V0 = 2,
+    r_V1 = 3,
+    r_A0 = 4,
+    r_A1 = 5,
+    r_A2 = 6,
+    r_A3 = 7,
+    r_T0 = 8,
+    r_T1 = 9,
+    r_T2 = 10,
+    r_T3 = 11,
+    r_T4 = 12,
+    r_T5 = 13,
+    r_T6 = 14,
+    r_T7 = 15,
+    r_S0 = 16,
+    r_S1 = 17,
+    r_S2 = 18,
+    r_S3 = 19,
+    r_S4 = 20,
+    r_S5 = 21,
+    r_S6 = 22,
+    r_S7 = 23,
+    r_T8 = 24,
+    r_T9 = 25,
+    r_K0 = 26,
+    r_K1 = 27,
+    r_GP = 28,
+    r_SP = 29,
+    r_FP = 30,
+    r_RA = 31,
+
+    r_F0 = 0 + FP_REG_OFFSET,
+    r_F1,
+    r_F2,
+    r_F3,
+    r_F4,
+    r_F5,
+    r_F6,
+    r_F7,
+    r_F8,
+    r_F9,
+    r_F10,
+    r_F11,
+    r_F12,
+    r_F13,
+    r_F14,
+    r_F15,
+#if 0 /* only 16 fp regs supported currently */
+    r_F16,
+    r_F17,
+    r_F18,
+    r_F19,
+    r_F20,
+    r_F21,
+    r_F22,
+    r_F23,
+    r_F24,
+    r_F25,
+    r_F26,
+    r_F27,
+    r_F28,
+    r_F29,
+    r_F30,
+    r_F31,
+#endif
+    r_DF0 = r_F0 + FP_DOUBLE,
+    r_DF1 = r_F2 + FP_DOUBLE,
+    r_DF2 = r_F4 + FP_DOUBLE,
+    r_DF3 = r_F6 + FP_DOUBLE,
+    r_DF4 = r_F8 + FP_DOUBLE,
+    r_DF5 = r_F10 + FP_DOUBLE,
+    r_DF6 = r_F12 + FP_DOUBLE,
+    r_DF7 = r_F14 + FP_DOUBLE,
+#if 0 /* only 16 fp regs supported currently */
+    r_DF8 = r_F16 + FP_DOUBLE,
+    r_DF9 = r_F18 + FP_DOUBLE,
+    r_DF10 = r_F20 + FP_DOUBLE,
+    r_DF11 = r_F22 + FP_DOUBLE,
+    r_DF12 = r_F24 + FP_DOUBLE,
+    r_DF13 = r_F26 + FP_DOUBLE,
+    r_DF14 = r_F28 + FP_DOUBLE,
+    r_DF15 = r_F30 + FP_DOUBLE,
+#endif
+    r_HI = EXTRA_REG_OFFSET,
+    r_LO,
+    r_PC,
+} NativeRegisterPool;
+
+
+/* must match gp offset used mterp/mips files */
+#define STACK_OFFSET_GP 84
+
+/* MIPSTODO: properly remap arm regs (dPC, dFP, dGLUE) and remove these mappings */
+#define r4PC r_S0
+#define rFP r_S1
+#define rSELF r_S2
+#define rINST r_S4
+
+/* Shift encodings */
+typedef enum MipsShiftEncodings {
+    kMipsLsl = 0x0,
+    kMipsLsr = 0x1,
+    kMipsAsr = 0x2,
+    kMipsRor = 0x3
+} MipsShiftEncodings;
+
+/* condition encodings */
+typedef enum MipsConditionCode {
+    kMipsCondEq = 0x0,    /* 0000 */
+    kMipsCondNe = 0x1,    /* 0001 */
+    kMipsCondCs = 0x2,    /* 0010 */
+    kMipsCondCc = 0x3,    /* 0011 */
+    kMipsCondMi = 0x4,    /* 0100 */
+    kMipsCondPl = 0x5,    /* 0101 */
+    kMipsCondVs = 0x6,    /* 0110 */
+    kMipsCondVc = 0x7,    /* 0111 */
+    kMipsCondHi = 0x8,    /* 1000 */
+    kMipsCondLs = 0x9,    /* 1001 */
+    kMipsCondGe = 0xa,    /* 1010 */
+    kMipsCondLt = 0xb,    /* 1011 */
+    kMipsCondGt = 0xc,    /* 1100 */
+    kMipsCondLe = 0xd,    /* 1101 */
+    kMipsCondAl = 0xe,    /* 1110 */
+    kMipsCondNv = 0xf,    /* 1111 */
+} MipsConditionCode;
+
+#define isPseudoOpCode(opCode) ((int)(opCode) < 0)
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding snippet positions will be defined in
+ * Assemble.c.
+ */
+typedef enum MipsOpCode {
+    kMipsChainingCellBottom = -18,
+    kMipsPseudoBarrier = -17,
+    kMipsPseudoExtended = -16,
+    kMipsPseudoSSARep = -15,
+    kMipsPseudoEntryBlock = -14,
+    kMipsPseudoExitBlock = -13,
+    kMipsPseudoTargetLabel = -12,
+    kMipsPseudoChainingCellBackwardBranch = -11,
+    kMipsPseudoChainingCellHot = -10,
+    kMipsPseudoChainingCellInvokePredicted = -9,
+    kMipsPseudoChainingCellInvokeSingleton = -8,
+    kMipsPseudoChainingCellNormal = -7,
+    kMipsPseudoDalvikByteCodeBoundary = -6,
+    kMipsPseudoPseudoAlign4 = -5,
+    kMipsPseudoPCReconstructionCell = -4,
+    kMipsPseudoPCReconstructionBlockLabel = -3,
+    kMipsPseudoEHBlockLabel = -2,
+    kMipsPseudoNormalBlockLabel = -1,
+
+    kMipsFirst,
+    kMips32BitData = kMipsFirst, /* data [31..0] */
+    kMipsAddiu,   /* addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] */
+    kMipsAddu,    /* add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001] */
+    kMipsAnd,     /* and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100] */
+    kMipsAndi,    /* andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0] */
+    kMipsB,       /* b o   [0001000000000000] o[15..0] */
+    kMipsBal,     /* bal o [0000010000010001] o[15..0] */
+    /* NOTE: the code tests the range kMipsBeq thru kMipsBne, so
+             adding an instruction in this range may require updates */
+    kMipsBeq,     /* beq s,t,o [000100] s[25..21] t[20..16] o[15..0] */
+    kMipsBeqz,    /* beqz s,o [000100] s[25..21] [00000] o[15..0] */
+    kMipsBgez,    /* bgez s,o [000001] s[25..21] [00001] o[15..0] */
+    kMipsBgtz,    /* bgtz s,o [000111] s[25..21] [00000] o[15..0] */
+    kMipsBlez,    /* blez s,o [000110] s[25..21] [00000] o[15..0] */
+    kMipsBltz,    /* bltz s,o [000001] s[25..21] [00000] o[15..0] */
+    kMipsBnez,    /* bnez s,o [000101] s[25..21] [00000] o[15..0] */
+    kMipsBne,     /* bne s,t,o [000101] s[25..21] t[20..16] o[15..0] */
+    kMipsDiv,     /* div s,t [000000] s[25..21] t[20..16] [0000000000011010] */
+#if __mips_isa_rev>=2
+    kMipsExt,     /* ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000] */
+#endif
+    kMipsJal,     /* jal t [000011] t[25..0] */
+    kMipsJalr,    /* jalr d,s [000000] s[25..21] [00000] d[15..11]
+                                  hint[10..6] [001001] */
+    kMipsJr,      /* jr s [000000] s[25..21] [0000000000] hint[10..6] [001000] */
+    kMipsLahi,    /* lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi */
+    kMipsLalo,    /* ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo */
+    kMipsLui,     /* lui t,imm16 [00111100000] t[20..16] imm16[15..0] */
+    kMipsLb,      /* lb t,o(b) [100000] b[25..21] t[20..16] o[15..0] */
+    kMipsLbu,     /* lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0] */
+    kMipsLh,      /* lh t,o(b) [100001] b[25..21] t[20..16] o[15..0] */
+    kMipsLhu,     /* lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0] */
+    kMipsLw,      /* lw t,o(b) [100011] b[25..21] t[20..16] o[15..0] */
+    kMipsMfhi,    /* mfhi d [0000000000000000] d[15..11] [00000010000] */
+    kMipsMflo,    /* mflo d [0000000000000000] d[15..11] [00000010010] */
+    kMipsMove,    /* move d,s [000000] s[25..21] [00000] d[15..11] [00000100101] */
+    kMipsMovz,    /* movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010] */
+    kMipsMul,     /* mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010] */
+    kMipsNop,     /* nop [00000000000000000000000000000000] */
+    kMipsNor,     /* nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111] */
+    kMipsOr,      /* or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101] */
+    kMipsOri,     /* ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] */
+    kMipsPref,    /* pref h,o(b) [101011] b[25..21] h[20..16] o[15..0] */
+    kMipsSb,      /* sb t,o(b) [101000] b[25..21] t[20..16] o[15..0] */
+#if __mips_isa_rev>=2
+    kMipsSeb,     /* seb d,t [01111100000] t[20..16] d[15..11] [10000100000] */
+    kMipsSeh,     /* seh d,t [01111100000] t[20..16] d[15..11] [11000100000] */
+#endif
+    kMipsSh,      /* sh t,o(b) [101001] b[25..21] t[20..16] o[15..0] */
+    kMipsSll,     /* sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000] */
+    kMipsSllv,    /* sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100] */
+    kMipsSlt,     /* slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010] */
+    kMipsSlti,    /* slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0] */
+    kMipsSltu,    /* sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011] */
+    kMipsSra,     /* sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011] */
+    kMipsSrav,    /* srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111] */
+    kMipsSrl,     /* srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010] */
+    kMipsSrlv,    /* srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110] */
+    kMipsSubu,    /* subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011] */
+    kMipsSw,      /* sw t,o(b) [101011] b[25..21] t[20..16] o[15..0] */
+    kMipsSync,    /* sync hint [000000000000000000000] hint[10..6] [001111] */
+    kMipsXor,     /* xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110] */
+    kMipsXori,    /* xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0] */
+#ifdef __mips_hard_float
+    kMipsFadds,   /* add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000] */
+    kMipsFsubs,   /* sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001] */
+    kMipsFmuls,   /* mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010] */
+    kMipsFdivs,   /* div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011] */
+    kMipsFaddd,   /* add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000] */
+    kMipsFsubd,   /* sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001] */
+    kMipsFmuld,   /* mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010] */
+    kMipsFdivd,   /* div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011] */
+    kMipsFcvtsd,  /* cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000] */
+    kMipsFcvtsw,  /* cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000] */
+    kMipsFcvtds,  /* cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001] */
+    kMipsFcvtdw,  /* cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001] */
+    kMipsFcvtws,  /* cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100] */
+    kMipsFcvtwd,  /* cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100] */
+    kMipsFmovs,   /* mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110] */
+    kMipsFmovd,   /* mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110] */
+    kMipsFlwc1,   /* lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0] */
+    kMipsFldc1,   /* ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0] */
+    kMipsFswc1,   /* swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0] */
+    kMipsFsdc1,   /* sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0] */
+    kMipsMfc1,    /* mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000] */
+    kMipsMtc1,    /* mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000] */
+#endif
+    kMipsUndefined,  /* undefined [011001xxxxxxxxxxxxxxxx] */
+    kMipsLast
+} MipsOpCode;
+
+/* Sync option encodings */
+typedef enum MipsSyncOptions {
+    /*
+     * sync guarantees ordering of Load/Store operations wrt itself
+     *
+     * Older: instruction classes that must be ordered before the sync instruction completes
+     * Younger: instruction classes that must be ordered after the sync instruction completes
+     * Global: instruction classes that must be performed globally when the sync completes
+     */
+                                /* Older        Younger         Global          */
+    kSY = 0x00,                 /* Load,Store   Load,Store      Load,Store      */
+    kWMB = 0x04,                /* Store        Store                           */
+    kMB = 0x10,                 /* Load,Store   Load,Store                      */
+    kACQUIRE = 0x11,            /* Load         Load,Store                      */
+    kRELEASE = 0x12,            /* Load,Store   Store                           */
+    kRMB = 0x13                 /* Load         Load                            */
+} MipsSyncOptions;
+
+/* Bit flags describing the behavior of each native opcode */
+typedef enum MipsOpFeatureFlags {
+    kIsBranch = 0,
+    kRegDef0,
+    kRegDef1,
+    kRegDefSP,
+    kRegDefLR,
+    kRegDefList0,
+    kRegDefList1,
+    kRegUse0,
+    kRegUse1,
+    kRegUse2,
+    kRegUse3,
+    kRegUseSP,
+    kRegUsePC,
+    kRegUseList0,
+    kRegUseList1,
+    kNoOperand,
+    kIsUnaryOp,
+    kIsBinaryOp,
+    kIsTertiaryOp,
+    kIsQuadOp,
+    kIsIT,
+    kSetsCCodes,
+    kUsesCCodes,
+    kMemLoad,
+    kMemStore,
+} MipsOpFeatureFlags;
+
+#define IS_LOAD         (1 << kMemLoad)
+#define IS_STORE        (1 << kMemStore)
+#define IS_BRANCH       (1 << kIsBranch)
+#define REG_DEF0        (1 << kRegDef0)
+#define REG_DEF1        (1 << kRegDef1)
+#define REG_DEF_SP      (1 << kRegDefSP)
+#define REG_DEF_LR      (1 << kRegDefLR)
+#define REG_DEF_LIST0   (1 << kRegDefList0)
+#define REG_DEF_LIST1   (1 << kRegDefList1)
+#define REG_USE0        (1 << kRegUse0)
+#define REG_USE1        (1 << kRegUse1)
+#define REG_USE2        (1 << kRegUse2)
+#define REG_USE3        (1 << kRegUse3)
+#define REG_USE_SP      (1 << kRegUseSP)
+#define REG_USE_PC      (1 << kRegUsePC)
+#define REG_USE_LIST0   (1 << kRegUseList0)
+#define REG_USE_LIST1   (1 << kRegUseList1)
+#define NO_OPERAND      (1 << kNoOperand)
+#define IS_UNARY_OP     (1 << kIsUnaryOp)
+#define IS_BINARY_OP    (1 << kIsBinaryOp)
+#define IS_TERTIARY_OP  (1 << kIsTertiaryOp)
+#define IS_QUAD_OP      (1 << kIsQuadOp)
+#define IS_IT           (1 << kIsIT)
+#define SETS_CCODES     (1 << kSetsCCodes)
+#define USES_CCODES     (1 << kUsesCCodes)
+
+/* Common combo register usage patterns */
+#define REG_USE01       (REG_USE0 | REG_USE1)
+#define REG_USE02       (REG_USE0 | REG_USE2)
+#define REG_USE012      (REG_USE01 | REG_USE2)
+#define REG_USE12       (REG_USE1 | REG_USE2)
+#define REG_USE23       (REG_USE2 | REG_USE3)
+#define REG_DEF01       (REG_DEF0 | REG_DEF1)
+#define REG_DEF0_USE0   (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE1   (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE2   (REG_DEF0 | REG_USE2)
+#define REG_DEF0_USE01  (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE12  (REG_DEF0 | REG_USE12)
+#define REG_DEF01_USE2  (REG_DEF0 | REG_DEF1 | REG_USE2)
+
+/* Instruction assembly fieldLoc kind */
+typedef enum MipsEncodingKind {
+    kFmtUnused,
+    kFmtBitBlt,        /* Bit string using end/start */
+    kFmtDfp,           /* Double FP reg */
+    kFmtSfp,           /* Single FP reg */
+} MipsEncodingKind;
+
+/* Struct used to define the snippet positions for each Thumb opcode */
+typedef struct MipsEncodingMap {
+    u4 skeleton;
+    struct {
+        MipsEncodingKind kind;
+        int end;   /* end for kFmtBitBlt, 1-bit slice end for FP regs */
+        int start; /* start for kFmtBitBlt, 4-bit slice end for FP regs */
+    } fieldLoc[4];
+    MipsOpCode opcode;
+    int flags;
+    const char *name;
+    const char* fmt;
+    int size;
+} MipsEncodingMap;
+
+/* Keys for target-specific scheduling and other optimization hints */
+typedef enum MipsTargetOptHints {
+    kMaxHoistDistance,
+} MipsTargetOptHints;
+
+extern MipsEncodingMap EncodingMap[kMipsLast];
+
+/*
+ * Each instance of this struct holds a pseudo or real LIR instruction:
+ * - pseudo ones (eg labels and marks) and will be discarded by the assembler.
+ * - real ones will be assembled into Thumb instructions.
+ *
+ * Machine resources are encoded into a 64-bit vector, where the encodings are
+ * as following:
+ * - [ 0..15]: general purpose registers including PC, SP, and LR
+ * - [16..47]: floating-point registers where d0 is expanded to s[01] and s0
+ *   starts at bit 16
+ * - [48]: IT block
+ * - [49]: integer condition code
+ * - [50]: floatint-point status word
+ */
+typedef struct MipsLIR {
+    LIR generic;
+    MipsOpCode opcode;
+    int operands[4];            // [0..3] = [dest, src1, src2, extra]
+    struct {
+        bool isNop:1;           // LIR is optimized away
+        bool insertWrapper:1;   // insert branch to emulate memory accesses
+        unsigned int age:4;     // default is 0, set lazily by the optimizer
+        unsigned int size:3;    // bytes (2 for thumb, 2/4 for thumb2)
+        unsigned int unused:23;
+    } flags;
+    int aliasInfo;              // For Dalvik register access & litpool disambiguation
+    u8 useMask;                 // Resource mask for use
+    u8 defMask;                 // Resource mask for def
+} MipsLIR;
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+#define PREDICTED_CHAIN_DELAY_SLOT_INIT  0
+#define PREDICTED_CHAIN_CLAZZ_INIT       0
+#define PREDICTED_CHAIN_METHOD_INIT      0
+#define PREDICTED_CHAIN_COUNTER_INIT     0
+
+/* Utility macros to traverse the LIR/MipsLIR list */
+#define NEXT_LIR(lir) ((MipsLIR *) lir->generic.next)
+#define PREV_LIR(lir) ((MipsLIR *) lir->generic.prev)
+
+#define NEXT_LIR_LVALUE(lir) (lir)->generic.next
+#define PREV_LIR_LVALUE(lir) (lir)->generic.prev
+
+#define CHAIN_CELL_OFFSET_TAG   0xcdabcdabL
+
+#define IS_UIMM16(v) ((0 <= (v)) && ((v) <= 65535))
+#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32766))
+#define IS_SIMM16_2WORD(v) ((-32764 <= (v)) && ((v) <= 32763)) /* 2 offsets must fit */
+
+#define CHAIN_CELL_NORMAL_SIZE    16
+#define CHAIN_CELL_PREDICTED_SIZE 20
+
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_MIPSLIR_H_
diff --git a/vm/compiler/codegen/mips/Ralloc.h b/vm/compiler/codegen/mips/Ralloc.h
new file mode 100644
index 0000000..33ad2fb
--- /dev/null
+++ b/vm/compiler/codegen/mips/Ralloc.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "compiler/codegen/mips/MipsLIR.h"
+
+/*
+ * Return most flexible allowed register class based on size.
+ * Bug: 2813841
+ * Must use a core register for data types narrower than word (due
+ * to possible unaligned load/store.
+ */
+static inline RegisterClass dvmCompilerRegClassBySize(OpSize size)
+{
+    return (size == kUnsignedHalf ||
+            size == kSignedHalf ||
+            size == kUnsignedByte ||
+            size == kSignedByte ) ? kCoreReg : kAnyReg;
+}
+
+static inline int dvmCompilerS2VReg(CompilationUnit *cUnit, int sReg)
+{
+    assert(sReg != INVALID_SREG);
+    return DECODE_REG(dvmConvertSSARegToDalvik(cUnit, sReg));
+}
+
+/* Reset the tracker to unknown state */
+static inline void dvmCompilerResetNullCheck(CompilationUnit *cUnit)
+{
+    dvmClearAllBits(cUnit->regPool->nullCheckedRegs);
+}
+
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+static inline int dvmCompilerSRegHi(int lowSreg) {
+    return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+
+static inline bool dvmCompilerLiveOut(CompilationUnit *cUnit, int sReg)
+{
+    //TODO: fully implement
+    return true;
+}
+
+static inline int dvmCompilerSSASrc(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numUses > num);
+    return mir->ssaRep->uses[num];
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update);
+/* Mark a temp register as dead.  Does not affect allocation state. */
+extern void dvmCompilerClobber(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit,
+                                        RegLocation loc);
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc);
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg);
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg,
+                                int highReg);
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl);
+
+/* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num);
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                               LIR *start, LIR *finish);
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                                   LIR *start, LIR *finish);
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high);
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high);
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num);
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num);
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit);
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit);
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg);
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit);
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit);
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit);
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc);
+
+//FIXME - this needs to also check the preserved pool.
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg);
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit);
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg);
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit);
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl);
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit);
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
diff --git a/vm/compiler/codegen/mips/RallocUtil.cpp b/vm/compiler/codegen/mips/RallocUtil.cpp
new file mode 100644
index 0000000..b13dddf
--- /dev/null
+++ b/vm/compiler/codegen/mips/RallocUtil.cpp
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "MipsLIR.h"
+#include "Codegen.h"
+#include "Ralloc.h"
+
+#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
+/*
+ * Get the "real" sreg number associated with an sReg slot.  In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array.  However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array.  Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+/*
+ * Free all allocated temps in the temp pools.  Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i < cUnit->regPool->numCoreTemps; i++) {
+        cUnit->regPool->coreTemps[i].inUse = false;
+    }
+    for (i=0; i < cUnit->regPool->numFPTemps; i++) {
+        cUnit->regPool->FPTemps[i].inUse = false;
+    }
+}
+
+ /* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num)
+{
+    int i;
+    for (i=0; i < num; i++) {
+        regs[i].reg = regNums[i];
+        regs[i].inUse = false;
+        regs[i].pair = false;
+        regs[i].live = false;
+        regs[i].dirty = false;
+        regs[i].sReg = INVALID_SREG;
+    }
+}
+
+static void dumpRegPool(RegisterInfo *p, int numRegs)
+{
+    int i;
+    ALOGE("================================================");
+    for (i=0; i < numRegs; i++ ){
+        ALOGE("R[%d]: U:%d, P:%d, part:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+           p[i].reg, p[i].inUse, p[i].pair, p[i].partner, p[i].live,
+           p[i].dirty, p[i].sReg,(int)p[i].defStart, (int)p[i].defEnd);
+    }
+    ALOGE("================================================");
+}
+
+static RegisterInfo *getRegInfo(CompilationUnit *cUnit, int reg)
+{
+    int numTemps = cUnit->regPool->numCoreTemps;
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    ALOGE("Tried to get info on a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+    return NULL;
+}
+
+static void flushRegWide(CompilationUnit *cUnit, int reg1, int reg2)
+{
+    RegisterInfo *info1 = getRegInfo(cUnit, reg1);
+    RegisterInfo *info2 = getRegInfo(cUnit, reg2);
+    assert(info1 && info2 && info1->pair && info2->pair &&
+           (info1->partner == info2->reg) &&
+           (info2->partner == info1->reg));
+    if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+        info1->dirty = false;
+        info2->dirty = false;
+        if (dvmCompilerS2VReg(cUnit, info2->sReg) <
+            dvmCompilerS2VReg(cUnit, info1->sReg))
+            info1 = info2;
+        dvmCompilerFlushRegWideImpl(cUnit, rFP,
+                                    dvmCompilerS2VReg(cUnit, info1->sReg) << 2,
+                                    info1->reg, info1->partner);
+    }
+}
+
+static void flushReg(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if (info->live && info->dirty) {
+        info->dirty = false;
+        dvmCompilerFlushRegImpl(cUnit, rFP,
+                                dvmCompilerS2VReg(cUnit, info->sReg) << 2,
+                                reg, kWord);
+    }
+}
+
+/* return true if found reg to clobber */
+static bool clobberRegBody(CompilationUnit *cUnit, RegisterInfo *p,
+                           int numTemps, int reg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            if (p[i].live && p[i].dirty) {
+                if (p[i].pair) {
+                    flushRegWide(cUnit, p[i].reg, p[i].partner);
+                } else {
+                    flushReg(cUnit, p[i].reg);
+                }
+            }
+            p[i].live = false;
+            p[i].sReg = INVALID_SREG;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+            if (p[i].pair) {
+                p[i].pair = false;
+                /* partners should be in same pool */
+                clobberRegBody(cUnit, p, numTemps, p[i].partner);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Mark a temp register as dead.  Does not affect allocation state. */
+void dvmCompilerClobber(CompilationUnit *cUnit, int reg)
+{
+    if (!clobberRegBody(cUnit, cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps, reg)) {
+        clobberRegBody(cUnit, cUnit->regPool->FPTemps,
+                       cUnit->regPool->numFPTemps, reg);
+    }
+}
+
+static void clobberSRegBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].sReg == sReg) {
+            p[i].live = false;
+            p[i].defStart = NULL;
+            p[i].defEnd = NULL;
+        }
+    }
+}
+
+/* Clobber any temp associated with an sReg.  Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg)
+{
+    clobberSRegBody(cUnit->regPool->coreTemps, cUnit->regPool->numCoreTemps,
+                    sReg);
+    clobberSRegBody(cUnit->regPool->FPTemps, cUnit->regPool->numFPTemps,
+                    sReg);
+}
+
+static int allocTempBody(CompilationUnit *cUnit, RegisterInfo *p, int numTemps,
+                         int *nextTemp, bool required)
+{
+    int i;
+    int next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next].live) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    next = *nextTemp;
+    for (i=0; i< numTemps; i++) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            p[next].inUse = true;
+            p[next].pair = false;
+            *nextTemp = next + 1;
+            return p[next].reg;
+        }
+        next++;
+    }
+    if (required) {
+        ALOGE("No free temp registers");
+        dvmCompilerAbort(cUnit);
+    }
+    return -1;  // No register available
+}
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit)
+{
+    RegisterInfo *p = cUnit->regPool->FPTemps;
+    int numTemps = cUnit->regPool->numFPTemps;
+    /* Cleanup - not all targets need aligned regs */
+    int start = cUnit->regPool->nextFPTemp + (cUnit->regPool->nextFPTemp & 1);
+    int next = start;
+    int i;
+
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if ((!p[next].inUse && !p[next].live) &&
+            (!p[next+1].inUse && !p[next+1].live)) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    next = start;
+    for (i=0; i < numTemps; i+=2) {
+        if (next >= numTemps)
+            next = 0;
+        if (!p[next].inUse && !p[next+1].inUse) {
+            dvmCompilerClobber(cUnit, p[next].reg);
+            dvmCompilerClobber(cUnit, p[next+1].reg);
+            p[next].inUse = true;
+            p[next+1].inUse = true;
+            assert((p[next].reg+1) == p[next+1].reg);
+            assert((p[next].reg & 0x1) == 0);
+            cUnit->regPool->nextFPTemp += 2;
+            return p[next].reg;
+        }
+        next += 2;
+    }
+    ALOGE("No free temp registers");
+    dvmCompilerAbort(cUnit);
+    return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+                         cUnit->regPool->numCoreTemps,
+                         &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit)
+{
+    return allocTempBody(cUnit, cUnit->regPool->FPTemps,
+                         cUnit->regPool->numFPTemps,
+                         &cUnit->regPool->nextFPTemp, true);
+}
+
+static RegisterInfo *allocLiveBody(RegisterInfo *p, int numTemps, int sReg)
+{
+    int i;
+    if (sReg == -1)
+        return NULL;
+    for (i=0; i < numTemps; i++) {
+        if (p[i].live && (p[i].sReg == sReg)) {
+            p[i].inUse = true;
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+static RegisterInfo *allocLive(CompilationUnit *cUnit, int sReg,
+                               int regClass)
+{
+    RegisterInfo *res = NULL;
+    switch(regClass) {
+        case kAnyReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            if (res)
+                break;
+            /* Intentional fallthrough */
+        case kCoreReg:
+            res = allocLiveBody(cUnit->regPool->coreTemps,
+                                cUnit->regPool->numCoreTemps, sReg);
+            break;
+        case kFPReg:
+            res = allocLiveBody(cUnit->regPool->FPTemps,
+                                cUnit->regPool->numFPTemps, sReg);
+            break;
+        default:
+            ALOGE("Invalid register type");
+            dvmCompilerAbort(cUnit);
+    }
+    return res;
+}
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = false;
+            p[i].pair = false;
+            return;
+        }
+    }
+    ALOGE("Tried to free a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+/*
+ * FIXME - this needs to also check the preserved pool once we start
+ * start using preserved registers.
+ */
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return p[i].live ? &p[i] : NULL;
+        }
+    }
+    return NULL;
+}
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            return &p[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register.  No check is made to see if the register was previously
+ * allocated.  Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = cUnit->regPool->coreTemps;
+    int numTemps = cUnit->regPool->numCoreTemps;
+    int i;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    p = cUnit->regPool->FPTemps;
+    numTemps = cUnit->regPool->numFPTemps;
+    for (i=0; i< numTemps; i++) {
+        if (p[i].reg == reg) {
+            p[i].inUse = true;
+            p[i].live = false;
+            return;
+        }
+    }
+    ALOGE("Tried to lock a non-existant temp: r%d",reg);
+    dvmCompilerAbort(cUnit);
+}
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit)
+{
+    dvmCompilerClobber(cUnit, r_ZERO);
+    dvmCompilerClobber(cUnit, r_AT);
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerClobber(cUnit, r_V1);
+    dvmCompilerClobber(cUnit, r_A0);
+    dvmCompilerClobber(cUnit, r_A1);
+    dvmCompilerClobber(cUnit, r_A2);
+    dvmCompilerClobber(cUnit, r_A3);
+    dvmCompilerClobber(cUnit, r_T0);
+    dvmCompilerClobber(cUnit, r_T1);
+    dvmCompilerClobber(cUnit, r_T2);
+    dvmCompilerClobber(cUnit, r_T3);
+    dvmCompilerClobber(cUnit, r_T4);
+    dvmCompilerClobber(cUnit, r_T5);
+    dvmCompilerClobber(cUnit, r_T6);
+    dvmCompilerClobber(cUnit, r_T7);
+    dvmCompilerClobber(cUnit, r_T8);
+    dvmCompilerClobber(cUnit, r_T9);
+    dvmCompilerClobber(cUnit, r_K0);
+    dvmCompilerClobber(cUnit, r_K1);
+    dvmCompilerClobber(cUnit, r_GP);
+    dvmCompilerClobber(cUnit, r_FP);
+    dvmCompilerClobber(cUnit, r_RA);
+    dvmCompilerClobber(cUnit, r_HI);
+    dvmCompilerClobber(cUnit, r_LO);
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerClobber(cUnit, r_F1);
+    dvmCompilerClobber(cUnit, r_F2);
+    dvmCompilerClobber(cUnit, r_F3);
+    dvmCompilerClobber(cUnit, r_F4);
+    dvmCompilerClobber(cUnit, r_F5);
+    dvmCompilerClobber(cUnit, r_F6);
+    dvmCompilerClobber(cUnit, r_F7);
+    dvmCompilerClobber(cUnit, r_F8);
+    dvmCompilerClobber(cUnit, r_F9);
+    dvmCompilerClobber(cUnit, r_F10);
+    dvmCompilerClobber(cUnit, r_F11);
+    dvmCompilerClobber(cUnit, r_F12);
+    dvmCompilerClobber(cUnit, r_F13);
+    dvmCompilerClobber(cUnit, r_F14);
+    dvmCompilerClobber(cUnit, r_F15);
+}
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit)
+{
+    //TUNING: reduce the set of regs used by handlers.  Only a few need lots.
+    dvmCompilerClobberCallRegs(cUnit);
+    dvmCompilerClobber(cUnit, r_S0);
+    dvmCompilerClobber(cUnit, r_S1);
+    dvmCompilerClobber(cUnit, r_S2);
+    dvmCompilerClobber(cUnit, r_S3);
+    dvmCompilerClobber(cUnit, r_S4);
+    dvmCompilerClobber(cUnit, r_S5);
+    dvmCompilerClobber(cUnit, r_S6);
+    dvmCompilerClobber(cUnit, r_S7);
+}
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *p = getRegInfo(cUnit, reg);
+    p->defStart = NULL;
+    p->defEnd = NULL;
+}
+
+static void nullifyRange(CompilationUnit *cUnit, LIR *start, LIR *finish,
+                         int sReg1, int sReg2)
+{
+    if (start && finish) {
+        LIR *p;
+        assert(sReg1 == sReg2);
+        for (p = start; ;p = p->next) {
+            ((MipsLIR *)p)->flags.isNop = true;
+            if (p == finish)
+                break;
+        }
+    }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+                    LIR *start, LIR *finish)
+{
+    assert(!rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence.  Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+                        LIR *start, LIR *finish)
+{
+    assert(rl.wide);
+    assert(start && start->next);
+    assert(finish);
+    RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);  // Only track low of pair
+    p->defStart = start->next;
+    p->defEnd = finish;
+}
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+                                           RegLocation rl)
+{
+    assert(rl.wide);
+    if (rl.location == kLocPhysReg) {
+        RegisterInfo *infoLo = getRegInfo(cUnit, rl.lowReg);
+        RegisterInfo *infoHi = getRegInfo(cUnit, rl.highReg);
+        if (!infoLo->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoLo->pair);
+        }
+        if (!infoHi->pair) {
+            dumpRegPool(cUnit->regPool->coreTemps,
+                        cUnit->regPool->numCoreTemps);
+            assert(infoHi->pair);
+        }
+        assert(infoLo->pair);
+        assert(infoHi->pair);
+        assert(infoLo->partner == infoHi->reg);
+        assert(infoHi->partner == infoLo->reg);
+        infoLo->pair = false;
+        infoHi->pair = false;
+        infoLo->defStart = NULL;
+        infoLo->defEnd = NULL;
+        infoHi->defStart = NULL;
+        infoHi->defEnd = NULL;
+    }
+#ifndef HAVE_LITTLE_ENDIAN
+    else if (rl.location == kLocDalvikFrame) {
+        rl.sRegLow = dvmCompilerSRegHi(rl.sRegLow);
+    }
+#endif
+
+    rl.wide = false;
+    return rl;
+}
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(!rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(!p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+}
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl)
+{
+    assert(rl.wide);
+    if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+        RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+        assert(p->pair);
+        nullifyRange(cUnit, p->defStart, p->defEnd,
+                     p->sReg, rl.sRegLow);
+    }
+    dvmCompilerResetDef(cUnit, rl.lowReg);
+    dvmCompilerResetDef(cUnit, rl.highReg);
+}
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerResetDef(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+    for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+        dvmCompilerClobber(cUnit, cUnit->regPool->FPTemps[i].reg);
+    }
+}
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit)
+{
+    int i;
+    for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+        dvmCompilerLockTemp(cUnit, cUnit->regPool->coreTemps[i].reg);
+    }
+}
+
+// Make sure nothing is live and dirty
+static void flushAllRegsBody(CompilationUnit *cUnit, RegisterInfo *info,
+                             int numRegs)
+{
+    int i;
+    for (i=0; i < numRegs; i++) {
+        if (info[i].live && info[i].dirty) {
+            if (info[i].pair) {
+                flushRegWide(cUnit, info[i].reg, info[i].partner);
+            } else {
+                flushReg(cUnit, info[i].reg);
+            }
+        }
+    }
+}
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit)
+{
+    flushAllRegsBody(cUnit, cUnit->regPool->coreTemps,
+                     cUnit->regPool->numCoreTemps);
+    flushAllRegsBody(cUnit, cUnit->regPool->FPTemps,
+                     cUnit->regPool->numFPTemps);
+    dvmCompilerClobberAllRegs(cUnit);
+}
+
+
+//TUNING: rewrite all of this reg stuff.  Probably use an attribute table
+static bool regClassMatches(int regClass, int reg)
+{
+    if (regClass == kAnyReg) {
+        return true;
+    } else if (regClass == kCoreReg) {
+        return !FPREG(reg);
+    } else {
+        return FPREG(reg);
+    }
+}
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
+        return;  /* already live */
+    } else if (sReg != INVALID_SREG) {
+        dvmCompilerClobberSReg(cUnit, sReg);
+        info->live = true;
+    } else {
+        /* Can't be live if no associated sReg */
+        info->live = false;
+    }
+    info->sReg = sReg;
+}
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg, int highReg)
+{
+    RegisterInfo *infoLo = getRegInfo(cUnit, lowReg);
+    RegisterInfo *infoHi = getRegInfo(cUnit, highReg);
+    infoLo->pair = infoHi->pair = true;
+    infoLo->partner = highReg;
+    infoHi->partner = lowReg;
+}
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = false;
+}
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg)
+{
+    RegisterInfo *info = getRegInfo(cUnit, reg);
+    info->dirty = true;
+}
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg)
+{
+      RegisterInfo *info = getRegInfo(cUnit, reg);
+          info->inUse = true;
+}
+
+void copyRegInfo(CompilationUnit *cUnit, int newReg, int oldReg)
+{
+    RegisterInfo *newInfo = getRegInfo(cUnit, newReg);
+    RegisterInfo *oldInfo = getRegInfo(cUnit, oldReg);
+    *newInfo = *oldInfo;
+    newInfo->reg = newReg;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact.  No code
+ * is generated.  The the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs.  Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit, RegLocation loc)
+{
+    assert(!loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        if (infoLo) {
+            if (infoLo->pair) {
+                dvmCompilerClobber(cUnit, infoLo->reg);
+                dvmCompilerClobber(cUnit, infoLo->partner);
+            } else {
+                loc.lowReg = infoLo->reg;
+                loc.location = kLocPhysReg;
+            }
+        }
+    }
+
+    return loc;
+}
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+                                            RegLocation loc)
+{
+    assert(loc.wide);
+    if (loc.location == kLocDalvikFrame) {
+        // Are the dalvik regs already live in physical registers?
+        RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+        RegisterInfo *infoHi = allocLive(cUnit,
+              dvmCompilerSRegHi(loc.sRegLow), kAnyReg);
+        bool match = true;
+        match = match && (infoLo != NULL);
+        match = match && (infoHi != NULL);
+        // Are they both core or both FP?
+        match = match && (FPREG(infoLo->reg) == FPREG(infoHi->reg));
+        // If a pair of floating point singles, are they properly aligned?
+        if (match && FPREG(infoLo->reg)) {
+            match &= ((infoLo->reg & 0x1) == 0);
+            match &= ((infoHi->reg - infoLo->reg) == 1);
+        }
+        // If previously used as a pair, it is the same pair?
+        if (match && (infoLo->pair || infoHi->pair)) {
+            match = (infoLo->pair == infoHi->pair);
+            match &= ((infoLo->reg == infoHi->partner) &&
+                      (infoHi->reg == infoLo->partner));
+        }
+        if (match) {
+            // Can reuse - update the register usage info
+            loc.lowReg = infoLo->reg;
+            loc.highReg = infoHi->reg;
+            loc.location = kLocPhysReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+            return loc;
+        }
+        // Can't easily reuse - clobber any overlaps
+        if (infoLo) {
+            dvmCompilerClobber(cUnit, infoLo->reg);
+            if (infoLo->pair)
+                dvmCompilerClobber(cUnit, infoLo->partner);
+        }
+        if (infoHi) {
+            dvmCompilerClobber(cUnit, infoHi->reg);
+            if (infoHi->pair)
+                dvmCompilerClobber(cUnit, infoHi->partner);
+        }
+    }
+
+    return loc;
+}
+
+static RegLocation evalLocWide(CompilationUnit *cUnit, RegLocation loc,
+                               int regClass, bool update)
+{
+    assert(loc.wide);
+    int newRegs;
+    int lowReg;
+    int highReg;
+
+    loc = dvmCompilerUpdateLocWide(cUnit, loc);
+
+    /* If already in registers, we can assume proper form.  Right reg class? */
+    if (loc.location == kLocPhysReg) {
+        assert(FPREG(loc.lowReg) == FPREG(loc.highReg));
+        assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Reallocate and copy */
+            newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+            lowReg = newRegs & 0xff;
+            highReg = (newRegs >> 8) & 0xff;
+            dvmCompilerRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
+                                   loc.highReg);
+            copyRegInfo(cUnit, lowReg, loc.lowReg);
+            copyRegInfo(cUnit, highReg, loc.highReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.highReg);
+            loc.lowReg = lowReg;
+            loc.highReg = highReg;
+            dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+            assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+    assert((loc.location != kLocRetval) ||
+           (dvmCompilerSRegHi(loc.sRegLow) == INVALID_SREG));
+
+    newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+    loc.lowReg = newRegs & 0xff;
+    loc.highReg = (newRegs >> 8) & 0xff;
+
+    dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+        dvmCompilerMarkLive(cUnit, loc.highReg, dvmCompilerSRegHi(loc.sRegLow));
+    }
+    assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+    return loc;
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+                                      int regClass, bool update)
+{
+    int newReg;
+    if (loc.wide)
+        return evalLocWide(cUnit, loc, regClass, update);
+    loc = dvmCompilerUpdateLoc(cUnit, loc);
+
+    if (loc.location == kLocPhysReg) {
+        if (!regClassMatches(regClass, loc.lowReg)) {
+            /* Wrong register class.  Realloc, copy and transfer ownership */
+            newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+            dvmCompilerRegCopy(cUnit, newReg, loc.lowReg);
+            copyRegInfo(cUnit, newReg, loc.lowReg);
+            dvmCompilerClobber(cUnit, loc.lowReg);
+            loc.lowReg = newReg;
+        }
+        return loc;
+    }
+
+    assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+
+    newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+    loc.lowReg = newReg;
+
+    if (update) {
+        loc.location = kLocPhysReg;
+        dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+    }
+    return loc;
+}
+
+static inline int getDestSSAName(MIR *mir, int num)
+{
+    assert(mir->ssaRep->numDefs > num);
+    return mir->ssaRep->defs[num];
+}
+
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num)
+{
+    RegLocation loc = cUnit->regLocation[
+         SREG(cUnit, dvmCompilerSSASrc(mir, num))];
+    loc.fp = cUnit->regLocation[dvmCompilerSSASrc(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+                                      int num)
+{
+    RegLocation loc = cUnit->regLocation[SREG(cUnit, getDestSSAName(mir, num))];
+    loc.fp = cUnit->regLocation[getDestSSAName(mir, num)].fp;
+    loc.wide = false;
+    return loc;
+}
+
+static RegLocation getLocWide(CompilationUnit *cUnit, MIR *mir,
+                              int low, int high, bool isSrc)
+{
+    RegLocation lowLoc;
+    RegLocation highLoc;
+    /* Copy loc record for low word and patch in data from high word */
+    if (isSrc) {
+        lowLoc = dvmCompilerGetSrc(cUnit, mir, low);
+        highLoc = dvmCompilerGetSrc(cUnit, mir, high);
+    } else {
+        lowLoc = dvmCompilerGetDest(cUnit, mir, low);
+        highLoc = dvmCompilerGetDest(cUnit, mir, high);
+    }
+    /* Avoid this case by either promoting both or neither. */
+    assert(lowLoc.location == highLoc.location);
+    if (lowLoc.location == kLocPhysReg) {
+        /* This case shouldn't happen if we've named correctly */
+        assert(lowLoc.fp == highLoc.fp);
+    }
+    lowLoc.wide = true;
+    lowLoc.highReg = highLoc.lowReg;
+    return lowLoc;
+}
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+                                          int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, false);
+}
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+                                         int low, int high)
+{
+    return getLocWide(cUnit, mir, low, high, true);
+}
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE;
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerClobber(cUnit, r_V1);
+    dvmCompilerMarkInUse(cUnit, r_V0);
+    dvmCompilerMarkInUse(cUnit, r_V1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN;
+    dvmCompilerClobber(cUnit, r_V0);
+    dvmCompilerMarkInUse(cUnit, r_V0);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_WIDE_ALT;
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerClobber(cUnit, r_F1);
+    dvmCompilerMarkInUse(cUnit, r_F0);
+    dvmCompilerMarkInUse(cUnit, r_F1);
+    dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+    return res;
+}
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit)
+{
+    RegLocation res = LOC_C_RETURN_ALT;
+    dvmCompilerClobber(cUnit, r_F0);
+    dvmCompilerMarkInUse(cUnit, r_F0);
+    return res;
+}
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+                                          RegLocation loc)
+{
+    if (loc.location != kLocRetval) {
+        assert(loc.sRegLow != INVALID_SREG);
+        dvmClearBit(cUnit->regPool->nullCheckedRegs, loc.sRegLow);
+        if (loc.wide) {
+            assert(dvmCompilerSRegHi(loc.sRegLow) != INVALID_SREG);
+            dvmClearBit(cUnit->regPool->nullCheckedRegs,
+                        dvmCompilerSRegHi(loc.sRegLow));
+        }
+    }
+}
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+                                              int reg1, int reg2)
+{
+    flushRegWide(cUnit, reg1, reg2);
+}
+
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg)
+{
+    flushReg(cUnit, reg);
+}
diff --git a/vm/compiler/codegen/mips/mips/ArchVariant.cpp b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
new file mode 100644
index 0000000..473b88e
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/ArchVariant.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+extern "C" void dvmCompilerTemplateStart(void);
+
+/*
+ * This file is included by Codegen-mips.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_MIPS;
+}
+
+/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern "C" void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/mips/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    int i = 0;
+
+    /*
+     * Then, populate the templateEntryOffsets array with the offsets from the
+     * the dvmCompilerTemplateStart symbol for each template.
+     */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+    (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/mips/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 9; // 512
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 200;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 &&
+           (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 &&
+           (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0);
+    assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256);
+
+    /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+    assert(sizeof(StackSaveArea) < 236);
+
+    /*
+     * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+     * offset from the struct is less than 128.
+     */
+    assert((offsetof(Thread, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) < 128);
+
+    /* FIXME - comment out the following to enable method-based JIT */
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            ALOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit, int barrierKind)
+{
+#if ANDROID_SMP != 0
+    MipsLIR *sync = newLIR1(cUnit, kMipsSync, barrierKind);
+    sync->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/mips/mips/ArchVariant.h b/vm/compiler/codegen/mips/mips/ArchVariant.h
new file mode 100644
index 0000000..ec04dd8
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
+#define DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+enum TemplateOpcode{
+#include "../../../template/mips/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+};
+#undef JIT_TEMPLATE
+
+#endif  // DALVIK_VM_COMPILER_CODEGEN_MIPS_ARCHVARIANT_H_
diff --git a/vm/compiler/codegen/mips/mips/CallingConvention.S b/vm/compiler/codegen/mips/mips/CallingConvention.S
new file mode 100644
index 0000000..cfe2695
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/CallingConvention.S
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ *    a0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+    .text
+    .align 2
+    .global dvmJitCalleeSave
+    .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+#ifdef __mips_hard_float
+    /* For performance reasons, we are not using any "callee saved" */
+    /* fp registers, thus no need to save them.                     */
+#endif
+    jr $31
+
+    .global dvmJitCalleeRestore
+    .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+#ifdef __mips_hard_float
+    /* For performance reasons, we are not using any "callee saved" */
+    /* fp registers, thus no need to restore them.                  */
+#endif
+    jr $31
diff --git a/vm/compiler/codegen/mips/mips/Codegen.cpp b/vm/compiler/codegen/mips/mips/Codegen.cpp
new file mode 100644
index 0000000..2c7456e
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/Codegen.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+ #define _CODEGEN_C
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/mips/MipsLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/mips/Ralloc.h"
+#include "compiler/codegen/mips/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.cpp"
+
+/* Architectural independent building blocks */
+#include "../Mips32/Factory.cpp"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.cpp"
+
+/* Thumb-specific codegen routines */
+#include "../Mips32/Gen.cpp"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/MipsFP.cpp"
+
+/* Thumb-specific register allocation */
+#include "../Mips32/Ralloc.cpp"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.cpp"
+
+/* Dummy driver for method-based JIT */
+#include "MethodCodegenDriver.cpp"
+
+/* Architecture manifest */
+#include "ArchVariant.cpp"
diff --git a/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp b/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp
new file mode 100644
index 0000000..55c2d89
--- /dev/null
+++ b/vm/compiler/codegen/mips/mips/MethodCodegenDriver.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    ALOGE("Method-based JIT not supported for the Mips target");
+    dvmAbort();
+}
diff --git a/vm/compiler/codegen/x86/AnalysisO1.cpp b/vm/compiler/codegen/x86/AnalysisO1.cpp
new file mode 100644
index 0000000..a9fd5ce
--- /dev/null
+++ b/vm/compiler/codegen/x86/AnalysisO1.cpp
@@ -0,0 +1,5323 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file AnalysisO1.cpp
+  \brief This file implements register allocator, constant folding
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "interp/InterpState.h"
+#include "interp/InterpDefs.h"
+#include "libdex/Leb128.h"
+
+/* compilation flags to turn on debug printout */
+//#define DEBUG_COMPILE_TABLE
+//#define DEBUG_ALLOC_CONSTRAINT
+//#define DEBUG_REGALLOC
+//#define DEBUG_REG_USED
+//#define DEBUG_REFCOUNT
+//#define DEBUG_REACHING_DEF2
+//#define DEBUG_REACHING_DEF
+//#define DEBUG_LIVE_RANGE
+//#define DEBUG_MOVE_OPT
+//#define DEBUG_SPILL
+//#define DEBUG_ENDOFBB
+//#define DEBUG_CONST
+/*
+  #define DEBUG_XFER_POINTS
+  #define DEBUG_DSE
+  #define DEBUG_CFG
+  #define DEBUG_GLOBALTYPE
+  #define DEBUG_STATE
+  #define DEBUG_COMPILE_TABLE
+  #define DEBUG_VIRTUAL_INFO
+  #define DEBUG_MOVE_OPT
+  #define DEBUG_MERGE_ENTRY
+  #define DEBUG_INVALIDATE
+*/
+#include "AnalysisO1.h"
+
+void dumpCompileTable();
+
+/* There are 3 kinds of variables that are handled in this file:
+   1> virtual register (isVirtualReg())
+   2> temporary (!isVirtualReg() && regNum < PhysicalReg_GLUE_DVMDEX)
+   3> glue variables: regNum >= PhysicalReg_GLUE_DVMDEX
+*/
+/** check whether a variable is a virtual register
+ */
+bool isVirtualReg(int type) {
+    if((type & LowOpndRegType_virtual) != 0) return true;
+    return false;
+}
+bool isTemporary(int type, int regNum) {
+    if(!isVirtualReg(type) && regNum < PhysicalReg_GLUE_DVMDEX) return true;
+    return false;
+}
+
+/** convert type defined in lowering module to type defined in register allocator
+    in lowering module <type, isPhysical>
+    in register allocator: LowOpndRegType_hard LowOpndRegType_virtual LowOpndRegType_scratch
+*/
+int convertType(int type, int reg, bool isPhysical) {
+    int newType = type;
+    if(isPhysical) newType |= LowOpndRegType_hard;
+    if(isVirtualReg(type)) newType |= LowOpndRegType_virtual;
+    else {
+        /* reg number for a VR can exceed PhysicalReg_SCRATCH_1 */
+        if(reg >= PhysicalReg_SCRATCH_1 && reg < PhysicalReg_GLUE_DVMDEX)
+            newType |= LowOpndRegType_scratch;
+    }
+    return newType;
+}
+
+/** return the size of a variable
+ */
+OpndSize getRegSize(int type) {
+    if((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) return OpndSize_64;
+    if((type & MASK_FOR_TYPE) == LowOpndRegType_fs) return OpndSize_64;
+    /* for type _gp, _fs_s, _ss */
+    return OpndSize_32;
+}
+
+/*
+   Overlapping cases between two variables A and B
+   layout for A,B   isAPartiallyOverlapB  isBPartiallyOverlapA
+   1> |__|  |____|         OVERLAP_ALIGN        OVERLAP_B_COVER_A
+      |__|  |____|
+   2> |____|           OVERLAP_B_IS_LOW_OF_A    OVERLAP_B_COVER_LOW_OF_A
+        |__|
+   3> |____|           OVERLAP_B_IS_HIGH_OF_A   OVERLAP_B_COVER_HIGH_OF_A
+      |__|
+   4> |____|      OVERLAP_LOW_OF_A_IS_HIGH_OF_B OVERLAP_B_COVER_LOW_OF_A
+         |____|
+   5>    |____|   OVERLAP_HIGH_OF_A_IS_LOW_OF_B OVERLAP_B_COVER_HIGH_OF_A
+      |____|
+   6>   |__|           OVERLAP_A_IS_LOW_OF_B    OVERLAP_B_COVER_A
+      |____|
+   7> |__|             OVERLAP_A_IS_HIGH_OF_B   OVERLAP_B_COVER_A
+      |____|
+*/
+/** determine the overlapping between variable B and A
+*/
+OverlapCase getBPartiallyOverlapA(int regB, LowOpndRegType tB, int regA, LowOpndRegType tA) {
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_B_COVER_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB) return OVERLAP_B_COVER_LOW_OF_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA + 1) return OVERLAP_B_COVER_HIGH_OF_A;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && (regA == regB || regA == regB+1)) return OVERLAP_B_COVER_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1) return OVERLAP_B_COVER_LOW_OF_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1) return OVERLAP_B_COVER_HIGH_OF_A;
+    return OVERLAP_NO;
+}
+
+/** determine overlapping between variable A and B
+*/
+OverlapCase getAPartiallyOverlapB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return OVERLAP_ALIGN;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regA == regB)
+        return OVERLAP_B_IS_LOW_OF_A;
+    if(getRegSize(tA) == OpndSize_64 && getRegSize(tB) == OpndSize_32 && regB == regA+1)
+        return OVERLAP_B_IS_HIGH_OF_A;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regA == regB+1)
+        return OVERLAP_LOW_OF_A_IS_HIGH_OF_B;
+    if(getRegSize(tB) == OpndSize_64 && getRegSize(tA) == OpndSize_64 && regB == regA+1)
+        return OVERLAP_HIGH_OF_A_IS_LOW_OF_B;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB)
+        return OVERLAP_A_IS_LOW_OF_B;
+    if(getRegSize(tA) == OpndSize_32 && getRegSize(tB) == OpndSize_64 && regA == regB+1)
+        return OVERLAP_A_IS_HIGH_OF_B;
+    return OVERLAP_NO;
+}
+
+/** determine whether variable A fully covers B
+ */
+bool isAFullyCoverB(int regA, LowOpndRegType tA, int regB, LowOpndRegType tB) {
+    if(getRegSize(tB) == OpndSize_32) return true;
+    if(getRegSize(tA) == getRegSize(tB) && regA == regB) return true;
+    return false;
+}
+
+/*
+   RegAccessType accessType
+   1> DefOrUse.accessType
+      can only be D(VR), L(low part of VR), H(high part of VR), N(none)
+      for def, it means which part of the VR is live
+      for use, it means which part of the VR comes from the def
+   2> VirtualRegInfo.accessType
+      for currentInfo, it can only be a combination of U & D
+      for entries in infoBasicBlock, it can be a combination of U & D|L|H
+*/
+
+/*
+   Key data structures used:
+   1> BasicBlock_O1
+      VirtualRegInfo infoBasicBlock[]
+      DefUsePair* defUseTable
+      XferPoint xferPoints[]
+   2> MemoryVRInfo memVRTable[]
+      LiveRange* ranges
+   3> compileTableEntry compileTable[]
+   4> VirtualRegInfo
+      DefOrUse reachingDefs[3]
+   5> DefUsePair, LiveRange
+*/
+
+//! one entry for each variable used
+
+//! a variable can be virtual register, or a temporary (can be hard-coded)
+compileTableEntry compileTable[COMPILE_TABLE_SIZE];
+int num_compile_entries;
+//! tables to save the states of register allocation
+regAllocStateEntry1 stateTable1_1[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_2[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_3[COMPILE_TABLE_SIZE];
+regAllocStateEntry1 stateTable1_4[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_1[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_2[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_3[COMPILE_TABLE_SIZE];
+regAllocStateEntry2 stateTable2_4[COMPILE_TABLE_SIZE];
+
+//! array of VirtualRegInfo to store VRs accessed by a single bytecode
+VirtualRegInfo infoByteCode[MAX_REG_PER_BYTECODE];
+int num_regs_per_bytecode;
+//! array of TempRegInfo to store temporaries accessed by a single bytecode
+TempRegInfo infoByteCodeTemp[MAX_TEMP_REG_PER_BYTECODE];
+int num_temp_regs_per_bytecode;
+//! array of MemoryVRInfo to store whether a VR is in memory
+#define NUM_MEM_VR_ENTRY 140
+MemoryVRInfo memVRTable[NUM_MEM_VR_ENTRY];
+int num_memory_vr;
+
+CompilationUnit* currentUnit = NULL;
+
+//! the current basic block
+BasicBlock_O1* currentBB = NULL;
+//! array of RegisterInfo for all the physical registers
+RegisterInfo allRegs[PhysicalReg_GLUE+1]; //initialized in codeGen
+
+VirtualRegInfo currentInfo;
+VirtualRegInfo tmpInfo;
+
+//! this array says whether a spill location is used (0 means not used, 1 means used)
+int spillIndexUsed[MAX_SPILL_JIT_IA];
+int indexForGlue = -1;
+
+int num_bbs_for_method;
+//! array of basic blocks in a method in program order
+BasicBlock_O1* method_bbs_sorted[MAX_NUM_BBS_PER_METHOD];
+//! the entry basic block
+BasicBlock_O1* bb_entry;
+int pc_start = -1;
+int pc_end = -1;
+
+//!array of PCs for exception handlers
+int exceptionHandlers[10];
+int num_exception_handlers;
+
+bool canSpillReg[PhysicalReg_Null]; //physical registers that should not be spilled
+int inGetVR_num = -1;
+int inGetVR_type;
+
+///////////////////////////////////////////////////////////////////////////////
+// FORWARD FUNCTION DECLARATION
+void addExceptionHandler(s4 tmp);
+
+int createCFG(Method* method);
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb);
+void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb);
+void setTypeOfVR();
+void insertGlueReg();
+void dumpVirtualInfoOfMethod();
+int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb);
+
+//used in collectInfoOfBasicBlock: getVirtualRegInfo
+int mergeEntry2(BasicBlock_O1* bb);
+int sortAllocConstraint(RegAllocConstraint* allocConstraints,
+                        RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow);
+
+//used in codeGenBasicBlock
+void insertFromVirtualInfo(BasicBlock_O1* bb, int k); //update compileTable
+void insertFromTempInfo(int k); //update compileTable
+int updateXferPoints();
+void updateLiveTable();
+void printDefUseTable();
+bool isFirstOfHandler(BasicBlock_O1* bb);
+
+//used in mergeEntry2
+//following functions will not update global data structure
+RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA);
+RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB); //will not update global data structure
+RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2);
+RegAccessType updateAccess3(RegAccessType C, RegAccessType B);
+
+void updateDefUseTable();
+void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA);
+void updateReachingDefB1(int indexToA);
+void updateReachingDefB2();
+void updateReachingDefB3();
+
+RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType);
+DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType);
+RegAccessType insertDefUsePair(int reachingDefIndex);
+
+//used in updateXferPoints
+int fakeUsageAtEndOfBB(BasicBlock_O1* bb);
+void insertLoadXfer(int offset, int regNum, LowOpndRegType pType);
+int searchMemTable(int regNum);
+void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd);
+//used in updateLiveTable
+RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive);
+DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType);
+void insertAccess(int tableIndex, LiveRange* startP, int rangeStart);
+
+//register allocation
+int spillLogicalReg(int spill_index, bool updateTable);
+
+/** check whether the current bytecode is IF or GOTO or SWITCH
+ */
+bool isCurrentByteCodeJump() {
+    u2 inst_op = INST_INST(inst);
+    if(inst_op == OP_IF_EQ || inst_op == OP_IF_NE || inst_op == OP_IF_LT ||
+       inst_op == OP_IF_GE || inst_op == OP_IF_GT || inst_op == OP_IF_LE) return true;
+    if(inst_op == OP_IF_EQZ || inst_op == OP_IF_NEZ || inst_op == OP_IF_LTZ ||
+       inst_op == OP_IF_GEZ || inst_op == OP_IF_GTZ || inst_op == OP_IF_LEZ) return true;
+    if(inst_op == OP_GOTO || inst_op == OP_GOTO_16 || inst_op == OP_GOTO_32) return true;
+    if(inst_op == OP_PACKED_SWITCH || inst_op == OP_SPARSE_SWITCH) return true;
+    return false;
+}
+
+/* this function is called before code generation of basic blocks
+   initialize data structure allRegs, which stores information for each physical register,
+   whether it is used, when it was last freed, whether it is callee-saved */
+void initializeAllRegs() {
+    int k;
+    for(k = PhysicalReg_EAX; k <= PhysicalReg_EBP; k++) {
+        allRegs[k].physicalReg = (PhysicalReg) k;
+        if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
+            allRegs[k].isUsed = true;
+        else {
+            allRegs[k].isUsed = false;
+            allRegs[k].freeTimeStamp = -1;
+        }
+        if(k == PhysicalReg_EBX || k == PhysicalReg_EBP || k == PhysicalReg_ESI || k == PhysicalReg_EDI)
+            allRegs[k].isCalleeSaved = true;
+        else
+            allRegs[k].isCalleeSaved = false;
+    }
+    for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
+        allRegs[k].physicalReg = (PhysicalReg) k;
+        allRegs[k].isUsed = false;
+        allRegs[k].freeTimeStamp = -1;
+        allRegs[k].isCalleeSaved = false;
+    }
+}
+
+/** sync up allRegs (isUsed & freeTimeStamp) with compileTable
+    global data: RegisterInfo allRegs[PhysicalReg_Null]
+    update allRegs[EAX to XMM7] except EDI,ESP,EBP
+    update RegisterInfo.isUsed & RegisterInfo.freeTimeStamp
+        if the physical register was used and is not used now
+*/
+void syncAllRegs() {
+    int k, k2;
+    for(k = PhysicalReg_EAX; k <= PhysicalReg_XMM7; k++) {
+        if(k == PhysicalReg_EDI || k == PhysicalReg_ESP || k == PhysicalReg_EBP)
+            continue;
+        //check whether the physical register is used by any logical register
+        bool stillUsed = false;
+        for(k2 = 0; k2 < num_compile_entries; k2++) {
+            if(compileTable[k2].physicalReg == k) {
+                stillUsed = true;
+                break;
+            }
+        }
+        if(stillUsed && !allRegs[k].isUsed) {
+            allRegs[k].isUsed = true;
+        }
+        if(!stillUsed && allRegs[k].isUsed) {
+            allRegs[k].isUsed = false;
+            allRegs[k].freeTimeStamp = lowOpTimeStamp;
+        }
+    }
+    return;
+}
+
+//!sync up spillIndexUsed with compileTable
+
+//!
+void updateSpillIndexUsed() {
+    int k;
+    for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType)) continue;
+        if(compileTable[k].spill_loc_index >= 0) {
+            if(compileTable[k].spill_loc_index > 4*(MAX_SPILL_JIT_IA-1))
+                ALOGE("spill_loc_index is wrong for entry %d: %d",
+                      k, compileTable[k].spill_loc_index);
+            spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
+        }
+    }
+}
+
+/* free memory used in all basic blocks */
+void freeCFG() {
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        /* free defUseTable for method_bbs_sorted[k] */
+        DefUsePair* ptr = method_bbs_sorted[k]->defUseTable;
+        while(ptr != NULL) {
+            DefUsePair* tmp = ptr->next;
+            /* free ptr->uses */
+            DefOrUseLink* ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                DefOrUseLink* tmp2 = ptrUse->next;
+                free(ptrUse);
+                ptrUse = tmp2;
+            }
+            free(ptr);
+            ptr = tmp;
+        }
+        free(method_bbs_sorted[k]);
+    }
+}
+
+/* update compileTable.physicalReg, compileTable.spill_loc_index & allRegs.isUsed
+   for glue-related variables, they do not exist
+       not in a physical register (physicalReg is Null)
+       not in a spilled memory location (spill_loc_index is -1)
+*/
+void initializeRegStateOfBB(BasicBlock_O1* bb) {
+    //for GLUE variables, do not exist
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        /* trace-based JIT: there is no VR with GG type */
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
+            if(bb->bb_index > 0) { //non-entry block
+                if(isFirstOfHandler(bb)) {
+                    /* at the beginning of an exception handler, GG VR is in the interpreted stack */
+                    compileTable[k].physicalReg = PhysicalReg_Null;
+#ifdef DEBUG_COMPILE_TABLE
+                    ALOGI("at the first basic block of an exception handler, GG VR %d type %d is in memory",
+                          compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                } else {
+                    if(compileTable[k].physicalReg == PhysicalReg_Null) {
+                        /* GG VR is in a specific physical register */
+                        compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
+                    }
+                    int tReg = compileTable[k].physicalReg;
+                    allRegs[tReg].isUsed = true;
+#ifdef DEBUG_REG_USED
+                    ALOGI("REGALLOC: physical reg %d is used by a GG VR %d %d at beginning of BB", tReg, compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                }
+            } //non-entry block
+        } //if GG VR
+        if(compileTable[k].regNum != PhysicalReg_GLUE &&
+           compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
+            /* glue related registers */
+            compileTable[k].physicalReg = PhysicalReg_Null;
+            compileTable[k].spill_loc_index = -1;
+        }
+    }
+}
+
+/* update memVRTable[].nullCheckDone */
+void initializeNullCheck(int indexToMemVR) {
+    bool found = false;
+#ifdef GLOBAL_NULLCHECK_OPT
+    /* search nullCheck_inB of the current Basic Block */
+    for(k = 0; k < nullCheck_inSize[currentBB->bb_index2]; k++) {
+        if(nullCheck_inB[currentBB->bb_index2][k] == memVRTable[indexToMemVR].regNum) {
+            found = true;
+            break;
+        }
+    }
+#endif
+    memVRTable[indexToMemVR].nullCheckDone = found;
+}
+
+/* initialize memVRTable */
+void initializeMemVRTable() {
+    num_memory_vr = 0;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(!isVirtualReg(compileTable[k].physicalType)) continue;
+        /* VRs in compileTable */
+        bool setToInMemory = (compileTable[k].physicalReg == PhysicalReg_Null);
+        int regNum = compileTable[k].regNum;
+        OpndSize sizeVR = getRegSize(compileTable[k].physicalType);
+        /* search memVRTable for the VR in compileTable */
+        int kk;
+        int indexL = -1;
+        int indexH = -1;
+        for(kk = 0; kk < num_memory_vr; kk++) {
+            if(memVRTable[kk].regNum == regNum) {
+                indexL = kk;
+                continue;
+            }
+            if(memVRTable[kk].regNum == regNum+1 && sizeVR == OpndSize_64) {
+                indexH = kk;
+                continue;
+            }
+        }
+        if(indexL < 0) {
+            /* the low half of VR is not in memVRTable
+               add an entry for the low half in memVRTable */
+            if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
+                ALOGE("exceeds size of memVRTable");
+                dvmAbort();
+            }
+            memVRTable[num_memory_vr].regNum = regNum;
+            memVRTable[num_memory_vr].inMemory = setToInMemory;
+            initializeNullCheck(num_memory_vr); //set nullCheckDone
+            memVRTable[num_memory_vr].boundCheck.checkDone = false;
+            memVRTable[num_memory_vr].num_ranges = 0;
+            memVRTable[num_memory_vr].ranges = NULL;
+            memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
+            num_memory_vr++;
+        }
+        if(sizeVR == OpndSize_64 && indexH < 0) {
+            /* the high half of VR is not in memVRTable
+               add an entry for the high half in memVRTable */
+            if(num_memory_vr >= NUM_MEM_VR_ENTRY) {
+                ALOGE("exceeds size of memVRTable");
+                dvmAbort();
+            }
+            memVRTable[num_memory_vr].regNum = regNum+1;
+            memVRTable[num_memory_vr].inMemory = setToInMemory;
+            initializeNullCheck(num_memory_vr);
+            memVRTable[num_memory_vr].boundCheck.checkDone = false;
+            memVRTable[num_memory_vr].num_ranges = 0;
+            memVRTable[num_memory_vr].ranges = NULL;
+            memVRTable[num_memory_vr].delayFreeFlags = VRDELAY_NONE;
+            num_memory_vr++;
+        }
+    }
+}
+
+/* create a O1 basic block from basic block constructed in JIT MIR */
+BasicBlock_O1* createBasicBlockO1(BasicBlock* bb) {
+    BasicBlock_O1* bb1 = createBasicBlock(0, -1);
+    bb1->jitBasicBlock = bb;
+    return bb1;
+}
+
+/* a basic block in JIT MIR can contain bytecodes that are not in program order
+   for example, a "goto" bytecode will be followed by the goto target */
+void preprocessingBB(BasicBlock* bb) {
+    currentBB = createBasicBlockO1(bb);
+    /* initialize currentBB->allocConstraints */
+    int ii;
+    for(ii = 0; ii < 8; ii++) {
+        currentBB->allocConstraints[ii].physicalReg = (PhysicalReg)ii;
+        currentBB->allocConstraints[ii].count = 0;
+    }
+    collectInfoOfBasicBlock(currentMethod, currentBB);
+#ifdef DEBUG_COMPILE_TABLE
+    dumpVirtualInfoOfBasicBlock(currentBB);
+#endif
+    currentBB = NULL;
+}
+
+void preprocessingTrace() {
+    int k, k2, k3, jj;
+    /* this is a simplified verson of setTypeOfVR()
+        all VRs are assumed to be GL, no VR will be GG
+    */
+    for(k = 0; k < num_bbs_for_method; k++)
+        for(jj = 0; jj < method_bbs_sorted[k]->num_regs; jj++)
+            method_bbs_sorted[k]->infoBasicBlock[jj].gType = GLOBALTYPE_GL;
+
+    /* insert a glue-related register GLUE_DVMDEX to compileTable */
+    insertGlueReg();
+
+    int compile_entries_old = num_compile_entries;
+    for(k2 = 0; k2 < num_bbs_for_method; k2++) {
+        currentBB = method_bbs_sorted[k2];
+        /* update compileTable with virtual register from currentBB */
+        for(k3 = 0; k3 < currentBB->num_regs; k3++) {
+            insertFromVirtualInfo(currentBB, k3);
+        }
+
+        /* for each GL|GG type VR, insert fake usage at end of basic block to keep it live */
+        int offsetPC_back = offsetPC;
+        offsetPC = PC_FOR_END_OF_BB;
+        for(k = 0; k < num_compile_entries; k++) {
+            currentInfo.regNum = compileTable[k].regNum;
+            currentInfo.physicalType = (LowOpndRegType)compileTable[k].physicalType;
+            if(isVirtualReg(compileTable[k].physicalType) &&
+               compileTable[k].gType == GLOBALTYPE_GL) {
+                /* update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo */
+                fakeUsageAtEndOfBB(currentBB);
+            }
+            if(isVirtualReg(compileTable[k].physicalType) &&
+               compileTable[k].gType == GLOBALTYPE_GG) {
+                fakeUsageAtEndOfBB(currentBB);
+            }
+        }
+        offsetPC = offsetPC_back;
+        num_compile_entries = compile_entries_old;
+    }
+    /* initialize data structure allRegs */
+    initializeAllRegs();
+#ifdef DEBUG_COMPILE_TABLE
+    dumpCompileTable();
+#endif
+    currentBB = NULL;
+}
+
+void printJitTraceInfoAtRunTime(const Method* method, int offset) {
+    ALOGI("execute trace for %s%s at offset %x", method->clazz->descriptor, method->name, offset);
+}
+
+void startOfTraceO1(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, CompilationUnit *cUnit) {
+    num_exception_handlers = 0;
+    num_compile_entries = 0;
+    currentBB = NULL;
+    pc_start = -1;
+    bb_entry = NULL;
+    num_bbs_for_method = 0;
+    currentUnit = cUnit;
+    lowOpTimeStamp = 0;
+
+// dumpDebuggingInfo is gone in CompilationUnit struct
+#if 0
+    /* add code to dump debugging information */
+    if(cUnit->dumpDebuggingInfo) {
+        move_imm_to_mem(OpndSize_32, cUnit->startOffset, -4, PhysicalReg_ESP, true); //2nd argument: offset
+        move_imm_to_mem(OpndSize_32, (int)currentMethod, -8, PhysicalReg_ESP, true); //1st argument: method
+        load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+        typedef void (*vmHelper)(const Method*, int);
+        vmHelper funcPtr = printJitTraceInfoAtRunTime;
+        move_imm_to_reg(OpndSize_32, (int)funcPtr, PhysicalReg_ECX, true);
+        call_reg(PhysicalReg_ECX, true);
+
+        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    }
+#endif
+}
+
+
+/* Code generation for a basic block defined for JIT
+   We have two data structures for a basic block:
+       BasicBlock defined in vm/compiler by JIT
+       BasicBlock_O1 defined in o1 */
+int codeGenBasicBlockJit(const Method* method, BasicBlock* bb) {
+    /* search method_bbs_sorted to find the O1 basic block corresponding to bb */
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        if(method_bbs_sorted[k]->jitBasicBlock == bb) {
+            lowOpTimeStamp = 0; //reset time stamp at start of a basic block
+            currentBB = method_bbs_sorted[k];
+            int cg_ret = codeGenBasicBlock(method, currentBB);
+            currentBB = NULL;
+            return cg_ret;
+        }
+    }
+    ALOGE("can't find the corresponding O1 basic block for id %d type %d",
+         bb->id, bb->blockType);
+    return -1;
+}
+void endOfBasicBlock(BasicBlock* bb) {
+    isScratchPhysical = true;
+    currentBB = NULL;
+}
+void endOfTraceO1() {
+     freeCFG();
+}
+
+/** entry point to collect information about virtual registers used in a basic block
+    Initialize data structure BasicBlock_O1
+    The usage information of virtual registers is stoerd in bb->infoBasicBlock
+
+    Global variables accessed: offsetPC, rPC
+*/
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb) {
+    bb->num_regs = 0;
+    bb->num_defs = 0;
+    bb->defUseTable = NULL;
+    bb->defUseTail = NULL;
+    u2* rPC_start = (u2*)method->insns;
+    int kk;
+    bb->endsWithReturn = false;
+    bb->hasAccessToGlue = false;
+
+    MIR* mir;
+    int seqNum = 0;
+    /* traverse the MIR in basic block
+       sequence number is used to make sure next bytecode will have a larger sequence number */
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = seqNum;
+        mir->seqNum = seqNum++;
+        rPC = rPC_start + mir->offset;
+#ifdef WITH_JIT_INLINING
+        if(mir->dalvikInsn.opcode >= kMirOpFirst &&
+           mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) continue;
+        if(ir->dalvikInsn.opcode == kMirOpCheckInlinePrediction) { //TODO
+        }
+#else
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
+#endif
+        inst = FETCH(0);
+        u2 inst_op = INST_INST(inst);
+        /* update bb->hasAccessToGlue */
+        if((inst_op >= OP_MOVE_RESULT && inst_op <= OP_RETURN_OBJECT) ||
+           (inst_op >= OP_MONITOR_ENTER && inst_op <= OP_INSTANCE_OF) ||
+           (inst_op == OP_FILLED_NEW_ARRAY) ||
+           (inst_op == OP_FILLED_NEW_ARRAY_RANGE) ||
+           (inst_op == OP_THROW) ||
+           (inst_op >= OP_INVOKE_VIRTUAL && inst_op <= OP_INVOKE_INTERFACE_RANGE) ||
+           (inst_op >= OP_THROW_VERIFICATION_ERROR &&
+            inst_op <= OP_EXECUTE_INLINE_RANGE) ||
+           (inst_op >= OP_INVOKE_VIRTUAL_QUICK && inst_op <= OP_INVOKE_SUPER_QUICK_RANGE))
+            bb->hasAccessToGlue = true;
+        /* update bb->endsWithReturn */
+        if(inst_op == OP_RETURN_VOID || inst_op == OP_RETURN || inst_op == OP_RETURN_VOID_BARRIER ||
+           inst_op == OP_RETURN_OBJECT || inst_op == OP_RETURN_WIDE)
+            bb->endsWithReturn = true;
+
+        /* get virtual register usage in current bytecode */
+        getVirtualRegInfo(infoByteCode);
+        int num_regs = num_regs_per_bytecode;
+        for(kk = 0; kk < num_regs; kk++) {
+            currentInfo = infoByteCode[kk];
+#ifdef DEBUG_MERGE_ENTRY
+            ALOGI("call mergeEntry2 at offsetPC %x kk %d VR %d %d", offsetPC, kk,
+                  currentInfo.regNum, currentInfo.physicalType);
+#endif
+            mergeEntry2(bb); //update defUseTable of the basic block
+        }
+
+        //dumpVirtualInfoOfBasicBlock(bb);
+    }//for each bytecode
+
+    bb->pc_end = seqNum;
+
+    //sort allocConstraints of each basic block
+    for(kk = 0; kk < bb->num_regs; kk++) {
+#ifdef DEBUG_ALLOC_CONSTRAINT
+        ALOGI("sort virtual reg %d type %d -------", bb->infoBasicBlock[kk].regNum,
+              bb->infoBasicBlock[kk].physicalType);
+#endif
+        sortAllocConstraint(bb->infoBasicBlock[kk].allocConstraints,
+                            bb->infoBasicBlock[kk].allocConstraintsSorted, true);
+    }
+#ifdef DEBUG_ALLOC_CONSTRAINT
+    ALOGI("sort constraints for BB %d --------", bb->bb_index);
+#endif
+    sortAllocConstraint(bb->allocConstraints, bb->allocConstraintsSorted, false);
+    return 0;
+}
+
+/** entry point to generate native code for a O1 basic block
+    There are 3 kinds of virtual registers in a O1 basic block:
+    1> L VR: local within the basic block
+    2> GG VR: is live in other basic blocks,
+              its content is in a pre-defined GPR at the beginning of a basic block
+    3> GL VR: is live in other basic blocks,
+              its content is in the interpreted stack at the beginning of a basic block
+    compileTable is updated with infoBasicBlock at the start of the basic block;
+    Before lowering each bytecode, compileTable is updated with infoByteCodeTemp;
+    At end of the basic block, right before the jump instruction, handles constant VRs and GG VRs
+*/
+int codeGenBasicBlock(const Method* method, BasicBlock_O1* bb) {
+    /* we assume at the beginning of each basic block,
+       all GL VRs reside in memory and all GG VRs reside in predefined physical registers,
+       so at the end of a basic block, recover a spilled GG VR, store a GL VR to memory */
+    /* update compileTable with entries in bb->infoBasicBlock */
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        insertFromVirtualInfo(bb, k);
+    }
+    updateXferPoints(); //call fakeUsageAtEndOfBB
+#ifdef DEBUG_REACHING_DEF
+    printDefUseTable();
+#endif
+#ifdef DSE_OPT
+    removeDeadDefs();
+    printDefUseTable();
+#endif
+    //clear const section of compileTable
+    for(k = 0; k < num_compile_entries; k++) compileTable[k].isConst = false;
+    num_const_vr = 0;
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+    dumpCompileTable();
+#endif
+    initializeRegStateOfBB(bb);
+    initializeMemVRTable();
+    updateLiveTable();
+    freeReg(true);  //before code gen of a basic block, also called at end of a basic block?
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At start of basic block %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+#endif
+
+    u2* rPC_start = (u2*)method->insns;
+    bool lastByteCodeIsJump = false;
+    MIR* mir;
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = mir->seqNum;
+        rPC = rPC_start + mir->offset;
+#ifdef WITH_JIT_INLINING
+        if(mir->dalvikInsn.opcode >= kMirOpFirst &&
+           mir->dalvikInsn.opcode != kMirOpCheckInlinePrediction) {
+#else
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) {
+#endif
+            handleExtendedMIR(currentUnit, mir);
+            continue;
+        }
+
+        inst = FETCH(0);
+        //before handling a bytecode, import info of temporary registers to compileTable including refCount
+        num_temp_regs_per_bytecode = getTempRegInfo(infoByteCodeTemp);
+        for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+            if(infoByteCodeTemp[k].versionNum > 0) continue;
+            insertFromTempInfo(k);
+        }
+        startNativeCode(-1, -1);
+        for(k = 0; k <= MAX_SPILL_JIT_IA-1; k++) spillIndexUsed[k] = 0;
+        //update spillIndexUsed if a glue variable was spilled
+        for(k = 0; k < num_compile_entries; k++) {
+            if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX) {
+                if(compileTable[k].spill_loc_index >= 0)
+                    spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 1;
+            }
+        }
+#ifdef DEBUG_COMPILE_TABLE
+        ALOGI("compile table size after importing temporary info %d", num_compile_entries);
+        ALOGI("before one bytecode %d (num of VRs %d) -------", bb->bb_index, bb->num_regs);
+#endif
+        //set isConst to true for CONST & MOVE MOVE_OBJ?
+        //clear isConst to true for MOVE, MOVE_OBJ, MOVE_RESULT, MOVE_EXCEPTION ...
+        bool isConst = getConstInfo(bb); //will reset isConst if a VR is updated by the bytecode
+        bool isDeadStmt = false;
+#ifdef DSE_OPT
+        for(k = 0; k < num_dead_pc; k++) {
+            if(deadPCs[k] == offsetPC) {
+                isDeadStmt = true;
+                break;
+            }
+        }
+#endif
+        getVirtualRegInfo(infoByteCode);
+        //call something similar to mergeEntry2, but only update refCount
+        //clear refCount
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                            infoByteCode[k].regNum);
+            if(indexT >= 0)
+                compileTable[indexT].refCount = 0;
+        }
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                            infoByteCode[k].regNum);
+            if(indexT >= 0)
+                compileTable[indexT].refCount += infoByteCode[k].refCount;
+        } //for k
+#ifdef DSE_OPT
+        if(isDeadStmt) { //search compileTable
+            getVirtualRegInfo(infoByteCode);
+#ifdef DEBUG_DSE
+            ALOGI("DSE: stmt at offsetPC %d is dead", offsetPC);
+#endif
+            for(k = 0; k < num_regs_per_bytecode; k++) {
+                int indexT = searchCompileTable(LowOpndRegType_virtual | infoByteCode[k].physicalType,
+                                                infoByteCode[k].regNum);
+                if(indexT >= 0)
+                    compileTable[indexT].refCount -= infoByteCode[k].refCount;
+            }
+        }
+#endif
+        lastByteCodeIsJump = false;
+        if(!isConst && !isDeadStmt)  //isDeadStmt is false when DSE_OPT is not enabled
+        {
+#ifdef DEBUG_COMPILE_TABLE
+            dumpCompileTable();
+#endif
+            globalShortMap = NULL;
+            if(isCurrentByteCodeJump()) lastByteCodeIsJump = true;
+            //lowerByteCode will call globalVREndOfBB if it is jump
+            int retCode = lowerByteCodeJit(method, rPC, mir);
+            if(gDvmJit.codeCacheByteUsed + (stream - streamStart) +
+                 CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                 ALOGE("JIT code cache full");
+                 gDvmJit.codeCacheFull = true;
+                 return -1;
+            }
+
+            if (retCode == 1) {
+                // We always fall back to the interpreter for OP_INVOKE_OBJECT_INIT_RANGE,
+                // but any other failure is unexpected and should be logged.
+                if (mir->dalvikInsn.opcode != OP_INVOKE_OBJECT_INIT_RANGE) {
+                    ALOGE("JIT couldn't compile %s%s dex_pc=%d opcode=%d",
+                          method->clazz->descriptor,
+                          method->name,
+                          offsetPC,
+                          mir->dalvikInsn.opcode);
+                }
+                return -1;
+            }
+            updateConstInfo(bb);
+            freeShortMap();
+            if(retCode < 0) {
+                ALOGE("error in lowering the bytecode");
+                return retCode;
+            }
+            freeReg(true); //may dump GL VR to memory (this is necessary)
+            //after each bytecode, make sure non-VRs have refCount of zero
+            for(k = 0; k < num_compile_entries; k++) {
+                if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum)) {
+#ifdef PRINT_WARNING
+                    if(compileTable[k].refCount > 0) {
+                        ALOGW("refCount for a temporary reg %d %d is %d after a bytecode", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+                    }
+#endif
+                    compileTable[k].refCount = 0;
+                }
+            }
+        } else { //isConst || isDeadStmt
+            //if this bytecode is the target of a jump, the mapFromBCtoNCG should be updated
+            offsetNCG = stream - streamMethodStart;
+            mapFromBCtoNCG[offsetPC] = offsetNCG;
+#ifdef DEBUG_COMPILE_TABLE
+            ALOGI("this bytecode generates a constant and has no side effect");
+#endif
+            freeReg(true); //may dump GL VR to memory (this is necessary)
+        }
+#ifdef DEBUG_COMPILE_TABLE
+        ALOGI("after one bytecode BB %d (num of VRs %d)", bb->bb_index, bb->num_regs);
+#endif
+    }//for each bytecode
+#ifdef DEBUG_COMPILE_TABLE
+    dumpCompileTable();
+#endif
+    if(!lastByteCodeIsJump) constVREndOfBB();
+    //at end of a basic block, get spilled GG VR & dump GL VR
+    if(!lastByteCodeIsJump) globalVREndOfBB(method);
+    //remove entries for temporary registers, L VR and GL VR
+    int jj;
+    for(k = 0; k < num_compile_entries; ) {
+        bool removeEntry = false;
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType != GLOBALTYPE_GG) {
+            removeEntry = true;
+        }
+        if(isTemporary(compileTable[k].physicalType, compileTable[k].regNum))
+            removeEntry = true;
+        if(removeEntry) {
+#ifdef PRINT_WARNING
+            if(compileTable[k].refCount > 0)
+                ALOGW("refCount for REG %d %d is %d at end of a basic block", compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+#endif
+            compileTable[k].refCount = 0;
+            for(jj = k+1; jj < num_compile_entries; jj++) {
+                compileTable[jj-1] = compileTable[jj];
+            }
+            num_compile_entries--;
+        } else {
+            k++;
+        }
+    }
+    freeReg(true);
+    //free LIVE TABLE
+    for(k = 0; k < num_memory_vr; k++) {
+        LiveRange* ptr2 = memVRTable[k].ranges;
+        while(ptr2 != NULL) {
+            LiveRange* tmpP = ptr2->next;
+            free(ptr2->accessPC);
+            free(ptr2);
+            ptr2 = tmpP;
+        }
+    }
+#ifdef DEBUG_COMPILE_TABLE
+    ALOGI("At end of basic block -------");
+    dumpCompileTable();
+#endif
+    return 0;
+}
+
+/** update infoBasicBlock & defUseTable
+    input: currentInfo
+    side effect: update currentInfo.reachingDefs
+
+    update entries in infoBasicBlock by calling updateReachingDefA
+    if there is no entry in infoBasicBlock for B, an entry will be created and inserted to infoBasicBlock
+
+    defUseTable is updated to account for the access at currentInfo
+    if accessType of B is U or UD, we call updateReachingDefB to update currentInfo.reachingDefs
+        in order to correctly insert the usage to defUseTable
+*/
+int mergeEntry2(BasicBlock_O1* bb) {
+    LowOpndRegType typeB = currentInfo.physicalType;
+    int regB = currentInfo.regNum;
+    int jj, k;
+    int jjend = bb->num_regs;
+    bool isMerged = false;
+    bool hasAlias = false;
+    OverlapCase isBPartiallyOverlapA, isAPartiallyOverlapB;
+    RegAccessType tmpType = REGACCESS_N;
+    currentInfo.num_reaching_defs = 0;
+
+    /* traverse variable A in infoBasicBlock */
+    for(jj = 0; jj < jjend; jj++) {
+        int regA = bb->infoBasicBlock[jj].regNum;
+        LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
+        isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
+        isAPartiallyOverlapB = getAPartiallyOverlapB(regA, typeA, regB, typeB);
+        if(regA == regB && typeA == typeB) {
+            /* variable A and B are aligned */
+            bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
+                                                             OVERLAP_B_COVER_A);
+            bb->infoBasicBlock[jj].refCount += currentInfo.refCount;
+            /* copy reaching defs of variable B from variable A */
+            currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
+            for(k = 0; k < currentInfo.num_reaching_defs; k++)
+                currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
+            updateDefUseTable(); //use currentInfo to update defUseTable
+            updateReachingDefA(jj, OVERLAP_B_COVER_A); //update reachingDefs of A
+            isMerged = true;
+            hasAlias = true;
+            if(typeB == LowOpndRegType_gp) {
+                //merge allocConstraints
+                for(k = 0; k < 8; k++) {
+                    bb->infoBasicBlock[jj].allocConstraints[k].count += currentInfo.allocConstraints[k].count;
+                }
+            }
+        }
+        else if(isBPartiallyOverlapA != OVERLAP_NO) {
+            tmpType = updateAccess2(tmpType, updateAccess1(bb->infoBasicBlock[jj].accessType, isAPartiallyOverlapB));
+            bb->infoBasicBlock[jj].accessType = mergeAccess2(bb->infoBasicBlock[jj].accessType, currentInfo.accessType,
+                                                             isBPartiallyOverlapA);
+#ifdef DEBUG_MERGE_ENTRY
+            ALOGI("update accessType in case 2: VR %d %d accessType %d", regA, typeA, bb->infoBasicBlock[jj].accessType);
+#endif
+            hasAlias = true;
+            if(currentInfo.accessType == REGACCESS_U || currentInfo.accessType == REGACCESS_UD) {
+                /* update currentInfo.reachingDefs */
+                updateReachingDefB1(jj);
+                updateReachingDefB2();
+            }
+            updateReachingDefA(jj, isBPartiallyOverlapA);
+        }
+        else {
+            //even if B does not overlap with A, B can affect the reaching defs of A
+            //for example, B is a def of "v0", A is "v1"
+            //  B can kill some reaching defs of A or affect the accessType of a reaching def
+            updateReachingDefA(jj, OVERLAP_NO); //update reachingDefs of A
+        }
+    }//for each variable A in infoBasicBlock
+    if(!isMerged) {
+        /* create a new entry in infoBasicBlock */
+        bb->infoBasicBlock[bb->num_regs].refCount = currentInfo.refCount;
+        bb->infoBasicBlock[bb->num_regs].physicalType = typeB;
+        if(hasAlias)
+            bb->infoBasicBlock[bb->num_regs].accessType = updateAccess3(tmpType, currentInfo.accessType);
+        else
+            bb->infoBasicBlock[bb->num_regs].accessType = currentInfo.accessType;
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("update accessType in case 3: VR %d %d accessType %d", regB, typeB, bb->infoBasicBlock[bb->num_regs].accessType);
+#endif
+        bb->infoBasicBlock[bb->num_regs].regNum = regB;
+        for(k = 0; k < 8; k++)
+            bb->infoBasicBlock[bb->num_regs].allocConstraints[k] = currentInfo.allocConstraints[k];
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("isMerged is false, call updateDefUseTable");
+#endif
+        updateDefUseTable(); //use currentInfo to update defUseTable
+        updateReachingDefB3(); //update currentInfo.reachingDefs if currentInfo defines variable B
+
+        //copy from currentInfo.reachingDefs to bb->infoBasicBlock[bb->num_regs]
+        bb->infoBasicBlock[bb->num_regs].num_reaching_defs = currentInfo.num_reaching_defs;
+        for(k = 0; k < currentInfo.num_reaching_defs; k++)
+            bb->infoBasicBlock[bb->num_regs].reachingDefs[k] = currentInfo.reachingDefs[k];
+#ifdef DEBUG_MERGE_ENTRY
+        ALOGI("try to update reaching defs for VR %d %d", regB, typeB);
+        for(k = 0; k < bb->infoBasicBlock[bb->num_regs].num_reaching_defs; k++)
+            ALOGI("reaching def %d @ %d for VR %d %d access %d", k, currentInfo.reachingDefs[k].offsetPC,
+                  currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
+                  currentInfo.reachingDefs[k].accessType);
+#endif
+        bb->num_regs++;
+        if(bb->num_regs >= MAX_REG_PER_BASICBLOCK) {
+            ALOGE("too many VRs in a basic block");
+            dvmAbort();
+        }
+        return -1;
+    }
+    return 0;
+}
+
+//!update reaching defs for infoBasicBlock[indexToA]
+
+//!use currentInfo.reachingDefs to update reaching defs for variable A
+void updateReachingDefA(int indexToA, OverlapCase isBPartiallyOverlapA) {
+    if(indexToA < 0) return;
+    int k, k2;
+    OverlapCase isBPartiallyOverlapDef;
+    if(currentInfo.accessType == REGACCESS_U) {
+        return; //no update to reachingDefs of the VR
+    }
+    /* access in currentInfo is DU, D, or UD */
+    if(isBPartiallyOverlapA == OVERLAP_B_COVER_A) {
+        /* from this point on, the reachingDefs for variable A is a single def to currentInfo at offsetPC */
+        currentBB->infoBasicBlock[indexToA].num_reaching_defs = 1;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].offsetPC = offsetPC;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].regNum = currentInfo.regNum;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].physicalType = currentInfo.physicalType;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[0].accessType = REGACCESS_D;
+#ifdef DEBUG_REACHING_DEF
+        ALOGI("single reaching def @ %d for VR %d %d", offsetPC, currentInfo.regNum, currentInfo.physicalType);
+#endif
+        return;
+    }
+    /* update reachingDefs for variable A to get rid of dead defs */
+    /* Bug fix: it is possible that more than one reaching defs need to be removed
+                after one reaching def is removed, num_reaching_defs--, but k should not change
+    */
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; ) {
+        /* remove one reaching def in one interation of the loop */
+        //check overlapping between def & B
+        isBPartiallyOverlapDef = getBPartiallyOverlapA(currentInfo.regNum, currentInfo.physicalType,
+                                                       currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+                                                       currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType);
+#ifdef DEBUG_REACHING_DEF
+        ALOGI("DEBUG B %d %d def %d %d %d", currentInfo.regNum, currentInfo.physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
+#endif
+        /* cases where one def nees to be removed:
+           if B fully covers def, def is removed
+           if B overlaps high half of def & def's accessType is H, def is removed
+           if B overlaps low half of def & def's accessType is L, def is removed
+        */
+        if((isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_H) ||
+           (isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType == REGACCESS_L) ||
+           isBPartiallyOverlapDef == OVERLAP_B_COVER_A
+           ) { //remove def
+            //shift from k+1 to end
+            for(k2 = k+1; k2 < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k2++)
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k2-1] = currentBB->infoBasicBlock[indexToA].reachingDefs[k2];
+            currentBB->infoBasicBlock[indexToA].num_reaching_defs--;
+        }
+        /*
+           if B overlaps high half of def & def's accessType is not H --> update accessType of def
+        */
+        else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_HIGH_OF_A &&
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_H) {
+            //low half is still valid
+            if(getRegSize(currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType) == OpndSize_32)
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
+            else
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_L;
+#ifdef DEBUG_REACHING_DEF
+            ALOGI("DEBUG: set accessType of def to L");
+#endif
+            k++;
+        }
+        /*
+           if B overlaps low half of def & def's accessType is not L --> update accessType of def
+        */
+        else if(isBPartiallyOverlapDef == OVERLAP_B_COVER_LOW_OF_A &&
+                currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType != REGACCESS_L) {
+            //high half of def is still valid
+            currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_H;
+#ifdef DEBUG_REACHING_DEF
+            ALOGI("DEBUG: set accessType of def to H");
+#endif
+            k++;
+        }
+        else {
+            k++;
+        }
+    }//for k
+    if(isBPartiallyOverlapA != OVERLAP_NO) {
+        //insert the def to variable @ currentInfo
+        k = currentBB->infoBasicBlock[indexToA].num_reaching_defs;
+        if(k >= 3) {
+          ALOGE("more than 3 reaching defs");
+        }
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC = offsetPC;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum = currentInfo.regNum;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType = currentInfo.physicalType;
+        currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType = REGACCESS_D;
+        currentBB->infoBasicBlock[indexToA].num_reaching_defs++;
+    }
+#ifdef DEBUG_REACHING_DEF2
+    ALOGI("IN updateReachingDefA for VR %d %d", currentBB->infoBasicBlock[indexToA].regNum,
+          currentBB->infoBasicBlock[indexToA].physicalType);
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++)
+        ALOGI("reaching def %d @ %d for VR %d %d access %d", k,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].offsetPC,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+              currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType);
+#endif
+}
+
+/** Given a variable B @currentInfo,
+    updates its reaching defs by checking reaching defs of variable A @currentBB->infoBasicBlock[indexToA]
+    The result is stored in tmpInfo.reachingDefs
+*/
+void updateReachingDefB1(int indexToA) {
+    if(indexToA < 0) return;
+    int k;
+    tmpInfo.num_reaching_defs = 0;
+    for(k = 0; k < currentBB->infoBasicBlock[indexToA].num_reaching_defs; k++) {
+        /* go through reachingDefs of variable A @currentBB->infoBasicBlock[indexToA]
+           for each def, check whether it overlaps with variable B @currentInfo
+               if the def overlaps with variable B, insert it to tmpInfo.reachingDefs
+        */
+        OverlapCase isDefPartiallyOverlapB = getAPartiallyOverlapB(
+                                                 currentBB->infoBasicBlock[indexToA].reachingDefs[k].regNum,
+                                                 currentBB->infoBasicBlock[indexToA].reachingDefs[k].physicalType,
+                                                 currentInfo.regNum, currentInfo.physicalType
+                                                 );
+        bool insert1 = false; //whether to insert the def to tmpInfo.reachingDefs
+        if(isDefPartiallyOverlapB == OVERLAP_ALIGN ||
+           isDefPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B ||
+           isDefPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B) {
+            /* B aligns with def */
+            /* def is low half of B, def is high half of B
+               in these two cases, def is 32 bits */
+            insert1 = true;
+        }
+        RegAccessType deftype = currentBB->infoBasicBlock[indexToA].reachingDefs[k].accessType;
+        if(isDefPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A ||
+           isDefPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B) {
+            /* B is the low half of def */
+            /* the low half of def is the high half of B */
+            if(deftype != REGACCESS_H) insert1 = true;
+        }
+        if(isDefPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A ||
+           isDefPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B) {
+            /* B is the high half of def */
+            /* the high half of def is the low half of B */
+            if(deftype != REGACCESS_L) insert1 = true;
+        }
+        if(insert1) {
+            if(tmpInfo.num_reaching_defs >= 3) {
+                ALOGE("more than 3 reaching defs for tmpInfo");
+            }
+            tmpInfo.reachingDefs[tmpInfo.num_reaching_defs] = currentBB->infoBasicBlock[indexToA].reachingDefs[k];
+            tmpInfo.num_reaching_defs++;
+#ifdef DEBUG_REACHING_DEF2
+            ALOGI("insert from entry %d %d: index %d", currentBB->infoBasicBlock[indexToA].regNum,
+                  currentBB->infoBasicBlock[indexToA].physicalType, k);
+#endif
+        }
+    }
+}
+
+/** update currentInfo.reachingDefs by merging currentInfo.reachingDefs with tmpInfo.reachingDefs
+*/
+void updateReachingDefB2() {
+    int k, k2;
+    for(k2 = 0; k2 < tmpInfo.num_reaching_defs; k2++ ) {
+        bool merged = false;
+        for(k = 0; k < currentInfo.num_reaching_defs; k++) {
+            /* check whether it is the same def, if yes, do nothing */
+            if(currentInfo.reachingDefs[k].regNum == tmpInfo.reachingDefs[k2].regNum &&
+               currentInfo.reachingDefs[k].physicalType == tmpInfo.reachingDefs[k2].physicalType) {
+                merged = true;
+                if(currentInfo.reachingDefs[k].offsetPC != tmpInfo.reachingDefs[k2].offsetPC) {
+                    ALOGE("defs on the same VR %d %d with different offsetPC %d vs %d",
+                          currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType,
+                          currentInfo.reachingDefs[k].offsetPC, tmpInfo.reachingDefs[k2].offsetPC);
+                }
+                if(currentInfo.reachingDefs[k].accessType != tmpInfo.reachingDefs[k2].accessType)
+                    ALOGE("defs on the same VR %d %d with different accessType",
+                          currentInfo.reachingDefs[k].regNum, currentInfo.reachingDefs[k].physicalType);
+                break;
+            }
+        }
+        if(!merged) {
+            if(currentInfo.num_reaching_defs >= 3) {
+               ALOGE("more than 3 reaching defs for currentInfo");
+            }
+            currentInfo.reachingDefs[currentInfo.num_reaching_defs] = tmpInfo.reachingDefs[k2];
+            currentInfo.num_reaching_defs++;
+        }
+    }
+}
+
+//!update currentInfo.reachingDefs with currentInfo if variable is defined in currentInfo
+
+//!
+void updateReachingDefB3() {
+    if(currentInfo.accessType == REGACCESS_U) {
+        return; //no need to update currentInfo.reachingDefs
+    }
+    currentInfo.num_reaching_defs = 1;
+    currentInfo.reachingDefs[0].regNum = currentInfo.regNum;
+    currentInfo.reachingDefs[0].physicalType = currentInfo.physicalType;
+    currentInfo.reachingDefs[0].offsetPC = offsetPC;
+    currentInfo.reachingDefs[0].accessType = REGACCESS_D;
+}
+
+/** update defUseTable by checking currentInfo
+*/
+void updateDefUseTable() {
+    /* no access */
+    if(currentInfo.accessType == REGACCESS_N) return;
+    /* define then use, or define only */
+    if(currentInfo.accessType == REGACCESS_DU || currentInfo.accessType == REGACCESS_D) {
+        /* insert a definition at offsetPC to variable @ currentInfo */
+        DefUsePair* ptr = insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
+        if(currentInfo.accessType != REGACCESS_D) {
+             /* if access is define then use, insert a use at offsetPC */
+            insertAUse(ptr, offsetPC, currentInfo.regNum, currentInfo.physicalType);
+        }
+        return;
+    }
+    /* use only or use then define
+       check the reaching defs for the usage */
+    int k;
+    bool isLCovered = false, isHCovered = false, isDCovered = false;
+    for(k = 0; k < currentInfo.num_reaching_defs; k++) {
+        /* insert a def currentInfo.reachingDefs[k] and a use of variable at offsetPC */
+        RegAccessType useType = insertDefUsePair(k);
+        if(useType == REGACCESS_D) isDCovered = true;
+        if(useType == REGACCESS_L) isLCovered = true;
+        if(useType == REGACCESS_H) isHCovered = true;
+    }
+    OpndSize useSize = getRegSize(currentInfo.physicalType);
+    if((!isDCovered) && (!isLCovered)) {
+        /* the low half of variable is not defined in the basic block
+           so insert a def to the low half at START of the basic block */
+        insertDefUsePair(-1);
+    }
+    if(useSize == OpndSize_64 && (!isDCovered) && (!isHCovered)) {
+        /* the high half of variable is not defined in the basic block
+           so insert a def to the high half at START of the basic block */
+        insertDefUsePair(-2);
+    }
+    if(currentInfo.accessType == REGACCESS_UD) {
+        /* insert a def at offsetPC to variable @ currentInfo */
+        insertADef(offsetPC, currentInfo.regNum, currentInfo.physicalType, REGACCESS_D);
+        return;
+    }
+}
+
+//! insert a use at offsetPC of given variable at end of DefUsePair
+
+//!
+RegAccessType insertAUse(DefUsePair* ptr, int offsetPC, int regNum, LowOpndRegType physicalType) {
+    DefOrUseLink* tLink = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
+    if(tLink == NULL) {
+        ALOGE("Memory allocation failed");
+        return REGACCESS_UNKNOWN;
+    }
+    tLink->offsetPC = offsetPC;
+    tLink->regNum = regNum;
+    tLink->physicalType = physicalType;
+    tLink->next = NULL;
+    if(ptr->useTail != NULL)
+        ptr->useTail->next = tLink;
+    ptr->useTail = tLink;
+    if(ptr->uses == NULL)
+        ptr->uses = tLink;
+    ptr->num_uses++;
+
+    //check whether the def is partially overlapping with the variable
+    OverlapCase isDefPartiallyOverlapB = getBPartiallyOverlapA(ptr->def.regNum,
+                                                       ptr->def.physicalType,
+                                                       regNum, physicalType);
+    RegAccessType useType = setAccessTypeOfUse(isDefPartiallyOverlapB, ptr->def.accessType);
+    tLink->accessType = useType;
+    return useType;
+}
+
+//! insert a def to currentBB->defUseTable
+
+//! update currentBB->defUseTail if necessary
+DefUsePair* insertADef(int offsetPC, int regNum, LowOpndRegType pType, RegAccessType rType) {
+    DefUsePair* ptr = (DefUsePair*)malloc(sizeof(DefUsePair));
+    if(ptr == NULL) {
+        ALOGE("Memory allocation failed");
+        return NULL;
+    }
+    ptr->next = NULL;
+    ptr->def.offsetPC = offsetPC;
+    ptr->def.regNum = regNum;
+    ptr->def.physicalType = pType;
+    ptr->def.accessType = rType;
+    ptr->num_uses = 0;
+    ptr->useTail = NULL;
+    ptr->uses = NULL;
+    if(currentBB->defUseTail != NULL) {
+        currentBB->defUseTail->next = ptr;
+    }
+    currentBB->defUseTail = ptr;
+    if(currentBB->defUseTable == NULL)
+        currentBB->defUseTable = ptr;
+    currentBB->num_defs++;
+#ifdef DEBUG_REACHING_DEF
+    ALOGI("insert a def at %d to defUseTable for VR %d %d", offsetPC,
+          regNum, pType);
+#endif
+    return ptr;
+}
+
+/** insert a def to defUseTable, then insert a use of variable @ currentInfo
+    if reachingDefIndex >= 0, the def is currentInfo.reachingDefs[index]
+    if reachingDefIndex is -1, the low half is defined at START of the basic block
+    if reachingDefIndex is -2, the high half is defined at START of the basic block
+*/
+RegAccessType insertDefUsePair(int reachingDefIndex) {
+    int k = reachingDefIndex;
+    DefUsePair* tableIndex = NULL;
+    DefOrUse theDef;
+    theDef.regNum = 0;
+    if(k < 0) {
+        /* def at start of the basic blcok */
+        theDef.offsetPC = PC_FOR_START_OF_BB;
+        theDef.accessType = REGACCESS_D;
+        if(k == -1) //low half of variable
+            theDef.regNum = currentInfo.regNum;
+        if(k == -2) //high half of variable
+            theDef.regNum = currentInfo.regNum+1;
+        theDef.physicalType = LowOpndRegType_gp;
+    }
+    else {
+        theDef = currentInfo.reachingDefs[k];
+    }
+    tableIndex = searchDefUseTable(theDef.offsetPC, theDef.regNum, theDef.physicalType);
+    if(tableIndex == NULL) //insert an entry
+        tableIndex = insertADef(theDef.offsetPC, theDef.regNum, theDef.physicalType, theDef.accessType);
+    else
+        tableIndex->def.accessType = theDef.accessType;
+    RegAccessType useType = insertAUse(tableIndex, offsetPC, currentInfo.regNum, currentInfo.physicalType);
+    return useType;
+}
+
+//! insert a XFER_MEM_TO_XMM to currentBB->xferPoints
+
+//!
+void insertLoadXfer(int offset, int regNum, LowOpndRegType pType) {
+    //check whether it is already in currentBB->xferPoints
+    int k;
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
+           currentBB->xferPoints[k].offsetPC == offset &&
+           currentBB->xferPoints[k].regNum == regNum &&
+           currentBB->xferPoints[k].physicalType == pType)
+            return;
+    }
+    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_MEM_TO_XMM;
+    currentBB->xferPoints[currentBB->num_xfer_points].regNum = regNum;
+    currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = offset;
+    currentBB->xferPoints[currentBB->num_xfer_points].physicalType = pType;
+#ifdef DEBUG_XFER_POINTS
+    ALOGI("insert to xferPoints %d: XFER_MEM_TO_XMM of VR %d %d at %d", currentBB->num_xfer_points, regNum, pType, offset);
+#endif
+    currentBB->num_xfer_points++;
+    if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+        ALOGE("too many xfer points");
+        dvmAbort();
+    }
+}
+
+/** update defUseTable by assuming a fake usage at END of a basic block for variable @ currentInfo
+    create a fake usage at end of a basic block for variable B (currentInfo.physicalType, currentInfo.regNum)
+    get reaching def info for variable B and store the info in currentInfo.reachingDefs
+        for each virtual register (variable A) accessed in the basic block
+            update reaching defs of B by checking reaching defs of variable A
+    update defUseTable
+*/
+int fakeUsageAtEndOfBB(BasicBlock_O1* bb) {
+    currentInfo.accessType = REGACCESS_U;
+    LowOpndRegType typeB = currentInfo.physicalType;
+    int regB = currentInfo.regNum;
+    int jj, k;
+    currentInfo.num_reaching_defs = 0;
+    for(jj = 0; jj < bb->num_regs; jj++) {
+        int regA = bb->infoBasicBlock[jj].regNum;
+        LowOpndRegType typeA = bb->infoBasicBlock[jj].physicalType;
+        OverlapCase isBPartiallyOverlapA = getBPartiallyOverlapA(regB, typeB, regA, typeA);
+        if(regA == regB && typeA == typeB) {
+            /* copy reachingDefs from variable A */
+            currentInfo.num_reaching_defs = bb->infoBasicBlock[jj].num_reaching_defs;
+            for(k = 0; k < currentInfo.num_reaching_defs; k++)
+                currentInfo.reachingDefs[k] = bb->infoBasicBlock[jj].reachingDefs[k];
+            break;
+        }
+        else if(isBPartiallyOverlapA != OVERLAP_NO) {
+            /* B overlaps with A */
+            /* update reaching defs of variable B by checking reaching defs of bb->infoBasicBlock[jj] */
+            updateReachingDefB1(jj);
+            updateReachingDefB2(); //merge currentInfo with tmpInfo
+        }
+    }
+    /* update defUseTable by checking currentInfo */
+    updateDefUseTable();
+    return 0;
+}
+
+/** update xferPoints of currentBB
+    Traverse currentBB->defUseTable
+*/
+int updateXferPoints() {
+    int k = 0;
+    currentBB->num_xfer_points = 0;
+    DefUsePair* ptr = currentBB->defUseTable;
+    DefOrUseLink* ptrUse = NULL;
+    /* traverse the def use chain of the basic block */
+    while(ptr != NULL) {
+        LowOpndRegType defType = ptr->def.physicalType;
+        //if definition is for a variable of 32 bits
+        if(getRegSize(defType) == OpndSize_32) {
+            /* check usages of the definition, whether it reaches a GPR, a XMM, a FS, or a SS */
+            bool hasGpUsage = false;
+            bool hasGpUsage2 = false; //not a fake usage
+            bool hasXmmUsage = false;
+            bool hasFSUsage = false;
+            bool hasSSUsage = false;
+            ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                if(ptrUse->physicalType == LowOpndRegType_gp) {
+                    hasGpUsage = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsage2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_ss) hasSSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_fs ||
+                   ptrUse->physicalType == LowOpndRegType_fs_s)
+                    hasFSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_xmm) {
+                    hasXmmUsage = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_xmm ||
+                   ptrUse->physicalType == LowOpndRegType_ss) {
+                    /* if a 32-bit definition reaches a xmm usage or a SS usage,
+                       insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                ptrUse = ptrUse->next;
+            }
+            if(((hasXmmUsage || hasFSUsage || hasSSUsage) && defType == LowOpndRegType_gp) ||
+               (hasGpUsage && defType == LowOpndRegType_fs) ||
+               (defType == LowOpndRegType_ss && (hasGpUsage || hasXmmUsage || hasFSUsage))) {
+                /* insert a transfer if def is on a GPR, usage is on a XMM, FS or SS
+                                     if def is on a FS, usage is on a GPR
+                                     if def is on a SS, usage is on a GPR, XMM or FS
+                   transfer type is XFER_DEF_TO_GP_MEM if a real GPR usage exisits
+                   transfer type is XFER_DEF_TO_GP otherwise*/
+                currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
+                currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
+                currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
+                if(hasGpUsage2) { //create an entry XFER_DEF_TO_GP_MEM
+                    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_GP_MEM;
+                }
+                else { //create an entry XFER_DEF_TO_MEM
+                    currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_TO_MEM;
+                }
+                currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
+#ifdef DEBUG_XFER_POINTS
+                ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
+#endif
+                currentBB->num_xfer_points++;
+                if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+                    ALOGE("too many xfer points");
+                    dvmAbort();
+                }
+            }
+        }
+        else { /* def is on 64 bits */
+            bool hasGpUsageOfL = false; //exist a GPR usage of the low half
+            bool hasGpUsageOfH = false; //exist a GPR usage of the high half
+            bool hasGpUsageOfL2 = false;
+            bool hasGpUsageOfH2 = false;
+            bool hasMisaligned = false;
+            bool hasAligned = false;
+            bool hasFSUsage = false;
+            bool hasSSUsage = false;
+            ptrUse = ptr->uses;
+            while(ptrUse != NULL) {
+                if(ptrUse->physicalType == LowOpndRegType_gp &&
+                   ptrUse->regNum == ptr->def.regNum) {
+                    hasGpUsageOfL = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsageOfL2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_gp &&
+                   ptrUse->regNum == ptr->def.regNum + 1) {
+                    hasGpUsageOfH = true;
+                    if(ptrUse->offsetPC != PC_FOR_END_OF_BB)
+                        hasGpUsageOfH2 = true;
+                }
+                if(ptrUse->physicalType == LowOpndRegType_xmm &&
+                   ptrUse->regNum == ptr->def.regNum) {
+                    hasAligned = true;
+                    /* if def is on FS and use is on XMM, insert a XFER_MEM_TO_XMM */
+                    if(defType == LowOpndRegType_fs)
+                        insertLoadXfer(ptrUse->offsetPC,
+                                       ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                if(ptrUse->physicalType == LowOpndRegType_fs ||
+                   ptrUse->physicalType == LowOpndRegType_fs_s)
+                    hasFSUsage = true;
+                if(ptrUse->physicalType == LowOpndRegType_xmm &&
+                   ptrUse->regNum != ptr->def.regNum) {
+                    hasMisaligned = true;
+                    /* if use is on XMM and use and def are misaligned, insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_xmm);
+                }
+                if(ptrUse->physicalType == LowOpndRegType_ss) {
+                    hasSSUsage = true;
+                    /* if use is on SS, insert a XFER_MEM_TO_XMM */
+                    insertLoadXfer(ptrUse->offsetPC,
+                                   ptrUse->regNum, LowOpndRegType_ss);
+                }
+                ptrUse = ptrUse->next;
+            }
+            if(defType == LowOpndRegType_fs && !hasGpUsageOfL && !hasGpUsageOfH) {
+                ptr = ptr->next;
+                continue;
+            }
+            if(defType == LowOpndRegType_xmm && !hasFSUsage &&
+               !hasGpUsageOfL && !hasGpUsageOfH && !hasMisaligned && !hasSSUsage) {
+                ptr = ptr->next;
+                continue;
+            }
+            /* insert a XFER_DEF_IS_XMM */
+            currentBB->xferPoints[currentBB->num_xfer_points].regNum = ptr->def.regNum;
+            currentBB->xferPoints[currentBB->num_xfer_points].offsetPC = ptr->def.offsetPC;
+            currentBB->xferPoints[currentBB->num_xfer_points].physicalType = ptr->def.physicalType;
+            currentBB->xferPoints[currentBB->num_xfer_points].xtype = XFER_DEF_IS_XMM;
+            currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = -1;
+            currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = -1;
+            if(hasGpUsageOfL2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gpl = ptr->def.regNum;
+            if(hasGpUsageOfH2) currentBB->xferPoints[currentBB->num_xfer_points].vr_gph = ptr->def.regNum+1;
+            currentBB->xferPoints[currentBB->num_xfer_points].dumpToMem = true;
+            currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = false; //not used in updateVirtualReg
+            if(hasAligned) currentBB->xferPoints[currentBB->num_xfer_points].dumpToXmm = true;
+            currentBB->xferPoints[currentBB->num_xfer_points].tableIndex = k;
+#ifdef DEBUG_XFER_POINTS
+            ALOGI("insert XFER %d at def %d: V%d %d", currentBB->num_xfer_points, ptr->def.offsetPC, ptr->def.regNum, defType);
+#endif
+            currentBB->num_xfer_points++;
+            if(currentBB->num_xfer_points >= MAX_XFER_PER_BB) {
+                ALOGE("too many xfer points");
+                dvmAbort();
+            }
+        }
+        ptr = ptr->next;
+    } //while ptr
+#ifdef DEBUG_XFER_POINTS
+    ALOGI("XFER points for current basic block ------");
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        ALOGI("  at offset %x, VR %d %d: type %d, vr_gpl %d, vr_gph %d, dumpToMem %d, dumpToXmm %d",
+              currentBB->xferPoints[k].offsetPC, currentBB->xferPoints[k].regNum,
+              currentBB->xferPoints[k].physicalType, currentBB->xferPoints[k].xtype,
+              currentBB->xferPoints[k].vr_gpl, currentBB->xferPoints[k].vr_gph,
+              currentBB->xferPoints[k].dumpToMem, currentBB->xferPoints[k].dumpToXmm);
+    }
+#endif
+    return -1;
+}
+
+//! update memVRTable[].ranges by browsing the defUseTable
+
+//! each virtual register has a list of live ranges, and each live range has a list of PCs that access the VR
+void updateLiveTable() {
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        bool updateUse = false;
+        if(ptr->num_uses == 0) {
+            ptr->num_uses = 1;
+            ptr->uses = (DefOrUseLink*)malloc(sizeof(DefOrUseLink));
+            if(ptr->uses == NULL) {
+                ALOGE("Memory allocation failed");
+                return;
+            }
+            ptr->uses->accessType = REGACCESS_D;
+            ptr->uses->regNum = ptr->def.regNum;
+            ptr->uses->offsetPC = ptr->def.offsetPC;
+            ptr->uses->physicalType = ptr->def.physicalType;
+            ptr->uses->next = NULL;
+            ptr->useTail = ptr->uses;
+            updateUse = true;
+        }
+        DefOrUseLink* ptrUse = ptr->uses;
+        while(ptrUse != NULL) {
+            RegAccessType useType = ptrUse->accessType;
+            if(useType == REGACCESS_L || useType == REGACCESS_D) {
+                int indexL = searchMemTable(ptrUse->regNum);
+                if(indexL >= 0)
+                    mergeLiveRange(indexL, ptr->def.offsetPC,
+                                   ptrUse->offsetPC); //tableIndex, start PC, end PC
+            }
+            if(getRegSize(ptrUse->physicalType) == OpndSize_64 &&
+               (useType == REGACCESS_H || useType == REGACCESS_D)) {
+                int indexH = searchMemTable(ptrUse->regNum+1);
+                if(indexH >= 0)
+                    mergeLiveRange(indexH, ptr->def.offsetPC,
+                                   ptrUse->offsetPC);
+            }
+            ptrUse = ptrUse->next;
+        }//while ptrUse
+        if(updateUse) {
+            ptr->num_uses = 0;
+            free(ptr->uses);
+            ptr->uses = NULL;
+            ptr->useTail = NULL;
+        }
+        ptr = ptr->next;
+    }//while ptr
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVE TABLE");
+    for(int k = 0; k < num_memory_vr; k++) {
+        ALOGI("VR %d live ", memVRTable[k].regNum);
+        LiveRange* ptr = memVRTable[k].ranges;
+        while(ptr != NULL) {
+            ALOGI("[%x %x] (", ptr->start, ptr->end);
+            for(int k3 = 0; k3 < ptr->num_access; k3++)
+                ALOGI("%x ", ptr->accessPC[k3]);
+            ALOGI(") ");
+            ptr = ptr->next;
+        }
+        ALOGI("");
+    }
+#endif
+}
+
+//!add a live range [rangeStart, rangeEnd] to ranges of memVRTable, merge to existing live ranges if necessary
+
+//!ranges are in increasing order of startPC
+void mergeLiveRange(int tableIndex, int rangeStart, int rangeEnd) {
+    if(rangeStart == PC_FOR_START_OF_BB) rangeStart = currentBB->pc_start;
+    if(rangeEnd == PC_FOR_END_OF_BB) rangeEnd = currentBB->pc_end;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE call mergeLiveRange on tableIndex %d with [%x %x]", tableIndex, rangeStart, rangeEnd);
+#endif
+    int startIndex = -1, endIndex = -1;
+    bool startBeforeRange = false, endBeforeRange = false; //before the index or in the range
+    bool startDone = false, endDone = false;
+    LiveRange* ptr = memVRTable[tableIndex].ranges;
+    LiveRange* ptrStart = NULL;
+    LiveRange* ptrStart_prev = NULL;
+    LiveRange* ptrEnd = NULL;
+    LiveRange* ptrEnd_prev = NULL;
+    int k = 0;
+    while(ptr != NULL) {
+        if(!startDone) {
+            if(ptr->start <= rangeStart &&
+               ptr->end >= rangeStart) {
+                startIndex = k;
+                ptrStart = ptr;
+                startBeforeRange = false;
+                startDone = true;
+            }
+            else if(ptr->start > rangeStart) {
+                startIndex = k;
+                ptrStart = ptr;
+                startBeforeRange = true;
+                startDone = true;
+            }
+        }
+        if(!startDone) ptrStart_prev = ptr;
+        if(!endDone) {
+            if(ptr->start <= rangeEnd &&
+               ptr->end >= rangeEnd) {
+                endIndex = k;
+                ptrEnd = ptr;
+                endBeforeRange = false;
+                endDone = true;
+            }
+            else if(ptr->start > rangeEnd) {
+                endIndex = k;
+                ptrEnd = ptr;
+                endBeforeRange = true;
+                endDone = true;
+            }
+        }
+        if(!endDone) ptrEnd_prev = ptr;
+        ptr = ptr->next;
+        k++;
+    } //while
+    if(!startDone) { //both can be NULL
+        startIndex = memVRTable[tableIndex].num_ranges;
+        ptrStart = NULL; //ptrStart_prev should be the last live range
+        startBeforeRange = true;
+    }
+    //if endDone, ptrEnd is not NULL, ptrEnd_prev can be NULL
+    if(!endDone) { //both can be NULL
+        endIndex = memVRTable[tableIndex].num_ranges;
+        ptrEnd = NULL;
+        endBeforeRange = true;
+    }
+    if(startIndex == endIndex && startBeforeRange && endBeforeRange) { //insert at startIndex
+        //3 cases depending on BeforeRange when startIndex == endIndex
+        //insert only if both true
+        //merge otherwise
+        /////////// insert before ptrStart
+        LiveRange* currRange = (LiveRange *)malloc(sizeof(LiveRange));
+        if(ptrStart_prev == NULL) {
+            currRange->next = memVRTable[tableIndex].ranges;
+            memVRTable[tableIndex].ranges = currRange;
+        } else {
+            currRange->next = ptrStart_prev->next;
+            ptrStart_prev->next = currRange;
+        }
+        currRange->start = rangeStart;
+        currRange->end = rangeEnd;
+        currRange->accessPC = (int *)malloc(sizeof(int) * NUM_ACCESS_IN_LIVERANGE);
+        currRange->num_alloc = NUM_ACCESS_IN_LIVERANGE;
+        if(rangeStart != rangeEnd) {
+            currRange->num_access = 2;
+            currRange->accessPC[0] = rangeStart;
+            currRange->accessPC[1] = rangeEnd;
+        } else {
+            currRange->num_access = 1;
+            currRange->accessPC[0] = rangeStart;
+        }
+        memVRTable[tableIndex].num_ranges++;
+#ifdef DEBUG_LIVE_RANGE
+        ALOGI("LIVERANGE insert one live range [%x %x] to tableIndex %d", rangeStart, rangeEnd, tableIndex);
+#endif
+        return;
+    }
+    if(!endBeforeRange) { //here ptrEnd is not NULL
+        endIndex++; //next
+        ptrEnd_prev = ptrEnd; //ptrEnd_prev is not NULL
+        ptrEnd = ptrEnd->next; //ptrEnd can be NULL
+    }
+    if(endIndex < startIndex+1) ALOGE("mergeLiveRange endIndex %d startIndex %d", endIndex, startIndex);
+    ///////// use ptrStart & ptrEnd_prev
+    if(ptrStart == NULL || ptrEnd_prev == NULL) {
+        ALOGE("mergeLiveRange ptr is NULL");
+        return;
+    }
+    //endIndex > startIndex (merge the ranges between startIndex and endIndex-1)
+    //update ptrStart
+    if(ptrStart->start > rangeStart)
+        ptrStart->start = rangeStart; //min of old start & rangeStart
+    ptrStart->end = ptrEnd_prev->end; //max of old end & rangeEnd
+    if(rangeEnd > ptrStart->end)
+        ptrStart->end = rangeEnd;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE merge entries for tableIndex %d from %d to %d", tableIndex, startIndex+1, endIndex-1);
+#endif
+    if(ptrStart->num_access <= 0) ALOGE("mergeLiveRange number of access");
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE tableIndex %d startIndex %d num_access %d (", tableIndex, startIndex, ptrStart->num_access);
+    for(k = 0; k < ptrStart->num_access; k++)
+        ALOGI("%x ", ptrStart->accessPC[k]);
+    ALOGI(")");
+#endif
+    ///// go through pointers from ptrStart->next to ptrEnd
+    //from startIndex+1 to endIndex-1
+    ptr = ptrStart->next;
+    while(ptr != NULL && ptr != ptrEnd) {
+        int k2;
+        for(k2 = 0; k2 < ptr->num_access; k2++) { //merge to startIndex
+            insertAccess(tableIndex, ptrStart, ptr->accessPC[k2]);
+        }//k2
+        ptr = ptr->next;
+    }
+    insertAccess(tableIndex, ptrStart, rangeStart);
+    insertAccess(tableIndex, ptrStart, rangeEnd);
+    //remove startIndex+1 to endIndex-1
+    if(startIndex+1 < endIndex) {
+        ptr = ptrStart->next;
+        while(ptr != NULL && ptr != ptrEnd) {
+            LiveRange* tmpP = ptr->next;
+            free(ptr->accessPC);
+            free(ptr);
+            ptr = tmpP;
+        }
+        ptrStart->next = ptrEnd;
+    }
+    memVRTable[tableIndex].num_ranges -= (endIndex - startIndex - 1);
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("num_ranges for VR %d: %d", memVRTable[tableIndex].regNum, memVRTable[tableIndex].num_ranges);
+#endif
+}
+//! insert an access to a given live range, in order
+
+//!
+void insertAccess(int tableIndex, LiveRange* startP, int rangeStart) {
+    int k3, k4;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE insertAccess %d %x", tableIndex, rangeStart);
+#endif
+    int insertIndex = -1;
+    for(k3 = 0; k3 < startP->num_access; k3++) {
+        if(startP->accessPC[k3] == rangeStart) {
+            return;
+        }
+        if(startP->accessPC[k3] > rangeStart) {
+            insertIndex = k3;
+            break;
+        }
+    }
+
+    //insert here
+    k3 = insertIndex;
+    if(insertIndex == -1) {
+        k3 = startP->num_access;
+    }
+    if(startP->num_access == startP->num_alloc) {
+        int currentAlloc = startP->num_alloc;
+        startP->num_alloc += NUM_ACCESS_IN_LIVERANGE;
+        int* tmpPtr = (int *)malloc(sizeof(int) * startP->num_alloc);
+        for(k4 = 0; k4 < currentAlloc; k4++)
+            tmpPtr[k4] = startP->accessPC[k4];
+        free(startP->accessPC);
+        startP->accessPC = tmpPtr;
+    }
+    //insert accessPC
+    for(k4 = startP->num_access-1; k4 >= k3; k4--)
+        startP->accessPC[k4+1] = startP->accessPC[k4];
+    startP->accessPC[k3] = rangeStart;
+#ifdef DEBUG_LIVE_RANGE
+    ALOGI("LIVERANGE insert %x to tableIndex %d", rangeStart, tableIndex);
+#endif
+    startP->num_access++;
+    return;
+}
+
+/////////////////////////////////////////////////////////////////////
+bool isInMemory(int regNum, OpndSize size);
+void setVRToMemory(int regNum, OpndSize size);
+bool isVRLive(int vA);
+int getSpillIndex(bool isGLUE, OpndSize size);
+void clearVRToMemory(int regNum, OpndSize size);
+void clearVRNullCheck(int regNum, OpndSize size);
+
+inline int getSpillLocDisp(int offset) {
+#ifdef SPILL_IN_THREAD
+    return offset+offsetof(Thread, spillRegion);;
+#else
+    return offset+offEBP_spill;
+#endif
+}
+#if 0
+/* used if we keep self pointer in a physical register */
+inline int getSpillLocReg(int offset) {
+    return PhysicalReg_Glue;
+}
+#endif
+#ifdef SPILL_IN_THREAD
+inline void loadFromSpillRegion_with_self(OpndSize size, int reg_self, bool selfPhysical, int reg, int offset) {
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), reg_self, selfPhysical,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), reg_self, true,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void saveToSpillRegion_with_self(OpndSize size, int selfReg, bool selfPhysical, int reg, int offset) {
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), selfReg, selfPhysical,
+                            MemoryAccess_SPILL, offset);
+}
+inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    int reg_self = registerAlloc(LowOpndRegType_scratch, C_SCRATCH_1, isScratchPhysical, false);
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), reg_self, true,
+                            MemoryAccess_SPILL, offset);
+}
+#else
+inline void loadFromSpillRegion(OpndSize size, int reg, int offset) {
+    /* only 1 instruction is generated by move_mem_to_reg_noalloc */
+    move_mem_to_reg_noalloc(size,
+                            getSpillLocDisp(offset), PhysicalReg_EBP, true,
+                            MemoryAccess_SPILL, offset,
+                            reg, true);
+}
+inline void saveToSpillRegion(OpndSize size, int reg, int offset) {
+    move_reg_to_mem_noalloc(size,
+                            reg, true,
+                            getSpillLocDisp(offset), PhysicalReg_EBP, true,
+                            MemoryAccess_SPILL, offset);
+}
+#endif
+
+//! dump an immediate to memory, set inMemory to true
+
+//!
+void dumpImmToMem(int vrNum, OpndSize size, int value) {
+    if(isInMemory(vrNum, size)) {
+#ifdef DEBUG_SPILL
+        ALOGI("Skip dumpImmToMem vA %d size %d", vrNum, size);
+#endif
+        return;
+    }
+    set_VR_to_imm_noalloc(vrNum, size, value);
+    setVRToMemory(vrNum, size);
+}
+//! dump content of a VR to memory, set inMemory to true
+
+//!
+void dumpToMem(int vrNum, LowOpndRegType type, int regAll) { //ss,gp,xmm
+    if(isInMemory(vrNum, getRegSize(type))) {
+#ifdef DEBUG_SPILL
+        ALOGI("Skip dumpToMem vA %d type %d", vrNum, type);
+#endif
+        return;
+    }
+    if(type == LowOpndRegType_gp || type == LowOpndRegType_xmm)
+        set_virtual_reg_noalloc(vrNum, getRegSize(type), regAll, true);
+    if(type == LowOpndRegType_ss)
+        move_ss_reg_to_mem_noalloc(regAll, true,
+                                   4*vrNum, PhysicalReg_FP, true,
+                                   MemoryAccess_VR, vrNum);
+    setVRToMemory(vrNum, getRegSize(type));
+}
+//! dump part of a 64-bit VR to memory and update inMemory
+
+//! isLow tells whether low half or high half is dumped
+void dumpPartToMem(int reg /*xmm physical reg*/, int vA, bool isLow) {
+    if(isLow) {
+        if(isInMemory(vA, OpndSize_32)) {
+#ifdef DEBUG_SPILL
+            ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
+#endif
+            return;
+        }
+    }
+    else {
+        if(isInMemory(vA+1, OpndSize_32)) {
+#ifdef DEBUG_SPILL
+            ALOGI("Skip dumpPartToMem isLow %d vA %d", isLow, vA);
+#endif
+            return;
+        }
+    }
+    if(isLow) {
+        if(!isVRLive(vA)) return;
+    }
+    else {
+        if(!isVRLive(vA+1)) return;
+    }
+    //move part to vA or vA+1
+    if(isLow) {
+        move_ss_reg_to_mem_noalloc(reg, true,
+                                   4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    } else {
+        int k = getSpillIndex(false, OpndSize_64);
+        //H, L in 4*k+4 & 4*k
+#ifdef SPILL_IN_THREAD
+        get_self_pointer(PhysicalReg_SCRATCH_1, isScratchPhysical);
+        saveToSpillRegion_with_self(OpndSize_64, PhysicalReg_SCRATCH_1, isScratchPhysical, reg, 4*k);
+        //update low 32 bits of xmm reg from 4*k+4
+        move_ss_mem_to_reg(NULL,
+                                   getSpillLocDisp(4*k+4), PhysicalReg_SCRATCH_1, isScratchPhysical,
+                                   reg, true);
+#else
+        saveToSpillRegion(OpndSize_64, reg, 4*k);
+        //update low 32 bits of xmm reg from 4*k+4
+        move_ss_mem_to_reg_noalloc(
+                                   getSpillLocDisp(4*k+4), PhysicalReg_EBP, true,
+                                   MemoryAccess_SPILL, 4*k+4,
+                                   reg, true);
+#endif
+        //move low 32 bits of xmm reg to vA+1
+        move_ss_reg_to_mem_noalloc(reg, true, 4*(vA+1), PhysicalReg_FP, true, MemoryAccess_VR, vA+1);
+    }
+    if(isLow)
+        setVRToMemory(vA, OpndSize_32);
+    else
+        setVRToMemory(vA+1, OpndSize_32);
+}
+void clearVRBoundCheck(int regNum, OpndSize size);
+//! the content of a VR is no longer in memory or in physical register if the latest content of a VR is constant
+
+//! clear nullCheckDone; if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
+void invalidateVRDueToConst(int reg, OpndSize size) {
+    clearVRToMemory(reg, size); //memory content is out-dated
+    clearVRNullCheck(reg, size);
+    clearVRBoundCheck(reg, size);
+    //check reg,gp reg,ss reg,xmm reg-1,xmm
+    //if size is 64: check reg+1,gp|ss reg+1,xmm
+    int index;
+    //if VR is xmm, check whether we need to dump part of VR to memory
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
+#endif
+        if(size == OpndSize_32)
+            dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
+#endif
+        dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
+#endif
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
+#endif
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    if(size == OpndSize_64) {
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
+#endif
+            dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+}
+//! check which physical registers hold out-dated content if there is a def
+
+//! if another VR is overlapped with the given VR, the content of that VR is no longer in physical register
+//! should we update inMemory?
+void invalidateVR(int reg, LowOpndRegType pType) {
+    //def at fs: content of xmm & gp & ss are out-dated (reg-1,xmm reg,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
+    //def at xmm: content of misaligned xmm & gp are out-dated (reg-1,xmm reg+1,xmm) (reg,gp|ss reg+1,gp|ss)
+    //def at fs_s: content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp|ss)
+    //def at gp:   content of xmm is out-dated (reg-1,xmm reg,xmm) (reg,ss)
+    //def at ss:   content of xmm & gp are out-dated (reg-1,xmm reg,xmm) (reg,gp)
+    int index;
+    if(pType != LowOpndRegType_xmm) { //check xmm @reg
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_xmm);
+#endif
+            if(getRegSize(pType) == OpndSize_32)
+                dumpPartToMem(compileTable[index].physicalReg, reg, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    //check misaligned xmm @ reg-1
+    index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg-1);
+    if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+        ALOGI("INVALIDATE virtual reg %d type %d", reg-1, LowOpndRegType_xmm);
+#endif
+        dumpPartToMem(compileTable[index].physicalReg, reg-1, true); //dump low of xmm to memory
+        compileTable[index].physicalReg = PhysicalReg_Null;
+    }
+    //check misaligned xmm @ reg+1
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,xmm
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_xmm, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_xmm);
+#endif
+            dumpPartToMem(compileTable[index].physicalReg, reg+1, false); //dump high of xmm to memory
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType != LowOpndRegType_gp) {
+        //check reg,gp
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,gp
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_gp);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType != LowOpndRegType_ss) {
+        //check reg,ss
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+    if(pType == LowOpndRegType_xmm || pType == LowOpndRegType_fs) {
+        //check reg+1,ss
+        index = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_ss, reg+1);
+        if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_INVALIDATE
+            ALOGI("INVALIDATE virtual reg %d type %d", reg+1, LowOpndRegType_ss);
+#endif
+            compileTable[index].physicalReg = PhysicalReg_Null;
+        }
+    }
+}
+//! bookkeeping when a VR is updated
+
+//! invalidate contents of some physical registers, clear nullCheckDone, and update inMemory;
+//! check whether there exist tranfer points for this bytecode, if yes, perform the transfer
+int updateVirtualReg(int reg, LowOpndRegType pType) {
+    int k;
+    OpndSize size = getRegSize(pType);
+    //WAS only invalidate xmm VRs for the following cases:
+    //if def reaches a use of vA,xmm and (the def is not xmm or is misaligned xmm)
+    //  invalidate "vA,xmm"
+    invalidateVR(reg, pType);
+    clearVRNullCheck(reg, size);
+    clearVRBoundCheck(reg, size);
+    if(pType == LowOpndRegType_fs || pType == LowOpndRegType_fs_s)
+        setVRToMemory(reg, size);
+    else {
+        clearVRToMemory(reg, size);
+    }
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].offsetPC == offsetPC &&
+           currentBB->xferPoints[k].regNum == reg &&
+           currentBB->xferPoints[k].physicalType == pType &&
+           currentBB->xferPoints[k].xtype != XFER_MEM_TO_XMM) {
+            //perform the corresponding action for the def
+            PhysicalReg regAll;
+            if(currentBB->xferPoints[k].xtype == XFER_DEF_IS_XMM) {
+                //def at fs: content of xmm is out-dated
+                //def at xmm: content of misaligned xmm is out-dated
+                //invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
+#ifdef DEBUG_XFER_POINTS
+                if(currentBB->xferPoints[k].dumpToXmm) ALOGI("XFER set_virtual_reg to xmm: xmm VR %d", reg);
+#endif
+                if(pType == LowOpndRegType_xmm)  {
+#ifdef DEBUG_XFER_POINTS
+                    ALOGI("XFER set_virtual_reg to memory: xmm VR %d", reg);
+#endif
+                    PhysicalReg regAll = (PhysicalReg)checkVirtualReg(reg, LowOpndRegType_xmm, 0 /* do not update*/);
+                    dumpToMem(reg, LowOpndRegType_xmm, regAll);
+                }
+                if(currentBB->xferPoints[k].vr_gpl >= 0) { //
+                }
+                if(currentBB->xferPoints[k].vr_gph >= 0) {
+                }
+            }
+            if((pType == LowOpndRegType_gp || pType == LowOpndRegType_ss) &&
+               (currentBB->xferPoints[k].xtype == XFER_DEF_TO_MEM ||
+                currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM)) {
+                //the defined gp VR already in register
+                //invalidateXmmVR(currentBB->xferPoints[k].tableIndex);
+                regAll = (PhysicalReg)checkVirtualReg(reg, pType, 0 /* do not update*/);
+                dumpToMem(reg, pType, regAll);
+#ifdef DEBUG_XFER_POINTS
+                ALOGI("XFER set_virtual_reg to memory: gp VR %d", reg);
+#endif
+            }
+            if((pType == LowOpndRegType_fs_s || pType == LowOpndRegType_ss) &&
+               currentBB->xferPoints[k].xtype == XFER_DEF_TO_GP_MEM) {
+            }
+        }
+    }
+    return -1;
+}
+////////////////////////////////////////////////////////////////
+//REGISTER ALLOCATION
+int spillForHardReg(int regNum, int type);
+void decreaseRefCount(int index);
+int getFreeReg(int type, int reg, int indexToCompileTable);
+PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable);
+int unspillLogicalReg(int spill_index, int physicalReg);
+int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb);
+bool isTemp8Bit(int type, int reg);
+bool matchType(int typeA, int typeB);
+int getNextAccess(int compileIndex);
+void dumpCompileTable();
+
+//! allocate a register for a variable
+
+//!if no physical register is free, call spillForLogicalReg to free up a physical register;
+//!if the variable is a temporary and it was spilled, call unspillLogicalReg to load from spill location to the allocated physical register;
+//!if updateRefCount is true, reduce reference count of the variable by 1
+int registerAlloc(int type, int reg, bool isPhysical, bool updateRefCount) {
+#ifdef DEBUG_REGALLOC
+    ALOGI("%p: try to allocate register %d type %d isPhysical %d", currentBB, reg, type, isPhysical);
+#endif
+    if(currentBB == NULL) {
+        if(type & LowOpndRegType_virtual) return PhysicalReg_Null;
+        if(isPhysical) return reg; //for helper functions
+        return PhysicalReg_Null;
+    }
+    //ignore EDI, ESP, EBP (glue)
+    if(isPhysical && (reg == PhysicalReg_EDI || reg == PhysicalReg_ESP ||
+                      reg == PhysicalReg_EBP || reg == PhysicalReg_Null))
+        return reg;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int tIndex = searchCompileTable(newType, reg);
+    if(tIndex < 0) {
+      ALOGE("reg %d type %d not found in registerAlloc", reg, newType);
+      return PhysicalReg_Null;
+    }
+
+    //physical register
+    if(isPhysical) {
+        if(allRegs[reg].isUsed) { //if used by a non hard-coded register
+            spillForHardReg(reg, newType);
+        }
+        allRegs[reg].isUsed = true;
+#ifdef DEBUG_REG_USED
+        ALOGI("REGALLOC: allocate a reg %d", reg);
+#endif
+        compileTable[tIndex].physicalReg = reg;
+        if(updateRefCount)
+            decreaseRefCount(tIndex);
+#ifdef DEBUG_REGALLOC
+        ALOGI("REGALLOC: allocate register %d for logical register %d %d",
+               compileTable[tIndex].physicalReg, reg, newType);
+#endif
+        return reg;
+    }
+    //already allocated
+    if(compileTable[tIndex].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_REGALLOC
+        ALOGI("already allocated to physical register %d", compileTable[tIndex].physicalReg);
+#endif
+        if(updateRefCount)
+            decreaseRefCount(tIndex);
+        return compileTable[tIndex].physicalReg;
+    }
+
+    //at this point, the logical register is not hard-coded and is mapped to Reg_Null
+    //first check whether there is a free reg
+    //if not, call spillForLogicalReg
+    int index = getFreeReg(newType, reg, tIndex);
+    if(index >= 0 && index < PhysicalReg_Null) {
+        //update compileTable & allRegs
+        compileTable[tIndex].physicalReg = allRegs[index].physicalReg;
+        allRegs[index].isUsed = true;
+#ifdef DEBUG_REG_USED
+        ALOGI("REGALLOC: register %d is free", allRegs[index].physicalReg);
+#endif
+    } else {
+        PhysicalReg allocR = spillForLogicalReg(newType, reg, tIndex);
+        compileTable[tIndex].physicalReg = allocR;
+    }
+    if(compileTable[tIndex].spill_loc_index >= 0) {
+        unspillLogicalReg(tIndex, compileTable[tIndex].physicalReg);
+    }
+    if(updateRefCount)
+        decreaseRefCount(tIndex);
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: allocate register %d for logical register %d %d",
+           compileTable[tIndex].physicalReg, reg, newType);
+#endif
+    return compileTable[tIndex].physicalReg;
+}
+//!a variable will use a physical register allocated for another variable
+
+//!This is used when MOVE_OPT is on, it tries to alias a virtual register with a temporary to remove a move
+int registerAllocMove(int reg, int type, bool isPhysical, int srcReg) {
+    if(srcReg == PhysicalReg_EDI || srcReg == PhysicalReg_ESP || srcReg == PhysicalReg_EBP)
+        ALOGE("can't move from srcReg EDI or ESP or EBP");
+#ifdef DEBUG_REGALLOC
+    ALOGI("in registerAllocMove: reg %d type %d srcReg %d", reg, type, srcReg);
+#endif
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("reg %d type %d not found in registerAllocMove", reg, newType);
+        return -1;
+    }
+
+    decreaseRefCount(index);
+    compileTable[index].physicalReg = srcReg;
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: registerAllocMove %d for logical register %d %d",
+           compileTable[index].physicalReg, reg, newType);
+#endif
+    return srcReg;
+}
+
+//! check whether a physical register is available to be used by a variable
+
+//! data structures accessed:
+//! 1> currentBB->infoBasicBlock[index].allocConstraintsSorted
+//!    sorted from high count to low count
+//! 2> currentBB->allocConstraintsSorted
+//!    sorted from low count to high count
+//! 3> allRegs: whether a physical register is available, indexed by PhysicalReg
+//! NOTE: if a temporary variable is 8-bit, only %eax, %ebx, %ecx, %edx can be used
+int getFreeReg(int type, int reg, int indexToCompileTable) {
+    syncAllRegs();
+    /* handles requests for xmm or ss registers */
+    int k;
+    if(((type & MASK_FOR_TYPE) == LowOpndRegType_xmm) ||
+       ((type & MASK_FOR_TYPE) == LowOpndRegType_ss)) {
+        for(k = PhysicalReg_XMM0; k <= PhysicalReg_XMM7; k++) {
+            if(!allRegs[k].isUsed) return k;
+        }
+        return -1;
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("USED registers: ");
+    for(k = 0; k < 8; k++)
+        ALOGI("%d used: %d time freed: %d callee-saveld: %d", k, allRegs[k].isUsed,
+             allRegs[k].freeTimeStamp, allRegs[k].isCalleeSaved);
+    ALOGI("");
+#endif
+
+    /* a VR is requesting a physical register */
+    if(isVirtualReg(type)) { //find a callee-saved register
+        /* if VR is type GG, check the pre-allocated physical register first */
+        bool isGGVR = compileTable[indexToCompileTable].gType == GLOBALTYPE_GG;
+        if(isGGVR) {
+            int regCandidateT = compileTable[indexToCompileTable].physicalReg_prev;
+            if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+        }
+
+        int index = searchVirtualInfoOfBB((LowOpndRegType)(type&MASK_FOR_TYPE), reg, currentBB);
+        if(index < 0) {
+            ALOGE("VR %d %d not found in infoBasicBlock of currentBB %d (num of VRs %d)",
+                  reg, type, currentBB->bb_index, currentBB->num_regs);
+            dvmAbort();
+        }
+
+        /* check allocConstraints for this VR,
+           return an available physical register with the highest constraint > 0 */
+        for(k = 0; k < 8; k++) {
+            if(currentBB->infoBasicBlock[index].allocConstraintsSorted[k].count == 0) break;
+            int regCandidateT = currentBB->infoBasicBlock[index].allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+        }
+
+        /* WAS: return an available physical register with the lowest constraint
+           NOW: consider a new factor (freeTime) when there is a tie
+                if 2 available physical registers have the same number of constraints
+                choose the one with smaller free time stamp */
+        int currentCount = -1;
+        int index1 = -1;
+        int smallestTime = -1;
+        for(k = 0; k < 8; k++) {
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
+                break; //candidate has higher count than index1
+            if(!allRegs[regCandidateT].isUsed) {
+                if(index1 < 0) {
+                    index1 = k;
+                    currentCount = currentBB->allocConstraintsSorted[k].count;
+                    smallestTime = allRegs[regCandidateT].freeTimeStamp;
+                } else if(allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                    index1 = k;
+                    smallestTime = allRegs[regCandidateT].freeTimeStamp;
+                }
+            }
+        }
+        if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
+        return -1;
+    }
+    /* handle request from a temporary variable or a glue variable */
+    else {
+        bool is8Bit = isTemp8Bit(type, reg);
+
+        /* if the temporary variable is linked to a VR and
+              the VR is not yet allocated to any physical register */
+        int vr_num = compileTable[indexToCompileTable].linkageToVR;
+        if(vr_num >= 0) {
+            int index3 = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, vr_num);
+            if(index3 < 0) {
+                ALOGE("2 in tracing linkage to VR %d", vr_num);
+                dvmAbort();
+            }
+
+            if(compileTable[index3].physicalReg == PhysicalReg_Null) {
+                int index2 = searchVirtualInfoOfBB(LowOpndRegType_gp, vr_num, currentBB);
+                if(index2 < 0) {
+                    ALOGE("1 in tracing linkage to VR %d", vr_num);
+                    dvmAbort();
+                }
+#ifdef DEBUG_REGALLOC
+                ALOGI("in getFreeReg for temporary reg %d, trace the linkage to VR %d",
+                     reg, vr_num);
+#endif
+
+                /* check allocConstraints on the VR
+                   return an available physical register with the highest constraint > 0
+                */
+                for(k = 0; k < 8; k++) {
+                    if(currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count == 0) break;
+                    int regCandidateT = currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].physicalReg;
+#ifdef DEBUG_REGALLOC
+                    ALOGI("check register %d with count %d", regCandidateT,
+                          currentBB->infoBasicBlock[index2].allocConstraintsSorted[k].count);
+#endif
+                    /* if the requesting variable is 8 bit */
+                    if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
+                    assert(regCandidateT < PhysicalReg_Null);
+                    if(!allRegs[regCandidateT].isUsed) return regCandidateT;
+                }
+            }
+        }
+        /* check allocConstraints of the basic block
+           if 2 available physical registers have the same constraint count,
+              return the non callee-saved physical reg */
+        /* enhancement: record the time when a register is freed (freeTimeStamp)
+                        the purpose is to reduce false dependency
+           priority: constraint count, non callee-saved, time freed
+               let x be the lowest constraint count
+               set A be available callee-saved physical registers with count == x
+               set B be available non callee-saved physical registers with count == x
+               if set B is not null, return the one with smallest free time
+               otherwise, return the one in A with smallest free time
+           To ignore whether it is callee-saved, add all candidates to set A
+        */
+        int setAIndex[8];
+        int num_A = 0;
+        int setBIndex[8];
+        int num_B = 0;
+        int index1 = -1; //points to an available physical reg with lowest count
+        int currentCount = -1;
+        for(k = 0; k < 8; k++) {
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            if(is8Bit && regCandidateT > PhysicalReg_EDX) continue;
+
+            if(index1 >= 0 && currentBB->allocConstraintsSorted[k].count > currentCount)
+                break; //candidate has higher count than index1
+            assert(regCandidateT < PhysicalReg_Null);
+            if(!allRegs[regCandidateT].isUsed) {
+                /*To ignore whether it is callee-saved, add all candidates to set A */
+                if(false) {//!allRegs[regCandidateT].isCalleeSaved) { //add to set B
+                    setBIndex[num_B++] = k;
+                } else { //add to set A
+                    setAIndex[num_A++] = k;
+                }
+                if(index1 < 0) {
+                    /* index1 points to a physical reg with lowest count */
+                    index1 = k;
+                    currentCount = currentBB->allocConstraintsSorted[k].count;
+                }
+            }
+        }
+
+        int kk;
+        int smallestTime = -1;
+        index1 = -1;
+        for(kk = 0; kk < num_B; kk++) {
+            k = setBIndex[kk];
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            assert(regCandidateT < PhysicalReg_Null);
+            if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                index1 = k;
+                smallestTime = allRegs[regCandidateT].freeTimeStamp;
+            }
+        }
+        if(index1 >= 0)
+            return currentBB->allocConstraintsSorted[index1].physicalReg;
+        index1 = -1;
+        for(kk = 0; kk < num_A; kk++) {
+            k = setAIndex[kk];
+            int regCandidateT = currentBB->allocConstraintsSorted[k].physicalReg;
+            if(kk == 0 || allRegs[regCandidateT].freeTimeStamp < smallestTime) {
+                index1 = k;
+                smallestTime = allRegs[regCandidateT].freeTimeStamp;
+            }
+        }
+        if(index1 >= 0) return currentBB->allocConstraintsSorted[index1].physicalReg;
+        return -1;
+    }
+    return -1;
+}
+
+//! find a candidate physical register for a variable and spill all variables that are mapped to the candidate
+
+//!
+PhysicalReg spillForLogicalReg(int type, int reg, int indexToCompileTable) {
+    //choose a used register to spill
+    //when a GG virtual register is spilled, write it to interpretd stack, set physicalReg to Null
+    //  at end of the basic block, load spilled GG VR to physical reg
+    //when other types of VR is spilled, write it to interpreted stack, set physicalReg to Null
+    //when a temporary (non-virtual) register is spilled, write it to stack, set physicalReg to Null
+    //can we spill a hard-coded temporary register? YES
+    int k, k2;
+    PhysicalReg allocR;
+
+    //do not try to free a physical reg that is used by more than one logical registers
+    //fix on sep 28, 2009
+    //do not try to spill a hard-coded logical register
+    //do not try to free a physical reg that is outside of the range for 8-bit logical reg
+    /* for each physical register,
+       collect number of non-hardcode entries that are mapped to the physical register */
+    int numOfUses[PhysicalReg_Null];
+    for(k = PhysicalReg_EAX; k < PhysicalReg_Null; k++)
+        numOfUses[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
+            numOfUses[compileTable[k].physicalReg]++;
+        }
+    }
+
+    /* candidates: all non-hardcode entries that are mapped to
+           a physical register that is used by only one entry*/
+    bool is8Bit = isTemp8Bit(type, reg);
+    int candidates[COMPILE_TABLE_SIZE];
+    int num_cand = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(matchType(type, compileTable[k].physicalType) &&
+           compileTable[k].physicalReg != PhysicalReg_Null) {
+            if(is8Bit && compileTable[k].physicalReg > PhysicalReg_EDX) continue; //not a candidate
+            if(!canSpillReg[compileTable[k].physicalReg]) continue; //not a candidate
+            if((compileTable[k].physicalType & LowOpndRegType_hard) == 0 &&
+               numOfUses[compileTable[k].physicalReg] <= 1) {
+                candidates[num_cand++] = k;
+            }
+        }
+    }
+
+    /* go through all candidates:
+       first check GLUE-related entries */
+    int spill_index = -1;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           (compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+            compileTable[k].regNum != PhysicalReg_GLUE)) {
+            allocR = (PhysicalReg)spillLogicalReg(k, true);
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL register used by num %d type %d it is a GLUE register with refCount %d",
+                  compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].refCount);
+#endif
+            return allocR;
+        }
+    }
+
+    /* out of the candates, find a VR that has the furthest next use */
+    int furthestUse = offsetPC;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if((compileTable[k].physicalReg != PhysicalReg_Null) &&
+           matchType(type, compileTable[k].physicalType) &&
+           isVirtualReg(compileTable[k].physicalType)) {
+            int nextUse = getNextAccess(k);
+            if(spill_index < 0 || nextUse > furthestUse) {
+                spill_index = k;
+                furthestUse = nextUse;
+            }
+        }
+    }
+
+    /* spill the VR with the furthest next use */
+    if(spill_index >= 0) {
+        allocR = (PhysicalReg)spillLogicalReg(spill_index, true);
+        return allocR; //the register is still being used
+    }
+
+    /* spill an entry with the smallest refCount */
+    int baseLeftOver = 0;
+    int index = -1;
+    for(k2 = 0; k2 < num_cand; k2++) {
+        k = candidates[k2];
+        if(k != indexForGlue &&
+           (compileTable[k].physicalReg != PhysicalReg_Null) &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0 && //not hard-coded
+           matchType(type, compileTable[k].physicalType)) {
+            if((index < 0) || (compileTable[k].refCount < baseLeftOver)) {
+                baseLeftOver = compileTable[k].refCount;
+                index = k;
+            }
+        }
+    }
+    if(index < 0) {
+        dumpCompileTable();
+        ALOGE("no register to spill for logical %d %d", reg, type);
+        dvmAbort();
+    }
+    allocR = (PhysicalReg)spillLogicalReg(index, true);
+#ifdef DEBUG_REGALLOC
+    ALOGI("SPILL register used by num %d type %d it is a temporary register with refCount %d",
+           compileTable[index].regNum, compileTable[index].physicalType, compileTable[index].refCount);
+#endif
+    return allocR;
+}
+//!spill a variable to memory, the variable is specified by an index to compileTable
+
+//!If the variable is a temporary, get a spill location that is not in use and spill the content to the spill location;
+//!If updateTable is true, set physicalReg to Null;
+//!Return the physical register that was allocated to the variable
+int spillLogicalReg(int spill_index, bool updateTable) {
+    if((compileTable[spill_index].physicalType & LowOpndRegType_hard) != 0) {
+        ALOGE("can't spill a hard-coded register");
+        dvmAbort();
+    }
+    int physicalReg = compileTable[spill_index].physicalReg;
+    if(!canSpillReg[physicalReg]) {
+#ifdef PRINT_WARNING
+        ALOGW("can't spill register %d", physicalReg);
+#endif
+        //dvmAbort(); //this happens in get_virtual_reg where VR is allocated to the same reg as the hardcoded temporary
+    }
+    if(isVirtualReg(compileTable[spill_index].physicalType)) {
+        //spill back to memory
+        dumpToMem(compileTable[spill_index].regNum,
+                  (LowOpndRegType)(compileTable[spill_index].physicalType&MASK_FOR_TYPE),
+                  compileTable[spill_index].physicalReg);
+    }
+    else {
+        //update spill_loc_index
+        int k = getSpillIndex(spill_index == indexForGlue,
+                    getRegSize(compileTable[spill_index].physicalType));
+        compileTable[spill_index].spill_loc_index = 4*k;
+        if(k >= 0)
+            spillIndexUsed[k] = 1;
+        saveToSpillRegion(getRegSize(compileTable[spill_index].physicalType),
+                          compileTable[spill_index].physicalReg, 4*k);
+    }
+    //compileTable[spill_index].physicalReg_prev = compileTable[spill_index].physicalReg;
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: SPILL logical reg %d %d with refCount %d allocated to %d",
+           compileTable[spill_index].regNum,
+           compileTable[spill_index].physicalType, compileTable[spill_index].refCount,
+           compileTable[spill_index].physicalReg);
+#endif
+    if(!updateTable) return PhysicalReg_Null;
+
+    int allocR = compileTable[spill_index].physicalReg;
+    compileTable[spill_index].physicalReg = PhysicalReg_Null;
+    return allocR;
+}
+//! load a varible from memory to physical register, the variable is specified with an index to compileTable
+
+//!If the variable is a temporary, load from spill location and set the flag for the spill location to not used
+int unspillLogicalReg(int spill_index, int physicalReg) {
+    //can't un-spill to %eax in afterCall!!!
+    //what if GG VR is allocated to %eax!!!
+    if(isVirtualReg(compileTable[spill_index].physicalType)) {
+        get_virtual_reg_noalloc(compileTable[spill_index].regNum,
+                                getRegSize(compileTable[spill_index].physicalType),
+                                physicalReg, true);
+    }
+    else {
+        loadFromSpillRegion(getRegSize(compileTable[spill_index].physicalType),
+                            physicalReg, compileTable[spill_index].spill_loc_index);
+        spillIndexUsed[compileTable[spill_index].spill_loc_index >> 2] = 0;
+        compileTable[spill_index].spill_loc_index = -1;
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("REGALLOC: UNSPILL logical reg %d %d with refCount %d", compileTable[spill_index].regNum,
+           compileTable[spill_index].physicalType, compileTable[spill_index].refCount);
+#endif
+    return PhysicalReg_Null;
+}
+
+//!spill a virtual register to memory
+
+//!if the current value of a VR is constant, write immediate to memory;
+//!if the current value of a VR is in a physical register, call spillLogicalReg to dump content of the physical register to memory;
+//!ifupdateTable is true, set the physical register for VR to Null and decrease reference count of the virtual register
+int spillVirtualReg(int vrNum, LowOpndRegType type, bool updateTable) {
+    int index = searchCompileTable(type | LowOpndRegType_virtual, vrNum);
+    if(index < 0) {
+        ALOGE("can't find VR %d %d in spillVirtualReg", vrNum, type);
+        return -1;
+    }
+    //check whether it is const
+    int value[2];
+    int isConst = isVirtualRegConstant(vrNum, type, value, false); //do not update refCount
+    if(isConst == 1 || isConst == 3) {
+        dumpImmToMem(vrNum, OpndSize_32, value[0]);
+    }
+    if(getRegSize(type) == OpndSize_64 && (isConst == 2 || isConst == 3)) {
+        dumpImmToMem(vrNum+1, OpndSize_32, value[1]);
+    }
+    if(isConst != 3 && compileTable[index].physicalReg != PhysicalReg_Null)
+        spillLogicalReg(index, updateTable);
+    if(updateTable) decreaseRefCount(index);
+    return -1;
+}
+
+//! spill variables that are mapped to physical register (regNum)
+
+//!
+int spillForHardReg(int regNum, int type) {
+    //find an entry that uses the physical register
+    int spill_index = -1;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(k != indexForGlue &&
+           compileTable[k].physicalReg == regNum &&
+           matchType(type, compileTable[k].physicalType)) {
+            spill_index = k;
+            if(compileTable[k].regNum == regNum && compileTable[k].physicalType == type)
+                continue;
+            if(inGetVR_num >= 0 && compileTable[k].regNum == inGetVR_num && compileTable[k].physicalType == (type | LowOpndRegType_virtual))
+                continue;
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL logical reg %d %d to free hard-coded reg %d %d",
+                   compileTable[spill_index].regNum, compileTable[spill_index].physicalType,
+                   regNum, type);
+            if(compileTable[spill_index].physicalType & LowOpndRegType_hard) dumpCompileTable();
+#endif
+            assert(spill_index < COMPILE_TABLE_SIZE);
+            spillLogicalReg(spill_index, true);
+        }
+    }
+    return regNum;
+}
+////////////////////////////////////////////////////////////////
+//! update allocConstraints of the current basic block
+
+//! allocConstraints specify how many times a hardcoded register is used in this basic block
+void updateCurrentBBWithConstraints(PhysicalReg reg) {
+    if(reg > PhysicalReg_EBP) {
+        ALOGE("register %d out of range in updateCurrentBBWithConstraints", reg);
+    }
+    currentBB->allocConstraints[reg].count++;
+}
+//! sort allocConstraints and save the result in allocConstraintsSorted
+
+//! allocConstraints specify how many times a virtual register is linked to a hardcode register
+//! it is updated in getVirtualRegInfo and merged by mergeEntry2
+int sortAllocConstraint(RegAllocConstraint* allocConstraints,
+                        RegAllocConstraint* allocConstraintsSorted, bool fromHighToLow) {
+    int ii, jj;
+    int num_sorted = 0;
+    for(jj = 0; jj < 8; jj++) {
+        //figure out where to insert allocConstraints[jj]
+        int count = allocConstraints[jj].count;
+        int regT = allocConstraints[jj].physicalReg;
+        assert(regT < PhysicalReg_Null);
+        int insertIndex = -1;
+        for(ii = 0; ii < num_sorted; ii++) {
+            int regT2 = allocConstraintsSorted[ii].physicalReg;
+            assert(regT2 < PhysicalReg_Null);
+            if(allRegs[regT].isCalleeSaved &&
+               count == allocConstraintsSorted[ii].count) {
+                insertIndex = ii;
+                break;
+            }
+            if((!allRegs[regT].isCalleeSaved) &&
+               count == allocConstraintsSorted[ii].count &&
+               (!allRegs[regT2].isCalleeSaved)) { //skip until found one that is not callee-saved
+                insertIndex = ii;
+                break;
+            }
+            if((fromHighToLow && count > allocConstraintsSorted[ii].count) ||
+               ((!fromHighToLow) && count < allocConstraintsSorted[ii].count)) {
+                insertIndex = ii;
+                break;
+            }
+        }
+        if(insertIndex < 0) {
+            allocConstraintsSorted[num_sorted].physicalReg = (PhysicalReg)regT;
+            allocConstraintsSorted[num_sorted].count = count;
+            num_sorted++;
+        } else {
+            for(ii = num_sorted-1; ii >= insertIndex; ii--) {
+                allocConstraintsSorted[ii+1] = allocConstraintsSorted[ii];
+            }
+            allocConstraintsSorted[insertIndex] = allocConstraints[jj];
+            num_sorted++;
+        }
+    } //for jj
+#ifdef DEBUG_ALLOC_CONSTRAINT
+    for(jj = 0; jj < 8; jj++) {
+        if(allocConstraintsSorted[jj].count > 0)
+            ALOGI("%d: register %d has count %d", jj, allocConstraintsSorted[jj].physicalReg, allocConstraintsSorted[jj].count);
+    }
+#endif
+    return 0;
+}
+//! find the entry for a given virtual register in compileTable
+
+//!
+int findVirtualRegInTable(u2 vA, LowOpndRegType type, bool printError) {
+    int k = searchCompileTable(type | LowOpndRegType_virtual, vA);
+    if(k < 0 && printError) {
+        ALOGE("findVirtualRegInTable virtual register %d type %d", vA, type);
+        dvmAbort();
+    }
+    return k;
+}
+
+//! check whether a virtual register is constant
+
+//! the value of the constant is stored in valuePtr; if updateRefCount is true & the VR is constant, reference count for the VR will be reduced by 1
+int isVirtualRegConstant(int regNum, LowOpndRegType type, int* valuePtr, bool updateRefCount) {
+
+    OpndSize size = getRegSize(type);
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+#ifdef DEBUG_CONST
+        ALOGI("constVRTable VR %d isConst %d value %x", constVRTable[k].regNum, constVRTable[k].isConst, constVRTable[k].value);
+#endif
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    bool isConstL = false;
+    bool isConstH = false;
+    if(indexL >= 0) {
+        isConstL = constVRTable[indexL].isConst;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        isConstH = constVRTable[indexH].isConst;
+    }
+
+    if((isConstL || isConstH)) {
+        if(size == OpndSize_64 && isConstH)
+            valuePtr[1] = constVRTable[indexH].value;
+        if(isConstL)
+            *valuePtr = constVRTable[indexL].value;
+    }
+    if((isConstL && size == OpndSize_32) || (isConstL && isConstH)) {
+        if(updateRefCount) {
+            int indexOrig = searchCompileTable(type | LowOpndRegType_virtual, regNum);
+            if(indexOrig < 0) ALOGE("can't find VR in isVirtualRegConstant num %d type %d", regNum, type);
+            decreaseRefCount(indexOrig);
+        }
+#ifdef DEBUG_CONST
+        ALOGI("VR %d %d is const case", regNum, type);
+#endif
+        return 3;
+    }
+    if(size == OpndSize_32) return 0;
+    if(isConstL) return 1;
+    if(isConstH) return 2;
+    return 0;
+}
+
+//!update RegAccessType of virtual register vB given RegAccessType of vA
+
+//!RegAccessType can be D, L, H
+//!D means full definition, L means only lower-half is defined, H means only higher half is defined
+//!we say a VR has no exposed usage in a basic block if the accessType is D or DU
+//!we say a VR has exposed usage in a basic block if the accessType is not D nor DU
+//!we say a VR has exposed usage in other basic blocks (hasOtherExposedUsage) if
+//!  there exists another basic block where VR has exposed usage in that basic block
+//!A can be U, D, L, H, UD, UL, UH, DU, LU, HU (merged result)
+//!B can be U, D, UD, DU (an entry for the current bytecode)
+//!input isAPartiallyOverlapB can be any value between -1 to 6
+//!if A is xmm: gp B lower half of A, (isAPartiallyOverlapB is 1)
+//!             gp B higher half of A, (isAPartiallyOverlapB is 2)
+//!             lower half of A covers the higher half of xmm B  (isAPartiallyOverlapB is 4)
+//!             higher half of A covers the lower half of xmm B   (isAPartiallyOverlapB is 3)
+//!if A is gp:  A covers the lower half of xmm B, (isAPartiallyOverlapB is 5)
+//!             A covers the higher half of xmm B (isAPartiallyOverlapB is 6)
+RegAccessType updateAccess1(RegAccessType A, OverlapCase isAPartiallyOverlapB) {
+    if(A == REGACCESS_D || A == REGACCESS_DU || A == REGACCESS_UD) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A)
+            return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+        return REGACCESS_H;
+    }
+    if(A == REGACCESS_L || A == REGACCESS_LU || A == REGACCESS_UL) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A || isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B)
+            return REGACCESS_N;
+        if(isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
+            return REGACCESS_H;
+    }
+    if(A == REGACCESS_H || A == REGACCESS_HU || A == REGACCESS_UH) {
+        if(isAPartiallyOverlapB == OVERLAP_ALIGN || isAPartiallyOverlapB == OVERLAP_A_IS_HIGH_OF_B)
+            return REGACCESS_H;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_LOW_OF_A || isAPartiallyOverlapB == OVERLAP_HIGH_OF_A_IS_LOW_OF_B)
+            return REGACCESS_N;
+        if(isAPartiallyOverlapB == OVERLAP_B_IS_HIGH_OF_A) return REGACCESS_D;
+        if(isAPartiallyOverlapB == OVERLAP_LOW_OF_A_IS_HIGH_OF_B || isAPartiallyOverlapB == OVERLAP_A_IS_LOW_OF_B)
+            return REGACCESS_L;
+    }
+    return REGACCESS_N;
+}
+//! merge RegAccessType C1 with RegAccessType C2
+
+//!C can be N,L,H,D
+RegAccessType updateAccess2(RegAccessType C1, RegAccessType C2) {
+    if(C1 == REGACCESS_D || C2 == REGACCESS_D) return REGACCESS_D;
+    if(C1 == REGACCESS_N) return C2;
+    if(C2 == REGACCESS_N) return C1;
+    if(C1 == REGACCESS_L && C2 == REGACCESS_H) return REGACCESS_D;
+    if(C1 == REGACCESS_H && C2 == REGACCESS_L) return REGACCESS_D;
+    return C1;
+}
+//! merge RegAccessType C with RegAccessType B
+
+//!C can be N,L,H,D
+//!B can be U, D, UD, DU
+RegAccessType updateAccess3(RegAccessType C, RegAccessType B) {
+    if(B == REGACCESS_D || B == REGACCESS_DU) return B; //no exposed usage
+    if(B == REGACCESS_U || B == REGACCESS_UD) {
+        if(C == REGACCESS_N) return B;
+        if(C == REGACCESS_L) return REGACCESS_LU;
+        if(C == REGACCESS_H) return REGACCESS_HU;
+        if(C == REGACCESS_D) return REGACCESS_DU;
+    }
+    return B;
+}
+//! merge RegAccessType A with RegAccessType B
+
+//!argument isBPartiallyOverlapA can be any value between -1 and 2
+//!0 means fully overlapping, 1 means B is the lower half, 2 means B is the higher half
+RegAccessType mergeAccess2(RegAccessType A, RegAccessType B, OverlapCase isBPartiallyOverlapA) {
+    if(A == REGACCESS_UD || A == REGACCESS_UL || A == REGACCESS_UH ||
+       A == REGACCESS_DU || A == REGACCESS_LU || A == REGACCESS_HU) return A;
+    if(A == REGACCESS_D) {
+        if(B == REGACCESS_D) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_DU;
+        if(B == REGACCESS_UD) return REGACCESS_DU;
+        if(B == REGACCESS_DU) return B;
+    }
+    if(A == REGACCESS_U) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+        if(B == REGACCESS_U) return A;
+        if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_UD && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_UD && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_UL;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_UH;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_UD;
+    }
+    if(A == REGACCESS_L) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_L;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_D;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_LU;
+        if(B == REGACCESS_UD) return REGACCESS_LU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_LU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_DU;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
+    }
+    if(A == REGACCESS_H) {
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_D;
+        if(B == REGACCESS_D && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_H;
+        if(B == REGACCESS_D && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_D;
+        if(B == REGACCESS_U) return REGACCESS_HU;
+        if(B == REGACCESS_UD) return REGACCESS_HU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_LOW_OF_A) return REGACCESS_DU;
+        if(B == REGACCESS_DU && isBPartiallyOverlapA == OVERLAP_B_COVER_HIGH_OF_A) return REGACCESS_HU;
+        if(B == REGACCESS_DU && (isBPartiallyOverlapA == OVERLAP_B_COVER_A)) return REGACCESS_DU;
+    }
+    return REGACCESS_N;
+}
+
+//!determines which part of a use is from a given definition
+
+//!reachingDefLive tells us which part of the def is live at this point
+//!isDefPartiallyOverlapUse can be any value between -1 and 2
+RegAccessType setAccessTypeOfUse(OverlapCase isDefPartiallyOverlapUse, RegAccessType reachingDefLive) {
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_A)
+        return reachingDefLive;
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_LOW_OF_A) { //def covers the low half of use
+        return REGACCESS_L;
+    }
+    if(isDefPartiallyOverlapUse == OVERLAP_B_COVER_HIGH_OF_A) {
+        return REGACCESS_H;
+    }
+    return REGACCESS_N;
+}
+
+//! search currentBB->defUseTable to find a def for regNum at offsetPC
+
+//!
+DefUsePair* searchDefUseTable(int offsetPC, int regNum, LowOpndRegType pType) {
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        if(ptr->def.offsetPC == offsetPC &&
+           ptr->def.regNum == regNum &&
+           ptr->def.physicalType == pType) {
+            return ptr;
+        }
+        ptr = ptr->next;
+    }
+    return NULL;
+}
+void printDefUseTable() {
+    ALOGI("PRINT defUseTable --------");
+    DefUsePair* ptr = currentBB->defUseTable;
+    while(ptr != NULL) {
+        ALOGI("  def @ %x of VR %d %d has %d uses", ptr->def.offsetPC,
+              ptr->def.regNum, ptr->def.physicalType,
+              ptr->num_uses);
+        DefOrUseLink* ptr2 = ptr->uses;
+        while(ptr2 != NULL) {
+            ALOGI("    use @ %x of VR %d %d accessType %d", ptr2->offsetPC,
+                  ptr2->regNum,
+                  ptr2->physicalType,
+                  ptr2->accessType);
+            ptr2 = ptr2->next;
+        }
+        ptr = ptr->next;
+    }
+}
+//! when a VR is used, check whether a transfer from memory to XMM is necessary
+
+//!
+int updateVRAtUse(int reg, LowOpndRegType pType, int regAll) {
+    int k;
+    for(k = 0; k < currentBB->num_xfer_points; k++) {
+        if(currentBB->xferPoints[k].offsetPC == offsetPC &&
+           currentBB->xferPoints[k].xtype == XFER_MEM_TO_XMM &&
+           currentBB->xferPoints[k].regNum == reg &&
+           currentBB->xferPoints[k].physicalType == pType) {
+#ifdef DEBUG_XFER_POINTS
+            ALOGI("XFER from memory to xmm %d", reg);
+#endif
+            move_mem_to_reg_noalloc(OpndSize_64,
+                                    4*currentBB->xferPoints[k].regNum, PhysicalReg_FP, true,
+                                    MemoryAccess_VR, currentBB->xferPoints[k].regNum,
+                                    regAll, true);
+        }
+    }
+    return 0;
+}
+///////////////////////////////////////////////////////////////////////////////
+// DEAD/USELESS STATEMENT ELMINATION
+// bytecodes can be removed if a bytecode has no side effect and the defs are not used
+// this optimization is guarded with DSE_OPT
+// currently, this optimization is not on, since it does not provide observable performance improvement
+//     and it increases compilation time
+
+/* we remove a maximal of 40 bytecodes within a single basic block */
+#define MAX_NUM_DEAD_PC_IN_BB 40
+int deadPCs[MAX_NUM_DEAD_PC_IN_BB];
+int num_dead_pc = 0;
+//! collect all PCs that can be removed
+
+//! traverse each byte code in the current basic block and check whether it can be removed, if yes, update deadPCs
+void getDeadStmts() {
+    BasicBlock_O1* bb = currentBB;
+    int k;
+    num_dead_pc = 0;
+    //traverse each bytecode in the basic block
+    //update offsetPC, rPC & inst
+    u2* rPC_start = (u2*)currentMethod->insns;
+    MIR* mir;
+    for(mir = bb->jitBasicBlock->firstMIRInsn; mir; mir = mir->next) {
+        offsetPC = mir->seqNum;
+        rPC = rPC_start + mir->offset;
+        if(mir->dalvikInsn.opcode >= kNumPackedOpcodes) continue;
+#ifdef DEBUG_DSE
+        ALOGI("DSE: offsetPC %x", offsetPC);
+#endif
+        inst = FETCH(0);
+        bool isDeadStmt = true;
+        getVirtualRegInfo(infoByteCode);
+        u2 inst_op = INST_INST(inst);
+	//skip bytecodes with side effect
+        if(inst_op != OP_CONST_STRING && inst_op != OP_CONST_STRING_JUMBO &&
+           inst_op != OP_MOVE && inst_op != OP_MOVE_OBJECT &&
+           inst_op != OP_MOVE_FROM16 && inst_op != OP_MOVE_OBJECT_FROM16 &&
+           inst_op != OP_MOVE_16 && inst_op != OP_CONST_CLASS &&
+           inst_op != OP_MOVE_OBJECT_16 && inst_op != OP_MOVE_WIDE &&
+           inst_op != OP_MOVE_WIDE_FROM16 && inst_op != OP_MOVE_WIDE_16 &&
+           inst_op != OP_MOVE_RESULT && inst_op != OP_MOVE_RESULT_OBJECT) {
+            continue;
+        }
+        //some statements do not define any VR!!!
+        int num_defs = 0;
+        for(k = 0; k < num_regs_per_bytecode; k++) {
+            if(infoByteCode[k].accessType == REGACCESS_D ||
+               infoByteCode[k].accessType == REGACCESS_UD ||
+               infoByteCode[k].accessType == REGACCESS_DU) { //search defUseTable
+                num_defs++;
+                DefUsePair* indexT = searchDefUseTable(offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+                if(indexT == NULL) {
+                    ALOGE("def at %x of VR %d %d not in table",
+                           offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+                    return;
+                }
+                if(indexT->num_uses > 0) {
+                    isDeadStmt = false;
+                    break;
+                } else {
+#ifdef DEBUG_DSE
+                    ALOGI("DSE: num_uses is %d for def at %d for VR %d %d", indexT->num_uses,
+                          offsetPC, infoByteCode[k].regNum, infoByteCode[k].physicalType);
+#endif
+                }
+            }
+        } //for k
+        if(num_defs == 0) isDeadStmt = false;
+        if(isDeadStmt && num_dead_pc < MAX_NUM_DEAD_PC_IN_BB) {
+#ifdef DEBUG_DSE
+            ALOGI("DSE: stmt at %x is dead", offsetPC);
+#endif
+            deadPCs[num_dead_pc++] = offsetPC;
+        }
+    } //for offsetPC
+#ifdef DEBUG_DSE
+    ALOGI("Dead Stmts: ");
+    for(k = 0; k < num_dead_pc; k++) ALOGI("%x ", deadPCs[k]);
+    ALOGI("");
+#endif
+}
+//! entry point to remove dead statements
+
+//! recursively call getDeadStmts and remove uses in defUseTable that are from a dead PC
+//! until there is no change to number of dead PCs
+void removeDeadDefs() {
+    int k;
+    int deadPCs_2[MAX_NUM_DEAD_PC_IN_BB];
+    int num_dead_pc_2 = 0;
+    getDeadStmts();
+    if(num_dead_pc == 0) return;
+    DefUsePair* ptr = NULL;
+    DefOrUseLink* ptrUse = NULL;
+    DefOrUseLink* ptrUse_prev = NULL;
+    while(true) {
+        //check all the uses in defUseTable and remove any use that is from a dead PC
+        ptr = currentBB->defUseTable;
+        while(ptr != NULL) {
+            int k3;
+            ptrUse = ptr->uses;
+            ptrUse_prev = NULL;
+            while(ptrUse != NULL) {
+                bool isIn = false;
+                for(k3 = 0; k3 < num_dead_pc; k3++) {
+                    if(ptrUse->offsetPC == deadPCs[k3]) {
+                        isIn = true;
+                        break;
+                    }
+                }//k3
+                if(!isIn) {
+                    ptrUse_prev = ptrUse;
+                    ptrUse = ptrUse->next; //next use
+                }
+                else {
+                    //go to next use and remove ptrUse
+#ifdef DEBUG_DSE
+                    ALOGI("DSE: remove usage at offsetPC %d reached by def at %d", ptrUse->offsetPC,
+                           ptr->def.offsetPC);
+#endif
+                    DefOrUseLink* nextP = ptrUse->next;
+                    if(ptrUse == ptr->useTail) ptr->useTail = ptrUse_prev;
+                    free(ptrUse);
+                    if(ptrUse_prev == NULL) {
+                        ptr->uses = nextP;
+                    } else {
+                        ptrUse_prev->next = nextP;
+                    }
+                    ptrUse = nextP; //do not update ptrUse_prev
+                    ptr->num_uses--;
+                }
+            }//while ptrUse
+            ptr = ptr->next;
+        }//while ptr
+	//save deadPCs in deadPCs_2
+        num_dead_pc_2 = num_dead_pc;
+        for(k = 0; k < num_dead_pc_2; k++)
+            deadPCs_2[k] = deadPCs[k];
+	//update deadPCs
+        getDeadStmts();
+	//if no change to number of dead PCs, break out of the while loop
+        if(num_dead_pc_2 == num_dead_pc) break;
+    }//while
+#ifdef DEBUG_DSE
+    ALOGI("DSE: DEAD STMTS: ");
+    for(k = 0; k < num_dead_pc; k++) {
+        ALOGI("%d ", deadPCs[k]);
+    }
+    ALOGI("");
+#endif
+}
+/////////////////////////////////////////////////////////////
+//!search memVRTable for a given virtual register
+
+//!
+int searchMemTable(int regNum) {
+    int k;
+    for(k = 0; k < num_memory_vr; k++) {
+        if(memVRTable[k].regNum == regNum) {
+            return k;
+        }
+    }
+    ALOGW("in searchMemTable can't find VR %d num_memory_vr %d", regNum, num_memory_vr);
+    return -1;
+}
+/////////////////////////////////////////////////////////////////////////
+// A VR is already in memory && NULL CHECK
+//!check whether the latest content of a VR is in memory
+
+//!
+bool isInMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL < 0) return false;
+    if(size == OpndSize_64 && indexH < 0) return false;
+    if(!memVRTable[indexL].inMemory) return false;
+    if(size == OpndSize_64 && (!memVRTable[indexH].inMemory)) return false;
+    return true;
+}
+//!set field inMemory of memVRTable to true
+
+//!
+void setVRToMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return;
+    }
+    memVRTable[indexL].inMemory = true;
+    if(size == OpndSize_64) {
+        if(indexH < 0) {
+            ALOGE("VR %d not in memVRTable", regNum+1);
+            return;
+        }
+        memVRTable[indexH].inMemory = true;
+    }
+}
+//! check whether null check for a VR is performed previously
+
+//!
+bool isVRNullCheck(int regNum, OpndSize size) {
+    if(size != OpndSize_32) {
+        ALOGE("isVRNullCheck size should be 32");
+        dvmAbort();
+    }
+    int indexL = searchMemTable(regNum);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return false;
+    }
+    return memVRTable[indexL].nullCheckDone;
+}
+bool isVRBoundCheck(int vr_array, int vr_index) {
+    int indexL = searchMemTable(vr_array);
+    if(indexL < 0) {
+        ALOGE("isVRBoundCheck: VR %d not in memVRTable", vr_array);
+        return false;
+    }
+    if(memVRTable[indexL].boundCheck.indexVR == vr_index)
+        return memVRTable[indexL].boundCheck.checkDone;
+    return false;
+}
+//! set nullCheckDone in memVRTable to true
+
+//!
+void setVRNullCheck(int regNum, OpndSize size) {
+    if(size != OpndSize_32) {
+        ALOGE("setVRNullCheck size should be 32");
+        dvmAbort();
+    }
+    int indexL = searchMemTable(regNum);
+    if(indexL < 0) {
+        ALOGE("VR %d not in memVRTable", regNum);
+        return;
+    }
+    memVRTable[indexL].nullCheckDone = true;
+}
+void setVRBoundCheck(int vr_array, int vr_index) {
+    int indexL = searchMemTable(vr_array);
+    if(indexL < 0) {
+        ALOGE("setVRBoundCheck: VR %d not in memVRTable", vr_array);
+        return;
+    }
+    memVRTable[indexL].boundCheck.indexVR = vr_index;
+    memVRTable[indexL].boundCheck.checkDone = true;
+}
+void clearVRBoundCheck(int regNum, OpndSize size) {
+    int k;
+    for(k = 0; k < num_memory_vr; k++) {
+        if(memVRTable[k].regNum == regNum ||
+           (size == OpndSize_64 && memVRTable[k].regNum == regNum+1)) {
+            memVRTable[k].boundCheck.checkDone = false;
+        }
+        if(memVRTable[k].boundCheck.indexVR == regNum ||
+           (size == OpndSize_64 && memVRTable[k].boundCheck.indexVR == regNum+1)) {
+            memVRTable[k].boundCheck.checkDone = false;
+        }
+    }
+}
+//! set inMemory of memVRTable to false
+
+//!
+void clearVRToMemory(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL >= 0) {
+        memVRTable[indexL].inMemory = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        memVRTable[indexH].inMemory = false;
+    }
+}
+//! set nullCheckDone of memVRTable to false
+
+//!
+void clearVRNullCheck(int regNum, OpndSize size) {
+    int indexL = searchMemTable(regNum);
+    int indexH = -1;
+    if(size == OpndSize_64) indexH = searchMemTable(regNum+1);
+    if(indexL >= 0) {
+        memVRTable[indexL].nullCheckDone = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        memVRTable[indexH].nullCheckDone = false;
+    }
+}
+
+//! Extend Virtual Register life
+
+//! Requests that the life of a specific virtual register be extended. This ensures
+//! that its mapping to a physical register won't be canceled while the extension
+//! request is valid. NOTE: This does not support 64-bit values (when two adjacent
+//! VRs are used)
+//! @see cancelVRFreeDelayRequest
+//! @see getVRFreeDelayRequested
+//! @see VRFreeDelayFlags
+//! @param regNum is the VR number
+//! @param reason explains why freeing must be delayed. A single or combination
+//! of VRFreeDelayFlags should be used.
+//! @return negative value if request failed
+int requestVRFreeDelay(int regNum, u4 reason) {
+    //TODO Add 64-bit operand support when needed
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        memVRTable[indexL].delayFreeFlags |= reason;
+    } else {
+        ALOGE("requestVRFreeDelay: VR %d not in memVRTable", regNum);
+    }
+    return indexL;
+}
+
+//! Cancel request for virtual register life extension
+
+//! Cancels any outstanding requests to extended liveness of VR. Additionally,
+//! this ensures that if the VR is no longer life after this point, it will
+//! no longer be associated with a physical register which can then be reused.
+//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
+//! @see requestVRFreeDelay
+//! @see getVRFreeDelayRequested
+//! @see VRFreeDelayFlags
+//! @param regNum is the VR number
+//! @param reason explains what freeing delay request should be canceled. A single
+//! or combination of VRFreeDelayFlags should be used.
+void cancelVRFreeDelayRequest(int regNum, u4 reason) {
+    //TODO Add 64-bit operand support when needed
+    bool needCallToFreeReg = false;
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        if((memVRTable[indexL].delayFreeFlags & reason) != VRDELAY_NONE) { // don't cancel delay if it wasn't requested
+            memVRTable[indexL].delayFreeFlags ^= reason; // only cancel this particular reason, not all others
+            if(memVRTable[indexL].delayFreeFlags == VRDELAY_NONE)
+                needCallToFreeReg = true; // freeReg might want to free this VR now if there is no longer a valid delay
+        }
+    }
+    if(needCallToFreeReg)
+        freeReg(true);
+}
+
+//! Gets status of virtual register free delay request
+
+//! Finds out if there was a delay request for freeing this VR.
+//! NOTE: This does not support 64-bit values (when two adjacent VRs are used)
+//! @see requestVRFreeDelay
+//! @see cancelVRFreeDelayRequest
+//! @param regNum is the VR number
+//! @return true if VR has an active delay request
+bool getVRFreeDelayRequested(int regNum) {
+    //TODO Add 64-bit operand support when needed
+    int indexL = searchMemTable(regNum);
+    if(indexL >= 0) {
+        if(memVRTable[indexL].delayFreeFlags != VRDELAY_NONE)
+            return true;
+        return false;
+    }
+    return false;
+}
+
+//! find the basic block that a bytecode is in
+
+//!
+BasicBlock_O1* findForOffset(int offset) {
+    int k;
+    for(k = 0; k < num_bbs_for_method; k++) {
+        if(method_bbs_sorted[k]->pc_start <= offset && method_bbs_sorted[k]->pc_end > offset)
+            return method_bbs_sorted[k];
+    }
+    return NULL;
+}
+void dump_CFG(Method* method);
+
+int current_bc_size = -1;
+
+//! check whether a virtual register is used in a basic block
+
+//!
+bool isUsedInBB(int regNum, int type, BasicBlock_O1* bb) {
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        if(bb->infoBasicBlock[k].physicalType == (type&MASK_FOR_TYPE) && bb->infoBasicBlock[k].regNum == regNum)
+            return true;
+    }
+    return false;
+}
+//! return the index to infoBasicBlock for a given virtual register
+
+//! return -1 if not found
+int searchVirtualInfoOfBB(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
+    int k;
+    for(k = 0; k < bb->num_regs; k++) {
+        if(bb->infoBasicBlock[k].physicalType == type && bb->infoBasicBlock[k].regNum == regNum)
+            return k;
+    }
+    return -1;
+}
+//! return the index to compileTable for a given virtual register
+
+//! return -1 if not found
+int searchCompileTable(int type, int regNum) { //returns the index
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].physicalType == type && compileTable[k].regNum == regNum)
+            return k;
+    }
+    return -1;
+}
+//!check whether a physical register for a variable with typeA will work for another variable with typeB
+
+//!Type LowOpndRegType_ss is compatible with type LowOpndRegType_xmm
+bool matchType(int typeA, int typeB) {
+    if((typeA & MASK_FOR_TYPE) == (typeB & MASK_FOR_TYPE)) return true;
+    if((typeA & MASK_FOR_TYPE) == LowOpndRegType_ss &&
+       (typeB & MASK_FOR_TYPE) == LowOpndRegType_xmm) return true;
+    if((typeA & MASK_FOR_TYPE) == LowOpndRegType_xmm &&
+       (typeB & MASK_FOR_TYPE) == LowOpndRegType_ss) return true;
+    return false;
+}
+//!check whether a virtual register is used in the current bytecode
+
+//!
+bool isUsedInByteCode(int regNum, int type) {
+    getVirtualRegInfo(infoByteCode);
+    int k;
+    for(k = 0; k < num_regs_per_bytecode; k++) {
+        if(infoByteCode[k].physicalType == (type&MASK_FOR_TYPE) && infoByteCode[k].regNum == regNum)
+            return true;
+    }
+    return false;
+}
+//! obsolete
+bool defineFirst(int atype) {
+    if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
+        return true;
+    return false;
+}
+//!check whether a virtual register is updated in a basic block
+
+//!
+bool notUpdated(RegAccessType atype) {
+    if(atype == REGACCESS_U) return true;
+    return false;
+}
+//!check whether a virtual register has exposed usage within a given basic block
+
+//!
+bool hasExposedUsage2(BasicBlock_O1* bb, int index) {
+    RegAccessType atype = bb->infoBasicBlock[index].accessType;
+    if(atype == REGACCESS_D || atype == REGACCESS_L || atype == REGACCESS_H || atype == REGACCESS_DU)
+        return false;
+    return true;
+}
+//! return the spill location that is not used
+
+//!
+int getSpillIndex(bool isGLUE, OpndSize size) {
+    if(isGLUE) return 0;
+    int k;
+    for(k = 1; k <= MAX_SPILL_JIT_IA-1; k++) {
+        if(size == OpndSize_64) {
+            if(k < MAX_SPILL_JIT_IA-1 && spillIndexUsed[k] == 0 && spillIndexUsed[k+1] == 0)
+                return k;
+        }
+        else if(spillIndexUsed[k] == 0) {
+            return k;
+        }
+    }
+    ALOGE("can't find spill position in spillLogicalReg");
+    return -1;
+}
+//!this is called before generating a native code, it sets entries in array canSpillReg to true
+
+//!startNativeCode must be paired with endNativeCode
+void startNativeCode(int vr_num, int vr_type) {
+    int k;
+    for(k = 0; k < PhysicalReg_Null; k++) {
+        canSpillReg[k] = true;
+    }
+    inGetVR_num = vr_num;
+    inGetVR_type = vr_type;
+}
+//! called right after generating a native code
+
+//!It sets entries in array canSpillReg to true and reset inGetVR_num to -1
+void endNativeCode() {
+    int k;
+    for(k = 0; k < PhysicalReg_Null; k++) {
+        canSpillReg[k] = true;
+    }
+    inGetVR_num = -1;
+}
+//! set canSpillReg[physicalReg] to false
+
+//!
+void donotSpillReg(int physicalReg) {
+    canSpillReg[physicalReg] = false;
+}
+//! set canSpillReg[physicalReg] to true
+
+//!
+void doSpillReg(int physicalReg) {
+    canSpillReg[physicalReg] = true;
+}
+//! touch hardcoded register %ecx and reduce its reference count
+
+//!
+int touchEcx() {
+    //registerAlloc will spill logical reg that is mapped to ecx
+    //registerAlloc will reduce refCount
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_ECX, true, true);
+    return 0;
+}
+//! touch hardcoded register %eax and reduce its reference count
+
+//!
+int touchEax() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EAX, true, true);
+    return 0;
+}
+int touchEsi() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_ESI, true, true);
+    return 0;
+}
+int touchXmm1() {
+    registerAlloc(LowOpndRegType_xmm, XMM_1, true, true);
+    return 0;
+}
+int touchEbx() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EBX, true, true);
+    return 0;
+}
+
+//! touch hardcoded register %edx and reduce its reference count
+
+//!
+int touchEdx() {
+    registerAlloc(LowOpndRegType_gp, PhysicalReg_EDX, true, true);
+    return 0;
+}
+
+#ifdef HACK_FOR_DEBUG
+//for debugging purpose, instructions are added at a certain place
+bool hacked = false;
+void hackBug() {
+  if(!hacked && iget_obj_inst == 13) {
+#if 0
+    move_reg_to_reg_noalloc(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ECX, true);
+    //move from ebx to ecx & update compileTable for v3
+    int tIndex = searchCompileTable(LowOpndRegType_virtual | LowOpndRegType_gp, 3);
+    if(tIndex < 0) ALOGE("hack can't find VR3");
+    compileTable[tIndex].physicalReg = PhysicalReg_ECX;
+#else
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBX, true, 12, PhysicalReg_FP, true);
+#endif
+  }
+}
+void hackBug2() {
+  if(!hacked && iget_obj_inst == 13) {
+    dump_imm_mem_noalloc(Mnemonic_MOV, OpndSize_32, 0, 12, PhysicalReg_FP, true);
+    hacked = true;
+  }
+}
+#endif
+
+//! this function is called before calling a helper function or a vm function
+int beforeCall(const char* target) { //spill all live registers
+    if(currentBB == NULL) return -1;
+
+    /* special case for ncgGetEIP: this function only updates %edx */
+    if(!strcmp(target, "ncgGetEIP")) {
+        touchEdx();
+        return -1;
+    }
+
+    /* these functions use %eax for the return value */
+    if((!strcmp(target, "dvmInstanceofNonTrivial")) ||
+       (!strcmp(target, "dvmUnlockObject")) ||
+       (!strcmp(target, "dvmAllocObject")) ||
+       (!strcmp(target, "dvmAllocArrayByClass")) ||
+       (!strcmp(target, "dvmAllocPrimitiveArray")) ||
+       (!strcmp(target, "dvmInterpHandleFillArrayData")) ||
+       (!strcmp(target, "dvmFindInterfaceMethodInCache")) ||
+       (!strcmp(target, "dvmNcgHandlePackedSwitch")) ||
+       (!strcmp(target, "dvmNcgHandleSparseSwitch")) ||
+       (!strcmp(target, "dvmCanPutArrayElement")) ||
+       (!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3")) ||
+       (!strcmp(target, "execute_inline"))
+       || (!strcmp(target, "dvmJitToPatchPredictedChain"))
+       || (!strcmp(target, "dvmJitHandlePackedSwitch"))
+       || (!strcmp(target, "dvmJitHandleSparseSwitch"))
+       ) {
+        touchEax();
+    }
+
+    //these two functions also use %edx for the return value
+    if((!strcmp(target, "moddi3")) || (!strcmp(target, "divdi3"))) {
+        touchEdx();
+    }
+    if((!strcmp(target, ".new_instance_helper"))) {
+        touchEsi(); touchEax();
+    }
+#if defined(ENABLE_TRACING)
+    if((!strcmp(target, "common_periodicChecks4"))) {
+        touchEdx();
+    }
+#endif
+    if((!strcmp(target, ".const_string_helper"))) {
+        touchEcx(); touchEax();
+    }
+    if((!strcmp(target, ".check_cast_helper"))) {
+        touchEbx(); touchEsi();
+    }
+    if((!strcmp(target, ".instance_of_helper"))) {
+        touchEbx(); touchEsi(); touchEcx();
+    }
+    if((!strcmp(target, ".monitor_enter_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".monitor_exit_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".aget_wide_helper"))) {
+        touchEbx(); touchEcx(); touchXmm1();
+    }
+    if((!strcmp(target, ".aget_helper")) || (!strcmp(target, ".aget_char_helper")) ||
+       (!strcmp(target, ".aget_short_helper")) || (!strcmp(target, ".aget_bool_helper")) ||
+       (!strcmp(target, ".aget_byte_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".aput_helper")) || (!strcmp(target, ".aput_char_helper")) ||
+       (!strcmp(target, ".aput_short_helper")) || (!strcmp(target, ".aput_bool_helper")) ||
+       (!strcmp(target, ".aput_byte_helper")) || (!strcmp(target, ".aput_wide_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".sput_helper")) || (!strcmp(target, ".sput_wide_helper"))) {
+        touchEdx(); touchEax();
+    }
+    if((!strcmp(target, ".sget_helper"))) {
+        touchEdx(); touchEcx();
+    }
+    if((!strcmp(target, ".sget_wide_helper"))) {
+        touchEdx(); touchXmm1();
+    }
+    if((!strcmp(target, ".aput_obj_helper"))) {
+        touchEdx(); touchEcx(); touchEax();
+    }
+    if((!strcmp(target, ".iput_helper")) || (!strcmp(target, ".iput_wide_helper"))) {
+        touchEbx(); touchEcx(); touchEsi();
+    }
+    if((!strcmp(target, ".iget_helper"))) {
+        touchEbx(); touchEcx(); touchEdx();
+    }
+    if((!strcmp(target, ".iget_wide_helper"))) {
+        touchEbx(); touchEcx(); touchXmm1();
+    }
+    if((!strcmp(target, ".new_array_helper"))) {
+        touchEbx(); touchEdx(); touchEax();
+    }
+    if((!strcmp(target, ".invoke_virtual_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_direct_helper"))) {
+        touchEsi(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_super_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invoke_interface_helper"))) {
+        touchEbx(); touchEcx();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_5_helper")) ||
+       (!strcmp(target, ".invokeMethodNoRange_4_helper"))) {
+        touchEbx(); touchEsi(); touchEax(); touchEdx();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_3_helper"))) {
+        touchEbx(); touchEsi(); touchEax();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_2_helper"))) {
+        touchEbx(); touchEsi();
+    }
+    if((!strcmp(target, ".invokeMethodNoRange_1_helper"))) {
+        touchEbx();
+    }
+    if((!strcmp(target, ".invokeMethodRange_helper"))) {
+        touchEdx(); touchEsi();
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("enter beforeCall");
+#endif
+    if(!strncmp(target, ".invokeArgsDone", 15)) resetGlue(PhysicalReg_GLUE_DVMDEX);
+
+    freeReg(true); //to avoid spilling dead logical registers
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        /* before throwing an exception, if GLUE is spilled, load to %ebp
+           this should happen at last */
+        if(k == indexForGlue) continue;
+        if(compileTable[k].physicalReg != PhysicalReg_Null &&
+           (compileTable[k].physicalType & LowOpndRegType_hard) == 0) {
+            /* handles non hardcoded variables that are in physical registers */
+            if(!strcmp(target, "exception")) {
+                /* before throwing an exception
+                   update contents of all VRs in Java stack */
+                if(!isVirtualReg(compileTable[k].physicalType)) continue;
+                /* to have correct GC, we should update contents for L VRs as well */
+                //if(compileTable[k].gType == GLOBALTYPE_L) continue;
+            }
+            if((!strcmp(target, ".const_string_resolve")) ||
+               (!strcmp(target, ".static_field_resolve")) ||
+               (!strcmp(target, ".inst_field_resolve")) ||
+               (!strcmp(target, ".class_resolve")) ||
+               (!strcmp(target, ".direct_method_resolve")) ||
+               (!strcmp(target, ".virtual_method_resolve")) ||
+               (!strcmp(target, ".static_method_resolve"))) {
+               /* physical register %ebx will keep its content
+                  but to have correct GC, we should dump content of a VR
+                     that is mapped to %ebx */
+                if(compileTable[k].physicalReg == PhysicalReg_EBX &&
+                   (!isVirtualReg(compileTable[k].physicalType)))
+                    continue;
+            }
+            if((!strncmp(target, "dvm", 3)) || (!strcmp(target, "moddi3")) ||
+               (!strcmp(target, "divdi3")) ||
+               (!strcmp(target, "fmod")) || (!strcmp(target, "fmodf"))) {
+                /* callee-saved registers (%ebx, %esi, %ebp, %edi) will keep the content
+                   but to have correct GC, we should dump content of a VR
+                      that is mapped to a callee-saved register */
+                if((compileTable[k].physicalReg == PhysicalReg_EBX ||
+                    compileTable[k].physicalReg == PhysicalReg_ESI) &&
+                   (!isVirtualReg(compileTable[k].physicalType)))
+                    continue;
+            }
+#ifdef DEBUG_REGALLOC
+            ALOGI("SPILL logical register %d %d in beforeCall",
+                  compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+            spillLogicalReg(k, true);
+        }
+    }
+    if(indexForGlue >= 0 && !strcmp(target, "exception") &&
+       compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
+        unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
+    }
+#ifdef DEBUG_REGALLOC
+    ALOGI("exit beforeCall");
+#endif
+    return 0;
+}
+int getFreeReg(int type, int reg, int indexToCompileTable);
+//! after calling a helper function or a VM function
+
+//!
+int afterCall(const char* target) { //un-spill
+    if(currentBB == NULL) return -1;
+    if(!strcmp(target, "ncgGetEIP")) return -1;
+
+    return 0;
+}
+//! check whether a temporary is 8-bit
+
+//!
+bool isTemp8Bit(int type, int reg) {
+    if(currentBB == NULL) return false;
+    if(!isTemporary(type, reg)) return false;
+    int k;
+    for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+        if(infoByteCodeTemp[k].physicalType == type &&
+           infoByteCodeTemp[k].regNum == reg) {
+            return infoByteCodeTemp[k].is8Bit;
+        }
+    }
+    ALOGE("isTemp8Bit %d %d", type, reg);
+    return false;
+}
+
+/* functions to access live ranges of a VR
+   Live range info is stored in memVRTable[].ranges, which is a linked list
+*/
+//! check whether a VR is live at the current bytecode
+
+//!
+bool isVRLive(int vA) {
+    int index = searchMemTable(vA);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", vA);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start && offsetPC <= ptr->end) return true;
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+//! check whether the current bytecode is the last access to a VR within a live range
+
+//!for 64-bit VR, return true only when true for both low half and high half
+bool isLastByteCodeOfLiveRange(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    LiveRange* ptr = NULL;
+    if(tSize == OpndSize_32) {
+        /* check live ranges for the VR */
+        index = searchMemTable(compileTable[k].regNum);
+        if(index < 0) {
+            ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+            return false;
+        }
+        ptr = memVRTable[index].ranges;
+        while(ptr != NULL) {
+            if(offsetPC == ptr->end) return true;
+            ptr = ptr->next;
+        }
+        return false;
+    }
+    /* size of the VR is 64 */
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    bool tmpB = false;
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC == ptr->end) {
+            tmpB = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!tmpB) return false;
+    /* check live ranges of the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC == ptr->end) {
+            return true;
+        }
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+//! check whether the current bytecode is in a live range that extends to end of a basic block
+
+//!for 64 bit, return true if true for both low half and high half
+bool reachEndOfBB(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    bool retCode = false;
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            if(ptr->end == currentBB->pc_end) {
+                retCode = true;
+            }
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!retCode) return false;
+    if(tSize == OpndSize_32) return true;
+    /* check live ranges of the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            if(ptr->end == currentBB->pc_end) return true;
+            return false;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
+#endif
+    return false;
+}
+
+//!check whether the current bytecode is the next to last access to a VR within a live range
+
+//!for 64 bit, return true if true for both low half and high half
+bool isNextToLastAccess(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index;
+    /* check live ranges for the low half */
+    bool retCode = false;
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return false;
+    }
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        int num_access = ptr->num_access;
+
+        if(num_access < 2) {
+           ptr = ptr->next;
+           continue;
+        }
+
+        if(offsetPC == ptr->accessPC[num_access-2]) {
+           retCode = true;
+           break;
+        }
+        ptr = ptr->next;
+    }
+    if(!retCode) return false;
+    if(tSize == OpndSize_32) return true;
+    /* check live ranges for the high half */
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return false;
+    }
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        int num_access = ptr->num_access;
+
+        if(num_access < 2) {
+           ptr = ptr->next;
+           continue;
+        }
+
+        if(offsetPC == ptr->accessPC[num_access-2]) return true;
+        ptr = ptr->next;
+    }
+    return false;
+}
+
+/** return the start of the next live range
+    if there does not exist a next live range, return pc_end of the basic block
+    for 64 bits, return the larger one for low half and high half
+    Assume live ranges are sorted in order
+*/
+int getNextLiveRange(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    /* check live ranges of the low half */
+    int index;
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return offsetPC;
+    }
+    bool found = false;
+    int nextUse = offsetPC;
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(ptr->start > offsetPC) {
+            nextUse = ptr->start;
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!found) return currentBB->pc_end;
+    if(tSize == OpndSize_32) return nextUse;
+
+    /* check live ranges of the high half */
+    found = false;
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return offsetPC;
+    }
+    int nextUse2 = offsetPC;
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(ptr->start > offsetPC) {
+            nextUse2 = ptr->start;
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+    if(!found) return currentBB->pc_end;
+    /* return the larger one */
+    return (nextUse2 > nextUse ? nextUse2 : nextUse);
+}
+
+/** return the next access to a variable
+    If variable is 64-bit, get the next access to the lower half and the high half
+        return the eariler one
+*/
+int getNextAccess(int compileIndex) {
+    int k = compileIndex;
+    OpndSize tSize = getRegSize(compileTable[k].physicalType);
+    int index, k3;
+    /* check live ranges of the low half */
+    index = searchMemTable(compileTable[k].regNum);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum);
+        return offsetPC;
+    }
+    bool found = false;
+    int nextUse = offsetPC;
+    LiveRange* ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            /* offsetPC belongs to this live range */
+            for(k3 = 0; k3 < ptr->num_access; k3++) {
+                if(ptr->accessPC[k3] > offsetPC) {
+                    nextUse = ptr->accessPC[k3];
+                    break;
+                }
+            }
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    if(!found)
+        ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum);
+#endif
+    if(tSize == OpndSize_32) return nextUse;
+
+    /* check live ranges of the high half */
+    found = false;
+    index = searchMemTable(compileTable[k].regNum+1);
+    if(index < 0) {
+        ALOGE("couldn't find VR %d in memTable", compileTable[k].regNum+1);
+        return offsetPC;
+    }
+    int nextUse2 = offsetPC;
+    ptr = memVRTable[index].ranges;
+    while(ptr != NULL) {
+        if(offsetPC >= ptr->start &&
+           offsetPC <= ptr->end) {
+            for(k3 = 0; k3 < ptr->num_access; k3++) {
+                if(ptr->accessPC[k3] > offsetPC) {
+                    nextUse2 = ptr->accessPC[k3];
+                    break;
+                }
+            }
+            found = true;
+            break;
+        }
+        ptr = ptr->next;
+    }
+#ifdef PRINT_WARNING
+    if(!found) ALOGW("offsetPC %d not in live range of VR %d", offsetPC, compileTable[k].regNum+1);
+#endif
+    /* return the earlier one */
+    if(nextUse2 < nextUse) return nextUse2;
+    return nextUse;
+}
+
+/** free variables that are no longer in use
+    free a temporary with reference count of zero
+    will dump content of a GL VR to memory if necessary
+*/
+int freeReg(bool spillGL) {
+    if(currentBB == NULL) return 0;
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].refCount == 0 && compileTable[k].physicalReg != PhysicalReg_Null) {
+            /* check entries with reference count of zero and is mapped to a physical register */
+            bool typeA = !isVirtualReg(compileTable[k].physicalType);
+            bool freeCrit = true, delayFreeing = false;
+            bool typeC = false, typeB = false, reachEnd = false;
+            if(isVirtualReg(compileTable[k].physicalType)) {
+                /* VRs in the compile table */
+
+                /* Check if delay for freeing was requested for this VR */
+                delayFreeing = getVRFreeDelayRequested(compileTable[k].regNum);
+
+                freeCrit = isLastByteCodeOfLiveRange(k); /* last bytecode of a live range */
+                reachEnd = reachEndOfBB(k); /* in a live range that extends to end of a basic block */
+#ifdef DEBUG_LIVE_RANGE
+                ALOGI("IN freeReg: VR %d offsetPC %x freecrit %d reachEnd %d nextToLast %d", compileTable[k].regNum, offsetPC, freeCrit, reachEnd, isNextToLastAccess(k));
+#endif
+                /* Bug: spilling of VRs after edi(rFP) is updated in RETURN bytecode
+                        will cause variables for callee to be spilled to the caller stack frame and
+                                                        to overwrite varaibles for caller
+                */
+                /* last bytecode of a live range reaching end of BB if not counting the fake usage at end */
+                bool boolB = reachEnd && isNextToLastAccess(k);
+                /* Bug: when a GG VR is checked at end of a basic block,
+                        freeCrit will be true and physicalReg will be set to Null
+                   Fix: change free condition from freeCrit to (freeCrit && offsetPC != currentBB->pc_end)
+                */
+                /* conditions to free a GG VR:
+                       last bytecode of a live range reaching end of BB if not counting the fake usage at end && endsWithReturn
+                       or
+                       last bytecode of a live range && offsetPC != currentBB->pc_end
+                           -> last bytecode of a live range not reaching end
+                */
+                typeC = ((freeCrit && offsetPC != currentBB->pc_end) ||
+                         (currentBB->endsWithReturn && boolB)) &&
+                        compileTable[k].gType == GLOBALTYPE_GG &&
+                        !delayFreeing;
+                /* conditions to free a L|GL VR:
+                       last bytecode of a live range
+                       or
+                       last bytecode of a live range reaching end of BB if not counting the fake usage at end
+                */
+                typeB = (freeCrit || boolB) &&
+                        (compileTable[k].gType != GLOBALTYPE_GG) &&
+                        !delayFreeing;
+            }
+            if(typeA || typeB || typeC) {
+#ifdef DEBUG_REGALLOC
+                if(typeA)
+                    ALOGI("FREE TEMP %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+                else if(typeB)
+                    ALOGI("FREE VR L|GL %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+                else if(typeC)
+                    ALOGI("FREE VR GG %d with type %d allocated to %d",
+                           compileTable[k].regNum, compileTable[k].physicalType,
+                           compileTable[k].physicalReg);
+#endif
+                bool dumpGL = false;
+                if(compileTable[k].gType == GLOBALTYPE_GL && !reachEnd) {
+                    /* if the live range does not reach end of basic block
+                       and there exists a try block from offsetPC to the next live range
+                           dump VR to interpreted stack */
+                    int tmpPC = getNextLiveRange(k);
+                    if(existATryBlock(currentMethod, offsetPC, tmpPC)) dumpGL = true;
+                }
+                /* if the live range reach end of basic block, dump VR to interpreted stack */
+                if(compileTable[k].gType == GLOBALTYPE_GL && reachEnd) dumpGL = true;
+                if(dumpGL) {
+                    if(spillGL) {
+#ifdef DEBUG_REGALLOC
+                        ALOGI("SPILL VR GL %d %d", compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+                        spillLogicalReg(k, true); //will dump VR to memory & update physicalReg
+                    }
+                }
+                else
+                     compileTable[k].physicalReg = PhysicalReg_Null;
+            }
+            if(typeA) {
+                if(compileTable[k].spill_loc_index >= 0) {
+                    /* update spill info for temporaries */
+                    spillIndexUsed[compileTable[k].spill_loc_index >> 2] = 0;
+                    compileTable[k].spill_loc_index = -1;
+                    ALOGE("free a temporary register with TRSTATE_SPILLED");
+                }
+            }
+        }
+    }
+    syncAllRegs(); //sync up allRegs (isUsed & freeTimeStamp) with compileTable
+    return 0;
+}
+
+//! reduce the reference count by 1
+
+//! input: index to compileTable
+void decreaseRefCount(int index) {
+#ifdef DEBUG_REFCOUNT
+    ALOGI("REFCOUNT: %d in decreaseRefCount %d %d", compileTable[index].refCount,
+            compileTable[index].regNum, compileTable[index].physicalType);
+#endif
+    compileTable[index].refCount--;
+    if(compileTable[index].refCount < 0) {
+        ALOGE("refCount is negative for REG %d %d", compileTable[index].regNum, compileTable[index].physicalType);
+        dvmAbort();
+    }
+}
+//! reduce the reference count of a VR by 1
+
+//! input: reg & type
+int updateRefCount(int reg, LowOpndRegType type) {
+    if(currentBB == NULL) return 0;
+    int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in updateRefCount", reg, type);
+        return -1;
+    }
+    decreaseRefCount(index);
+    return 0;
+}
+//! reduce the reference count of a variable by 1
+
+//! The variable is named with lowering module's naming mechanism
+int updateRefCount2(int reg, int type, bool isPhysical) {
+    if(currentBB == NULL) return 0;
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("reg %d type %d not found in updateRefCount", reg, newType);
+        return -1;
+    }
+    decreaseRefCount(index);
+    return 0;
+}
+//! check whether a glue variable is in physical register or spilled
+
+//!
+bool isGlueHandled(int glue_reg) {
+    if(currentBB == NULL) return false;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in isGlueHandled", glue_reg);
+        return -1;
+    }
+    if(compileTable[index].spill_loc_index >= 0 ||
+       compileTable[index].physicalReg != PhysicalReg_Null) {
+#ifdef DEBUG_GLUE
+        ALOGI("GLUE isGlueHandled for %d returns true", glue_reg);
+#endif
+        return true;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("GLUE isGlueHandled for %d returns false", glue_reg);
+#endif
+    return false;
+}
+//! reset the state of a glue variable to not existant (not in physical register nor spilled)
+
+//!
+void resetGlue(int glue_reg) {
+    if(currentBB == NULL) return;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in resetGlue", glue_reg);
+        return;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("GLUE reset for %d", glue_reg);
+#endif
+    compileTable[index].physicalReg = PhysicalReg_Null;
+    if(compileTable[index].spill_loc_index >= 0)
+        spillIndexUsed[compileTable[index].spill_loc_index >> 2] = 0;
+    compileTable[index].spill_loc_index = -1;
+}
+//! set a glue variable in a physical register allocated for a variable
+
+//! Variable is using lowering module's naming convention
+void updateGlue(int reg, bool isPhysical, int glue_reg) {
+    if(currentBB == NULL) return;
+    int index = searchCompileTable(LowOpndRegType_gp, glue_reg);
+    if(index < 0) {
+        ALOGE("glue reg %d not found in updateGlue", glue_reg);
+        return;
+    }
+    /* find the compileTable entry for variable <reg, isPhysical> */
+    int newType = convertType(LowOpndRegType_gp, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index2 = searchCompileTable(newType, reg);
+    if(index2 < 0 || compileTable[index2].physicalReg == PhysicalReg_Null) {
+        ALOGE("updateGlue reg %d type %d", reg, newType);
+        return;
+    }
+#ifdef DEBUG_GLUE
+    ALOGI("physical register for GLUE %d set to %d", glue_reg, compileTable[index2].physicalReg);
+#endif
+    compileTable[index].physicalReg = compileTable[index2].physicalReg;
+    compileTable[index].spill_loc_index = -1;
+}
+
+//! check whether a virtual register is in a physical register
+
+//! If updateRefCount is 0, do not update reference count;
+//!If updateRefCount is 1, update reference count only when VR is in a physical register
+//!If updateRefCount is 2, update reference count
+int checkVirtualReg(int reg, LowOpndRegType type, int updateRefCount) {
+    if(currentBB == NULL) return PhysicalReg_Null;
+    int index = searchCompileTable(LowOpndRegType_virtual | type, reg);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in checkVirtualReg", reg, type);
+        return PhysicalReg_Null;
+    }
+    //reduce reference count
+    if(compileTable[index].physicalReg != PhysicalReg_Null) {
+        if(updateRefCount != 0) decreaseRefCount(index);
+        return compileTable[index].physicalReg;
+    }
+    if(updateRefCount == 2) decreaseRefCount(index);
+    return PhysicalReg_Null;
+}
+//!check whether a temporary can share the same physical register with a VR
+
+//!This is called in get_virtual_reg
+//!If this function returns false, new register will be allocated for this temporary
+bool checkTempReg2(int reg, int type, bool isPhysical, int physicalRegForVR) {
+    if(currentBB == NULL) return false;
+    if(isPhysical) return false;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int k;
+    for(k = 0; k < num_temp_regs_per_bytecode; k++) {
+        if(infoByteCodeTemp[k].physicalType == newType &&
+           infoByteCodeTemp[k].regNum == reg) {
+#ifdef DEBUG_MOVE_OPT
+            ALOGI("MOVE_OPT checkTempRegs for %d %d returns %d %d",
+                   reg, newType, infoByteCodeTemp[k].shareWithVR, infoByteCodeTemp[k].is8Bit);
+#endif
+            if(!infoByteCodeTemp[k].is8Bit) return infoByteCodeTemp[k].shareWithVR;
+            //is8Bit true for gp type only
+            if(!infoByteCodeTemp[k].shareWithVR) return false;
+            //both true
+            if(physicalRegForVR >= PhysicalReg_EAX && physicalRegForVR <= PhysicalReg_EDX) return true;
+#ifdef DEBUG_MOVE_OPT
+            ALOGI("MOVE_OPT registerAllocMove not used for 8-bit register");
+#endif
+            return false;
+        }
+    }
+    ALOGE("checkTempReg2 %d %d", reg, newType);
+    return false;
+}
+//!check whether a temporary can share the same physical register with a VR
+
+//!This is called in set_virtual_reg
+int checkTempReg(int reg, int type, bool isPhysical, int vrNum) {
+    if(currentBB == NULL) return PhysicalReg_Null;
+
+    int newType = convertType(type, reg, isPhysical);
+    if(newType & LowOpndRegType_scratch) reg = reg - PhysicalReg_SCRATCH_1 + 1;
+    int index = searchCompileTable(newType, reg);
+    if(index < 0) {
+        ALOGE("temp reg %d type %d not found in checkTempReg", reg, newType);
+        return PhysicalReg_Null;
+    }
+
+    //a temporary register can share the same physical reg with a VR if registerAllocMove is called
+    //this will cause problem with move bytecode
+    //get_VR(v1, t1) t1 and v1 point to the same physical reg
+    //set_VR(t1, v2) t1 and v2 point to the same physical reg
+    //this will cause v1 and v2 point to the same physical reg
+    //FIX: if this temp reg shares a physical reg with another reg
+    if(compileTable[index].physicalReg != PhysicalReg_Null) {
+        int k;
+        for(k = 0; k < num_compile_entries; k++) {
+            if(k == index) continue;
+            if(compileTable[k].physicalReg == compileTable[index].physicalReg) {
+                return PhysicalReg_Null; //will allocate a register for VR
+            }
+        }
+        decreaseRefCount(index);
+        return compileTable[index].physicalReg;
+    }
+    if(compileTable[index].spill_loc_index >= 0) {
+        //registerAlloc will call unspillLogicalReg (load from memory)
+#ifdef DEBUG_REGALLOC
+        ALOGW("in checkTempReg, the temporary register %d %d was spilled", reg, type);
+#endif
+        int regAll = registerAlloc(type, reg, isPhysical, true/* updateRefCount */);
+        return regAll;
+    }
+    return PhysicalReg_Null;
+}
+//!check whether a variable has exposed usage in a basic block
+
+//!It calls hasExposedUsage2
+bool hasExposedUsage(LowOpndRegType type, int regNum, BasicBlock_O1* bb) {
+    int index = searchVirtualInfoOfBB(type, regNum, bb);
+    if(index >= 0 && hasExposedUsage2(bb, index)) {
+        return true;
+    }
+    return false;
+}
+//!check whether a variable has exposed usage in other basic blocks
+
+//!
+bool hasOtherExposedUsage(OpndSize size, int regNum, BasicBlock_O1* bb) {
+    return true; //assume the worst case
+}
+
+//! handles constant VRs at end of a basic block
+
+//!If a VR is constant at end of a basic block and (it has exposed usage in other basic blocks or reaches a GG VR), dump immediate to memory
+void constVREndOfBB() {
+    BasicBlock_O1* bb = currentBB;
+    int k, k2;
+    //go through GG VRs, update a bool array
+    int constUsedByGG[MAX_CONST_REG];
+    for(k = 0; k < num_const_vr; k++)
+        constUsedByGG[k] = 0;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType) && compileTable[k].gType == GLOBALTYPE_GG) {
+            OpndSize size = getRegSize(compileTable[k].physicalType);
+            int regNum = compileTable[k].regNum;
+            int indexL = -1;
+            int indexH = -1;
+            for(k2 = 0; k2 < num_const_vr; k2++) {
+                if(constVRTable[k2].regNum == regNum) {
+                    indexL = k2;
+                    continue;
+                }
+                if(constVRTable[k2].regNum == regNum + 1 && size == OpndSize_64) {
+                    indexH = k2;
+                    continue;
+                }
+            }
+            if(indexL >= 0) constUsedByGG[indexL] = 1;
+            if(indexH >= 0) constUsedByGG[indexH] = 1;
+        } //GG VR
+    }
+    for(k = 0; k < num_const_vr; k++) {
+        if(!constVRTable[k].isConst) continue;
+        bool hasExp = false;
+        if(constUsedByGG[k] == 0)
+            hasExp = hasOtherExposedUsage(OpndSize_32, constVRTable[k].regNum, bb);
+        if(constUsedByGG[k] != 0 || hasExp) {
+            dumpImmToMem(constVRTable[k].regNum, OpndSize_32, constVRTable[k].value);
+            setVRToMemory(constVRTable[k].regNum, OpndSize_32);
+#ifdef DEBUG_ENDOFBB
+            ALOGI("ENDOFBB: exposed VR %d is const %d (%x)",
+                  constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
+#endif
+        } else {
+#ifdef DEBUG_ENDOFBB
+            ALOGI("ENDOFBB: unexposed VR %d is const %d (%x)",
+                  constVRTable[k].regNum, constVRTable[k].value, constVRTable[k].value);
+#endif
+        }
+    }
+}
+
+//!handles GG VRs at end of a basic block
+
+//!make sure all GG VRs are in pre-defined physical registers
+void globalVREndOfBB(const Method* method) {
+    //fix: freeReg first to write LL VR back to memory to avoid it gets overwritten by GG VRs
+    freeReg(true);
+    int k;
+    //spill GG VR first if it is not mapped to the specific reg
+    //release GLUE regs
+    for(k = 0; k < num_compile_entries; k++) {
+        if(compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+           compileTable[k].regNum != PhysicalReg_GLUE) {
+            compileTable[k].physicalReg = PhysicalReg_Null;
+            compileTable[k].spill_loc_index = -1;
+        }
+        //if part of a GG VR is const, the physical reg is set to null
+        if(isVirtualReg(compileTable[k].physicalType) &&
+           compileTable[k].gType == GLOBALTYPE_GG && compileTable[k].physicalReg != PhysicalReg_Null &&
+           compileTable[k].physicalReg != compileTable[k].physicalReg_prev) {
+#ifdef DEBUG_ENDOFBB
+            ALOGW("end of BB GG VR is not mapped to the specific reg: %d %d %d",
+                  compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg);
+            ALOGW("ENDOFBB SPILL VR %d %d", compileTable[k].regNum, compileTable[k].physicalType);
+#endif
+            spillLogicalReg(k, true); //the next section will load VR from memory to the specific reg
+        }
+    }
+    syncAllRegs();
+    for(k = 0; k < num_compile_entries; k++) {
+        if(isVirtualReg(compileTable[k].physicalType)) {
+            if(compileTable[k].gType == GLOBALTYPE_GG &&
+               compileTable[k].physicalReg == PhysicalReg_Null && (!currentBB->endsWithReturn)) {
+#ifdef DEBUG_ENDOFBB
+                ALOGI("ENDOFBB GET GG VR %d %d to physical register %d", compileTable[k].regNum,
+                      compileTable[k].physicalType, compileTable[k].physicalReg_prev);
+#endif
+                compileTable[k].physicalReg = compileTable[k].physicalReg_prev;
+                if(allRegs[compileTable[k].physicalReg_prev].isUsed) {
+                    ALOGE("physical register for GG VR is still used");
+                }
+                get_virtual_reg_noalloc(compileTable[k].regNum,
+                                        getRegSize(compileTable[k].physicalType),
+                                        compileTable[k].physicalReg_prev,
+                                        true);
+            }
+        }//not const
+    }
+    if(indexForGlue >= 0 &&
+        compileTable[indexForGlue].physicalReg == PhysicalReg_Null) {
+        unspillLogicalReg(indexForGlue, PhysicalReg_EBP); //load %ebp
+    }
+}
+
+//! get ready for the next version of a hard-coded register
+
+//!set its physicalReg to Null and update its reference count
+int nextVersionOfHardReg(PhysicalReg pReg, int refCount) {
+    int indexT = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_hard, pReg);
+    if(indexT < 0)
+        return -1;
+    compileTable[indexT].physicalReg = PhysicalReg_Null;
+#ifdef DEBUG_REFCOUNT
+    ALOGI("REFCOUNT: to %d in nextVersionOfHardReg %d", refCount, pReg);
+#endif
+    compileTable[indexT].refCount = refCount;
+    return 0;
+}
+
+/** update compileTable with bb->infoBasicBlock[k]
+*/
+void insertFromVirtualInfo(BasicBlock_O1* bb, int k) {
+    int index = searchCompileTable(LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType, bb->infoBasicBlock[k].regNum);
+    if(index < 0) {
+        /* the virtual register is not in compileTable, insert it */
+        index = num_compile_entries;
+        compileTable[num_compile_entries].physicalType = (LowOpndRegType_virtual | bb->infoBasicBlock[k].physicalType);
+        compileTable[num_compile_entries].regNum = bb->infoBasicBlock[k].regNum;
+        compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
+        compileTable[num_compile_entries].bb = bb;
+        compileTable[num_compile_entries].indexToInfoBB = k;
+        compileTable[num_compile_entries].spill_loc_index = -1;
+        compileTable[num_compile_entries].gType = bb->infoBasicBlock[k].gType;
+        num_compile_entries++;
+        if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+            ALOGE("compileTable overflow");
+            dvmAbort();
+        }
+    }
+    /* re-set reference count of all VRs */
+    compileTable[index].refCount = bb->infoBasicBlock[k].refCount;
+    compileTable[index].accessType = bb->infoBasicBlock[k].accessType;
+    if(compileTable[index].gType == GLOBALTYPE_GG)
+        compileTable[index].physicalReg_prev = bb->infoBasicBlock[k].physicalReg_GG;
+}
+
+/** update compileTable with infoByteCodeTemp[k]
+*/
+void insertFromTempInfo(int k) {
+    int index = searchCompileTable(infoByteCodeTemp[k].physicalType, infoByteCodeTemp[k].regNum);
+    if(index < 0) {
+        /* the temporary is not in compileTable, insert it */
+        index = num_compile_entries;
+        compileTable[num_compile_entries].physicalType = infoByteCodeTemp[k].physicalType;
+        compileTable[num_compile_entries].regNum = infoByteCodeTemp[k].regNum;
+        num_compile_entries++;
+        if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+            ALOGE("compileTable overflow");
+            dvmAbort();
+        }
+    }
+    compileTable[index].physicalReg = PhysicalReg_Null;
+    compileTable[index].refCount = infoByteCodeTemp[k].refCount;
+    compileTable[index].linkageToVR = infoByteCodeTemp[k].linkageToVR;
+    compileTable[index].gType = GLOBALTYPE_L;
+    compileTable[index].spill_loc_index = -1;
+}
+
+/* insert a glue-related register GLUE_DVMDEX to compileTable */
+void insertGlueReg() {
+    compileTable[num_compile_entries].physicalType = LowOpndRegType_gp;
+    compileTable[num_compile_entries].regNum = PhysicalReg_GLUE_DVMDEX;
+    compileTable[num_compile_entries].refCount = 2;
+    compileTable[num_compile_entries].physicalReg = PhysicalReg_Null;
+    compileTable[num_compile_entries].bb = NULL;
+    compileTable[num_compile_entries].spill_loc_index = -1;
+    compileTable[num_compile_entries].accessType = REGACCESS_N;
+    compileTable[num_compile_entries].linkageToVR = -1;
+    compileTable[num_compile_entries].gType = GLOBALTYPE_L;
+
+    num_compile_entries++;
+    if(num_compile_entries >= COMPILE_TABLE_SIZE) {
+        ALOGE("compileTable overflow");
+        dvmAbort();
+    }
+}
+
+/** print infoBasicBlock of the given basic block
+*/
+void dumpVirtualInfoOfBasicBlock(BasicBlock_O1* bb) {
+    int jj;
+    ALOGI("Virtual Info for BB%d --------", bb->bb_index);
+    for(jj = 0; jj < bb->num_regs; jj++) {
+        ALOGI("regNum %d physicalType %d accessType %d refCount %d def ",
+               bb->infoBasicBlock[jj].regNum, bb->infoBasicBlock[jj].physicalType,
+               bb->infoBasicBlock[jj].accessType, bb->infoBasicBlock[jj].refCount);
+        int k;
+        for(k = 0; k < bb->infoBasicBlock[jj].num_reaching_defs; k++)
+            ALOGI("[%x %d %d %d] ", bb->infoBasicBlock[jj].reachingDefs[k].offsetPC,
+                   bb->infoBasicBlock[jj].reachingDefs[k].regNum,
+                   bb->infoBasicBlock[jj].reachingDefs[k].physicalType,
+                   bb->infoBasicBlock[jj].reachingDefs[k].accessType);
+        ALOGI("");
+    }
+}
+
+/** print compileTable
+*/
+void dumpCompileTable() {
+    int jj;
+    ALOGI("Compile Table for method ----------");
+    for(jj = 0; jj < num_compile_entries; jj++) {
+        ALOGI("regNum %d physicalType %d refCount %d isConst %d physicalReg %d type %d",
+               compileTable[jj].regNum, compileTable[jj].physicalType,
+               compileTable[jj].refCount, compileTable[jj].isConst, compileTable[jj].physicalReg, compileTable[jj].gType);
+    }
+}
+
+//!check whether a basic block is the start of an exception handler
+
+//!
+bool isFirstOfHandler(BasicBlock_O1* bb) {
+    int i;
+    for(i = 0; i < num_exception_handlers; i++) {
+        if(bb->pc_start == exceptionHandlers[i]) return true;
+    }
+    return false;
+}
+
+//! create a basic block that starts at src_pc and ends at end_pc
+
+//!
+BasicBlock_O1* createBasicBlock(int src_pc, int end_pc) {
+    BasicBlock_O1* bb = (BasicBlock_O1*)malloc(sizeof(BasicBlock_O1));
+    if(bb == NULL) {
+        ALOGE("out of memory");
+        return NULL;
+    }
+    bb->pc_start = src_pc;
+    bb->bb_index = num_bbs_for_method;
+    if(bb_entry == NULL) bb_entry = bb;
+
+    /* insert the basic block to method_bbs_sorted in ascending order of pc_start */
+    int k;
+    int index = -1;
+    for(k = 0; k < num_bbs_for_method; k++)
+        if(method_bbs_sorted[k]->pc_start > src_pc) {
+            index = k;
+            break;
+        }
+    if(index == -1)
+        method_bbs_sorted[num_bbs_for_method] = bb;
+    else {
+        /* push the elements from index by 1 */
+        for(k = num_bbs_for_method-1; k >= index; k--)
+            method_bbs_sorted[k+1] = method_bbs_sorted[k];
+        method_bbs_sorted[index] = bb;
+    }
+    num_bbs_for_method++;
+    if(num_bbs_for_method >= MAX_NUM_BBS_PER_METHOD) {
+        ALOGE("too many basic blocks");
+        dvmAbort();
+    }
+    return bb;
+}
+
+/* BEGIN code to handle state transfers */
+//! save the current state of register allocator to a state table
+
+//!
+void rememberState(int stateNum) {
+#ifdef DEBUG_STATE
+    ALOGI("STATE: remember state %d", stateNum);
+#endif
+    int k;
+    for(k = 0; k < num_compile_entries; k++) {
+        if(stateNum == 1) {
+            stateTable1_1[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_1[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            stateTable1_2[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_2[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            stateTable1_3[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_3[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            stateTable1_4[k].physicalReg = compileTable[k].physicalReg;
+            stateTable1_4[k].spill_loc_index = compileTable[k].spill_loc_index;
+        }
+        else ALOGE("state table overflow");
+#ifdef DEBUG_STATE
+        ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
+               compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
+               compileTable[k].spill_loc_index, compileTable[k].refCount);
+#endif
+    }
+    for(k = 0; k < num_memory_vr; k++) {
+        if(stateNum == 1) {
+            stateTable2_1[k].regNum = memVRTable[k].regNum;
+            stateTable2_1[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            stateTable2_2[k].regNum = memVRTable[k].regNum;
+            stateTable2_2[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            stateTable2_3[k].regNum = memVRTable[k].regNum;
+            stateTable2_3[k].inMemory = memVRTable[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            stateTable2_4[k].regNum = memVRTable[k].regNum;
+            stateTable2_4[k].inMemory = memVRTable[k].inMemory;
+        }
+        else ALOGE("state table overflow");
+#ifdef DEBUG_STATE
+        ALOGI("virtual reg %d in memory %d", memVRTable[k].regNum, memVRTable[k].inMemory);
+#endif
+    }
+}
+
+//!update current state of register allocator with a state table
+
+//!
+void goToState(int stateNum) {
+    int k;
+#ifdef DEBUG_STATE
+    ALOGI("STATE: go to state %d", stateNum);
+#endif
+    for(k = 0; k < num_compile_entries; k++) {
+        if(stateNum == 1) {
+            compileTable[k].physicalReg = stateTable1_1[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_1[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            compileTable[k].physicalReg = stateTable1_2[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_2[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            compileTable[k].physicalReg = stateTable1_3[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_3[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            compileTable[k].physicalReg = stateTable1_4[k].physicalReg;
+            compileTable[k].spill_loc_index = stateTable1_4[k].spill_loc_index;
+        }
+        else ALOGE("state table overflow");
+    }
+    updateSpillIndexUsed();
+    syncAllRegs(); //to sync up allRegs CAN'T call freeReg here
+    //since it will change the state!!!
+    for(k = 0; k < num_memory_vr; k++) {
+        if(stateNum == 1) {
+            memVRTable[k].regNum = stateTable2_1[k].regNum;
+            memVRTable[k].inMemory = stateTable2_1[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            memVRTable[k].regNum = stateTable2_2[k].regNum;
+            memVRTable[k].inMemory = stateTable2_2[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            memVRTable[k].regNum = stateTable2_3[k].regNum;
+            memVRTable[k].inMemory = stateTable2_3[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            memVRTable[k].regNum = stateTable2_4[k].regNum;
+            memVRTable[k].inMemory = stateTable2_4[k].inMemory;
+        }
+        else ALOGE("state table overflow");
+    }
+}
+typedef struct TransferOrder {
+    int targetReg;
+    int targetSpill;
+    int compileIndex;
+} TransferOrder;
+#define MAX_NUM_DEST 20
+//! a source register is used as a source in transfer
+//! it can have a maximum of MAX_NUM_DEST destinations
+typedef struct SourceReg {
+    int physicalReg;
+    int num_dests; //check bound
+    TransferOrder dsts[MAX_NUM_DEST];
+} SourceReg;
+int num_src_regs = 0; //check bound
+//! physical registers that are used as a source in transfer
+//! we allow a maximum of MAX_NUM_DEST sources in a transfer
+SourceReg srcRegs[MAX_NUM_DEST];
+//! tell us whether a source register is handled already
+bool handledSrc[MAX_NUM_DEST];
+//! in what order should the source registers be handled
+int handledOrder[MAX_NUM_DEST];
+//! insert a source register with a single destination
+
+//!
+void insertSrcReg(int srcPhysical, int targetReg, int targetSpill, int index) {
+    int k = 0;
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == srcPhysical) { //increase num_dests
+            if(srcRegs[k].num_dests >= MAX_NUM_DEST) {
+                ALOGE("exceed number dst regs for a source reg");
+                dvmAbort();
+            }
+            srcRegs[k].dsts[srcRegs[k].num_dests].targetReg = targetReg;
+            srcRegs[k].dsts[srcRegs[k].num_dests].targetSpill = targetSpill;
+            srcRegs[k].dsts[srcRegs[k].num_dests].compileIndex = index;
+            srcRegs[k].num_dests++;
+            return;
+        }
+    }
+    if(num_src_regs >= MAX_NUM_DEST) {
+        ALOGE("exceed number of source regs");
+        dvmAbort();
+    }
+    srcRegs[num_src_regs].physicalReg = srcPhysical;
+    srcRegs[num_src_regs].num_dests = 1;
+    srcRegs[num_src_regs].dsts[0].targetReg = targetReg;
+    srcRegs[num_src_regs].dsts[0].targetSpill = targetSpill;
+    srcRegs[num_src_regs].dsts[0].compileIndex = index;
+    num_src_regs++;
+}
+//! check whether a register is a source and the source is not yet handled
+
+//!
+bool dstStillInUse(int dstReg) {
+    if(dstReg == PhysicalReg_Null) return false;
+    int k;
+    int index = -1;
+    for(k = 0; k < num_src_regs; k++) {
+        if(dstReg == srcRegs[k].physicalReg) {
+            index = k;
+            break;
+        }
+    }
+    if(index < 0) return false; //not in use
+    if(handledSrc[index]) return false; //not in use
+    return true;
+}
+//! reset the state of glue variables in a state table
+
+//!
+void resetStateOfGlue(int stateNum, int k) {
+#ifdef DEBUG_STATE
+    ALOGI("resetStateOfGlue state %d regNum %d", stateNum, compileTable[k].regNum);
+#endif
+    if(stateNum == 1) {
+        stateTable1_1[k].physicalReg = PhysicalReg_Null;
+        stateTable1_1[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 2) {
+        stateTable1_2[k].physicalReg = PhysicalReg_Null;
+        stateTable1_2[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 3) {
+        stateTable1_3[k].physicalReg = PhysicalReg_Null;
+        stateTable1_3[k].spill_loc_index = -1;
+    }
+    else if(stateNum == 4) {
+        stateTable1_4[k].physicalReg = PhysicalReg_Null;
+        stateTable1_4[k].spill_loc_index = -1;
+    }
+}
+//! construct a legal order of the source registers in this transfer
+
+//!
+void constructSrcRegs(int stateNum) {
+    int k;
+    num_src_regs = 0;
+#ifdef DEBUG_STATE
+    ALOGI("IN constructSrcRegs");
+#endif
+
+    for(k = 0; k < num_compile_entries; k++) {
+#ifdef DEBUG_STATE
+        ALOGI("logical reg %d %d mapped to physical reg %d with spill index %d refCount %d",
+               compileTable[k].regNum, compileTable[k].physicalType, compileTable[k].physicalReg,
+               compileTable[k].spill_loc_index, compileTable[k].refCount);
+#endif
+
+        int pType = compileTable[k].physicalType;
+        //ignore hardcoded logical registers
+        if((pType & LowOpndRegType_hard) != 0) continue;
+        //ignore type _fs
+        if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs) continue;
+        if((pType & MASK_FOR_TYPE) == LowOpndRegType_fs_s) continue;
+
+        //GL VR refCount is zero, can't ignore
+        //L VR refCount is zero, ignore
+        //GG VR refCount is zero, can't ignore
+        //temporary refCount is zero, ignore
+
+        //for GLUE variables, if they do not exist, reset the entries in state table
+        if(compileTable[k].physicalReg == PhysicalReg_Null &&
+           compileTable[k].regNum >= PhysicalReg_GLUE_DVMDEX &&
+           compileTable[k].regNum != PhysicalReg_GLUE &&
+           compileTable[k].spill_loc_index < 0) {
+            resetStateOfGlue(stateNum, k);
+        }
+
+        /* get the target state */
+        int targetReg = PhysicalReg_Null;
+        int targetSpill = -1;
+        if(stateNum == 1) {
+            targetReg = stateTable1_1[k].physicalReg;
+            targetSpill = stateTable1_1[k].spill_loc_index;
+        }
+        else if(stateNum == 2) {
+            targetReg = stateTable1_2[k].physicalReg;
+            targetSpill = stateTable1_2[k].spill_loc_index;
+        }
+        else if(stateNum == 3) {
+            targetReg = stateTable1_3[k].physicalReg;
+            targetSpill = stateTable1_3[k].spill_loc_index;
+        }
+        else if(stateNum == 4) {
+            targetReg = stateTable1_4[k].physicalReg;
+            targetSpill = stateTable1_4[k].spill_loc_index;
+        }
+
+        /* there exists an ordering problem
+           for example:
+             for a VR, move from memory to a physical reg esi
+             for a temporary regsiter, from esi to ecx
+             if we handle VR first, content of the temporary reg. will be overwritten
+           there are 4 cases:
+             I: a variable is currently in memory and its target is in physical reg
+             II: a variable is currently in a register and its target is in memory
+             III: a variable is currently in a different register
+             IV: a variable is currently in a different memory location (for non-VRs)
+           for GLUE, since it can only be allocated to %ebp, we don't have case III
+           For now, case IV is not handled since it didn't show
+        */
+        if(compileTable[k].physicalReg != targetReg &&
+           isVirtualReg(compileTable[k].physicalType)) {
+            /* handles VR for case I to III */
+
+            if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles VR for case I:
+                   insert a xfer order from PhysicalReg_Null to targetReg */
+                insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
+#ifdef DEBUG_STATE
+                ALOGI("insert for VR Null %d %d %d", targetReg, targetSpill, k);
+#endif
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles VR for case III
+                   insert a xfer order from srcReg to targetReg */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                /* handles VR for case II
+                   insert a xfer order from srcReg to memory */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+        }
+
+        if(compileTable[k].physicalReg != targetReg &&
+           !isVirtualReg(compileTable[k].physicalType)) {
+            /* handles non-VR for case I to III */
+
+            if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles non-VR for case I */
+                if(compileTable[k].spill_loc_index < 0) {
+                    /* this variable is freed, no need to transfer */
+#ifdef DEBUG_STATE
+                    ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
+#endif
+                } else {
+                    /* insert a xfer order from memory to targetReg */
+#ifdef DEBUG_STATE
+                    ALOGI("insert Null %d %d %d", targetReg, targetSpill, k);
+#endif
+                    insertSrcReg(PhysicalReg_Null, targetReg, targetSpill, k);
+                }
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                /* handles non-VR for case III
+                   insert a xfer order from srcReg to targetReg */
+                insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+            }
+
+            if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                /* handles non-VR for case II */
+                if(targetSpill < 0) {
+                    /* this variable is freed, no need to transfer */
+#ifdef DEBUG_STATE
+                    ALOGW("in transferToState spill_loc_index is negative for temporary %d", compileTable[k].regNum);
+#endif
+                } else {
+                    /* insert a xfer order from srcReg to memory */
+                    insertSrcReg(compileTable[k].physicalReg, targetReg, targetSpill, k);
+                }
+            }
+
+        }
+    }//for compile entries
+
+    int k2;
+#ifdef DEBUG_STATE
+    for(k = 0; k < num_src_regs; k++) {
+        ALOGI("SRCREG %d: ", srcRegs[k].physicalReg);
+        for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
+            int index = srcRegs[k].dsts[k2].compileIndex;
+            ALOGI("[%d %d %d: %d %d %d] ", srcRegs[k].dsts[k2].targetReg,
+                   srcRegs[k].dsts[k2].targetSpill, srcRegs[k].dsts[k2].compileIndex,
+                   compileTable[index].regNum, compileTable[index].physicalType,
+                   compileTable[index].spill_loc_index);
+        }
+        ALOGI("");
+    }
+#endif
+
+    /* construct an order: xfers from srcReg first, then xfers from memory */
+    int num_handled = 0;
+    int num_in_order = 0;
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == PhysicalReg_Null) {
+            handledSrc[k] = true;
+            num_handled++;
+        } else {
+            handledSrc[k] = false;
+        }
+    }
+    while(num_handled < num_src_regs) {
+        int prev_handled = num_handled;
+        for(k = 0; k < num_src_regs; k++) {
+            if(handledSrc[k]) continue;
+            bool canHandleNow = true;
+            for(k2 = 0; k2 < srcRegs[k].num_dests; k2++) {
+                if(dstStillInUse(srcRegs[k].dsts[k2].targetReg)) {
+                    canHandleNow = false;
+                    break;
+                }
+            }
+            if(canHandleNow) {
+                handledSrc[k] = true;
+                num_handled++;
+                handledOrder[num_in_order] = k;
+                num_in_order++;
+            }
+        } //for k
+        if(num_handled == prev_handled) {
+            ALOGE("no progress in selecting order");
+            dvmAbort();
+        }
+    } //while
+    for(k = 0; k < num_src_regs; k++) {
+        if(srcRegs[k].physicalReg == PhysicalReg_Null) {
+            handledOrder[num_in_order] = k;
+            num_in_order++;
+        }
+    }
+    if(num_in_order != num_src_regs) {
+        ALOGE("num_in_order != num_src_regs");
+        dvmAbort();
+    }
+#ifdef DEBUG_STATE
+    ALOGI("ORDER: ");
+    for(k = 0; k < num_src_regs; k++) {
+        ALOGI("%d ", handledOrder[k]);
+    }
+    ALOGI("");
+#endif
+}
+//! transfer the state of register allocator to a state specified in a state table
+
+//!
+void transferToState(int stateNum) {
+    freeReg(false); //do not spill GL
+    int k;
+#ifdef DEBUG_STATE
+    ALOGI("STATE: transfer to state %d", stateNum);
+#endif
+    if(stateNum > 4 || stateNum < 1) ALOGE("state table overflow");
+    constructSrcRegs(stateNum);
+    int k4, k3;
+    for(k4 = 0; k4 < num_src_regs; k4++) {
+        int k2 = handledOrder[k4]; //index to srcRegs
+        for(k3 = 0; k3 < srcRegs[k2].num_dests; k3++) {
+            k = srcRegs[k2].dsts[k3].compileIndex;
+            int targetReg = srcRegs[k2].dsts[k3].targetReg;
+            int targetSpill = srcRegs[k2].dsts[k3].targetSpill;
+            if(compileTable[k].physicalReg != targetReg && isVirtualReg(compileTable[k].physicalType)) {
+                OpndSize oSize = getRegSize(compileTable[k].physicalType);
+                bool isSS = ((compileTable[k].physicalType & MASK_FOR_TYPE) == LowOpndRegType_ss);
+                if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    if(isSS)
+                        move_ss_mem_to_reg_noalloc(4*compileTable[k].regNum,
+                                                   PhysicalReg_FP, true,
+                                                   MemoryAccess_VR, compileTable[k].regNum,
+                                                   targetReg, true);
+                    else
+                        move_mem_to_reg_noalloc(oSize, 4*compileTable[k].regNum,
+                                                PhysicalReg_FP, true,
+                                                MemoryAccess_VR, compileTable[k].regNum,
+                                                targetReg, true);
+                }
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    move_reg_to_reg_noalloc((isSS ? OpndSize_64 : oSize),
+                                            compileTable[k].physicalReg, true,
+                                            targetReg, true);
+                }
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                    dumpToMem(compileTable[k].regNum, (LowOpndRegType)(compileTable[k].physicalType & MASK_FOR_TYPE),
+                              compileTable[k].physicalReg);
+                }
+            } //VR
+            if(compileTable[k].physicalReg != targetReg && !isVirtualReg(compileTable[k].physicalType)) {
+                OpndSize oSize = getRegSize(compileTable[k].physicalType);
+                if(compileTable[k].physicalReg == PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    loadFromSpillRegion(oSize, targetReg,
+                                        compileTable[k].spill_loc_index);
+                }
+                //both are not null, move from one to the other
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg != PhysicalReg_Null) {
+                    move_reg_to_reg_noalloc(oSize, compileTable[k].physicalReg, true,
+                                            targetReg, true);
+                }
+                //current is not null, target is null (move from reg to memory)
+                if(compileTable[k].physicalReg != PhysicalReg_Null && targetReg == PhysicalReg_Null) {
+                    saveToSpillRegion(oSize, compileTable[k].physicalReg, targetSpill);
+                }
+            } //temporary
+        }//for
+    }//for
+    for(k = 0; k < num_memory_vr; k++) {
+        bool targetBool = false;
+        int targetReg = -1;
+        if(stateNum == 1) {
+            targetReg = stateTable2_1[k].regNum;
+            targetBool = stateTable2_1[k].inMemory;
+        }
+        else if(stateNum == 2) {
+            targetReg = stateTable2_2[k].regNum;
+            targetBool = stateTable2_2[k].inMemory;
+        }
+        else if(stateNum == 3) {
+            targetReg = stateTable2_3[k].regNum;
+            targetBool = stateTable2_3[k].inMemory;
+        }
+        else if(stateNum == 4) {
+            targetReg = stateTable2_4[k].regNum;
+            targetBool = stateTable2_4[k].inMemory;
+        }
+        if(targetReg != memVRTable[k].regNum)
+            ALOGE("regNum mismatch in transferToState");
+        if(targetBool && (!memVRTable[k].inMemory)) {
+            //dump to memory, check entries in compileTable: vA gp vA xmm vA ss
+#ifdef DEBUG_STATE
+            ALOGW("inMemory mismatch for VR %d in transferToState", targetReg);
+#endif
+            bool doneXfer = false;
+            int index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg);
+            if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                dumpToMem(targetReg, LowOpndRegType_xmm, compileTable[index].physicalReg);
+                doneXfer = true;
+            }
+            if(!doneXfer) { //vA-1, xmm
+                index = searchCompileTable(LowOpndRegType_xmm | LowOpndRegType_virtual, targetReg-1);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg-1, LowOpndRegType_xmm, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) { //vA gp
+                index = searchCompileTable(LowOpndRegType_gp | LowOpndRegType_virtual, targetReg);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg, LowOpndRegType_gp, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) { //vA, ss
+                index = searchCompileTable(LowOpndRegType_ss | LowOpndRegType_virtual, targetReg);
+                if(index >= 0 && compileTable[index].physicalReg != PhysicalReg_Null) {
+                    dumpToMem(targetReg, LowOpndRegType_ss, compileTable[index].physicalReg);
+                    doneXfer = true;
+                }
+            }
+            if(!doneXfer) ALOGW("can't match inMemory of VR %d in transferToState", targetReg);
+        }
+        if((!targetBool) && memVRTable[k].inMemory) {
+            //do nothing
+        }
+    }
+#ifdef DEBUG_STATE
+    ALOGI("END transferToState %d", stateNum);
+#endif
+    goToState(stateNum);
+}
+/* END code to handle state transfers */
diff --git a/vm/compiler/codegen/x86/AnalysisO1.h b/vm/compiler/codegen/x86/AnalysisO1.h
new file mode 100644
index 0000000..7f436d9
--- /dev/null
+++ b/vm/compiler/codegen/x86/AnalysisO1.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file ncg_o1_data.h
+    \brief A header file to define data structures used by register allocator & const folding
+*/
+#ifndef _DALVIK_NCG_ANALYSISO1_H
+#define _DALVIK_NCG_ANALYSISO1_H
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+#include "Lower.h"
+#ifdef WITH_JIT
+#include "compiler/CompilerIR.h"
+#endif
+
+//! maximal number of edges per basic block
+#define MAX_NUM_EDGE_PER_BB 300
+//! maximal number of basic blocks per method
+#define MAX_NUM_BBS_PER_METHOD 1000
+//! maximal number of virtual registers per basic block
+#define MAX_REG_PER_BASICBLOCK 140
+//! maximal number of virtual registers per bytecode
+#define MAX_REG_PER_BYTECODE 40
+//! maximal number of virtual registers per method
+#define MAX_REG_PER_METHOD 200
+//! maximal number of temporaries per bytecode
+#define MAX_TEMP_REG_PER_BYTECODE 30
+//! maximal number of GG GPR VRs in a method
+#define MAX_GLOBAL_VR      2
+//! maximal number of GG XMM VRs in a method
+#define MAX_GLOBAL_VR_XMM  4
+#define MAX_CONST_REG 150
+
+#define MASK_FOR_TYPE 7 //last 3 bits 111
+
+#define LOOP_COUNT 10
+//! maximal number of entries in compileTable
+#define COMPILE_TABLE_SIZE 200
+//! maximal number of transfer points per basic block
+#define MAX_XFER_PER_BB 1000  //on Jan 4
+#define PC_FOR_END_OF_BB -999
+#define PC_FOR_START_OF_BB -998
+
+//! various cases of overlapping between 2 variables
+typedef enum OverlapCase {
+  OVERLAP_ALIGN = 0,
+  OVERLAP_B_IS_LOW_OF_A,
+  OVERLAP_B_IS_HIGH_OF_A,
+  OVERLAP_LOW_OF_A_IS_HIGH_OF_B,
+  OVERLAP_HIGH_OF_A_IS_LOW_OF_B,
+  OVERLAP_A_IS_LOW_OF_B,
+  OVERLAP_A_IS_HIGH_OF_B,
+  OVERLAP_B_COVER_A,
+  OVERLAP_B_COVER_LOW_OF_A,
+  OVERLAP_B_COVER_HIGH_OF_A,
+  OVERLAP_NO
+} OverlapCase;
+
+//!access type of a variable
+typedef enum RegAccessType {
+  REGACCESS_D = 0,
+  REGACCESS_U,
+  REGACCESS_DU,
+  REGACCESS_UD,
+  REGACCESS_L,
+  REGACCESS_H,
+  REGACCESS_UL,
+  REGACCESS_UH,
+  REGACCESS_LU,
+  REGACCESS_HU,
+  REGACCESS_N, //no access
+  REGACCESS_UNKNOWN
+} RegAccessType;
+//! a variable can be local (L), globally local (GL) or global (GG)
+typedef enum GlobalType {
+  GLOBALTYPE_GG,
+  GLOBALTYPE_GL,
+  GLOBALTYPE_L
+} GlobalType;
+typedef enum VRState {
+  VRSTATE_SPILLED,
+  VRSTATE_UPDATED,
+  VRSTATE_CLEAN
+} VRState;
+//! helper state to determine if freeing VRs needs to be delayed
+enum VRDelayFreeFlags {
+  VRDELAY_NONE = 0, // used when VR can be freed from using physical register if needed
+  VRDELAY_NULLCHECK = 1 << 0, // used when VR is used for null check and freeing must be delayed
+  VRDELAY_BOUNDCHECK = 1 << 1 // used when VR is used for bound check and freeing must be delayed
+};
+typedef enum TRState { //state of temporary registers
+  TRSTATE_SPILLED,
+  TRSTATE_UNSPILLED,
+  TRSTATE_CLEAN
+} TRState;
+//!information about a physical register
+typedef struct RegisterInfo {
+  PhysicalReg physicalReg;
+  bool isUsed;
+  bool isCalleeSaved;
+  int freeTimeStamp;
+} RegisterInfo;
+typedef struct UniqueRegister {
+  LowOpndRegType physicalType;
+  int regNum;
+  int numExposedUsage;
+  PhysicalReg physicalReg;
+} UniqueRegister;
+//!specifies the weight of a VR allocated to a specific physical register
+//!it is used for GPR VR only
+typedef struct RegAllocConstraint {
+  PhysicalReg physicalReg;
+  int count;
+} RegAllocConstraint;
+
+typedef enum XferType {
+  XFER_MEM_TO_XMM, //for usage
+  XFER_DEF_TO_MEM, //def is gp
+  XFER_DEF_TO_GP_MEM,
+  XFER_DEF_TO_GP,
+  XFER_DEF_IS_XMM //def is xmm
+} XferType;
+typedef struct XferPoint {
+  int tableIndex; //generated from a def-use pair
+  XferType xtype;
+  int offsetPC;
+  int regNum; //get or set VR at offsetPC
+  LowOpndRegType physicalType;
+
+  //if XFER_DEF_IS_XMM
+  int vr_gpl; //a gp VR that uses the lower half of the def
+  int vr_gph;
+  bool dumpToXmm;
+  bool dumpToMem;
+} XferPoint;
+
+//!for def: accessType means which part of the VR defined at offestPC is live now
+//!for use: accessType means which part of the usage comes from the reachingDef
+typedef struct DefOrUse {
+  int offsetPC; //!the program point
+  int regNum; //!access the virtual reg
+  LowOpndRegType physicalType; //!xmm or gp or ss
+  RegAccessType accessType; //!D, L, H, N
+} DefOrUse;
+//!a link list of DefOrUse
+typedef struct DefOrUseLink {
+  int offsetPC;
+  int regNum; //access the virtual reg
+  LowOpndRegType physicalType; //xmm or gp
+  RegAccessType accessType; //D, L, H, N
+  struct DefOrUseLink* next;
+} DefOrUseLink;
+//!pair of def and uses
+typedef struct DefUsePair {
+  DefOrUseLink* uses;
+  DefOrUseLink* useTail;
+  int num_uses;
+  DefOrUse def;
+  struct DefUsePair* next;
+} DefUsePair;
+
+//!information associated with a virtual register
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct VirtualRegInfo {
+  int regNum;
+  LowOpndRegType physicalType;
+  int refCount;
+  RegAccessType accessType;
+  GlobalType gType;
+  int physicalReg_GG;
+  RegAllocConstraint allocConstraints[8];
+  RegAllocConstraint allocConstraintsSorted[8];
+
+  DefOrUse reachingDefs[3]; //!reaching defs to the virtual register
+  int num_reaching_defs;
+} VirtualRegInfo;
+//!information of whether a VR is constant and its value
+typedef struct ConstVRInfo {
+  int regNum;
+  int value;
+  bool isConst;
+} ConstVRInfo;
+#define NUM_ACCESS_IN_LIVERANGE 10
+//!specifies one live range
+typedef struct LiveRange {
+  int start;
+  int end; //inclusive
+  //all accesses in the live range
+  int num_access;
+  int num_alloc;
+  int* accessPC;
+  struct LiveRange* next;
+} LiveRange;
+typedef struct BoundCheckIndex {
+  int indexVR;
+  bool checkDone;
+} BoundCheckIndex;
+//!information for a virtual register such as live ranges, in memory
+typedef struct MemoryVRInfo {
+  int regNum;
+  bool inMemory;
+  bool nullCheckDone;
+  BoundCheckIndex boundCheck;
+  int num_ranges;
+  LiveRange* ranges;
+  u4 delayFreeFlags; //! for use with flags defined by VRDelayFreeFlags enum
+} MemoryVRInfo;
+//!information of a temporary
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct TempRegInfo {
+  int regNum;
+  int physicalType;
+  int refCount;
+  int linkageToVR;
+  int versionNum;
+  bool shareWithVR; //for temp. regs updated by get_virtual_reg
+  bool is8Bit;
+} TempRegInfo;
+struct BasicBlock_O1;
+//!all variables accessed
+//!the pair <regNum, physicalType> uniquely determines a variable
+typedef struct compileTableEntry {
+  int regNum;
+  int physicalType; //gp, xmm or scratch, virtual
+  int physicalReg;
+  int physicalReg_prev; //for spilled GG VR
+  RegAccessType accessType;
+
+  bool isConst;
+  int value[2]; //[0]: lower [1]: higher
+  int refCount;
+
+  int linkageToVR; //for temporary registers only
+  GlobalType gType;
+  struct BasicBlock_O1* bb; //bb VR belongs to
+  int indexToInfoBB;
+
+  VRState regState;
+  TRState trState; //for temporary registers only
+  int spill_loc_index; //for temporary registers only
+} compileTableEntry;
+//!to save the state of register allocator
+typedef struct regAllocStateEntry1 {
+  int spill_loc_index;
+  int physicalReg;
+} regAllocStateEntry1;
+typedef struct regAllocStateEntry2 {
+  int regNum; //
+  bool inMemory; //whether 4-byte virtual reg is in memory
+} regAllocStateEntry2;
+//!edge in control flow graph
+typedef struct Edge_O1 {
+  struct BasicBlock_O1* src;
+  struct BasicBlock_O1* dst;
+} Edge_O1;
+//!information associated with a basic block
+typedef struct BasicBlock_O1 {
+  int bb_index;
+  int bb_index2;
+  int pc_start;       //!inclusive
+#ifndef WITH_JIT
+  int pc_end;         //!exclusive
+  Edge_O1* in_edges[MAX_NUM_EDGE_PER_BB]; //array of Edge*
+  int num_in_edges;
+  Edge_O1* out_edges[MAX_NUM_EDGE_PER_BB];
+  int num_out_edges;
+#else
+  int pc_end;
+  BasicBlock* jitBasicBlock;
+#endif
+  VirtualRegInfo infoBasicBlock[MAX_REG_PER_BASICBLOCK];
+  int num_regs;
+
+  RegAllocConstraint allocConstraints[8]; //# of times a hardcoded register is used in this basic block
+  //a physical register that is used many times has a lower priority to get picked in getFreeReg
+  RegAllocConstraint allocConstraintsSorted[8]; //count from low to high
+
+  DefUsePair* defUseTable;
+  DefUsePair* defUseTail;
+  int num_defs;
+  XferPoint xferPoints[MAX_XFER_PER_BB]; //program points where the transfer is required
+  int num_xfer_points;
+
+  bool endsWithReturn;
+  bool hasAccessToGlue;
+} BasicBlock_O1;
+typedef struct CFG_O1 {
+  BasicBlock_O1* head;
+} CFG_O1;
+//!worklist to create a control flow graph
+typedef struct CFGWork {
+  BasicBlock_O1* bb_prev;
+  int targetOff;
+  struct CFGWork* nextItem;
+} CFGWork;
+
+/////////////////////////////////////////
+extern compileTableEntry compileTable[COMPILE_TABLE_SIZE];
+extern int num_compile_entries;
+extern VirtualRegInfo infoByteCode[MAX_REG_PER_BYTECODE];
+extern int num_regs_per_bytecode;
+extern TempRegInfo infoByteCodeTemp[MAX_TEMP_REG_PER_BYTECODE];
+extern int num_temp_regs_per_bytecode;
+extern VirtualRegInfo infoMethod[MAX_REG_PER_METHOD];
+extern int num_regs_per_method;
+extern BasicBlock_O1* currentBB;
+
+extern BasicBlock_O1* method_bbs[MAX_NUM_BBS_PER_METHOD];
+extern int num_bbs_for_method;
+extern BasicBlock_O1* method_bbs_sorted[MAX_NUM_BBS_PER_METHOD];
+extern BasicBlock_O1* bb_entry;
+extern int pc_start;
+extern int pc_end;
+extern int current_bc_size;
+extern int num_exception_handlers;
+extern int exceptionHandlers[10];
+
+extern int num_const_vr;
+extern ConstVRInfo constVRTable[MAX_CONST_REG];
+
+extern int genSet[MAX_REG_PER_BYTECODE];
+extern int killSet[MAX_REG_PER_BYTECODE];
+extern int num_regs_gen; //per bytecode
+extern int num_regs_kill; //per bytecode
+
+extern int genSetBB[MAX_NUM_BBS_PER_METHOD][40];
+extern int killSetBB[MAX_NUM_BBS_PER_METHOD][40]; //same as size of memVRTable
+extern int num_gen_bb[MAX_NUM_BBS_PER_METHOD];
+extern int num_kill_bb[MAX_NUM_BBS_PER_METHOD];
+
+extern int nullCheck_inB[MAX_NUM_BBS_PER_METHOD][40];
+extern int nullCheck_inSize[MAX_NUM_BBS_PER_METHOD];
+extern int nullCheck_outB[MAX_NUM_BBS_PER_METHOD][40];
+extern int nullCheck_outSize[MAX_NUM_BBS_PER_METHOD];
+
+typedef enum GlueVarType {
+  RES_CLASS = 0,
+  RES_METHOD,
+  RES_FIELD,
+  RES_STRING,
+  GLUE_DVMDEX,
+  GLUE_METHOD_CLASS,
+  GLUE_METHOD
+} GlueVarType;
+
+void forwardAnalysis(int type);
+
+//functions in bc_visitor.c
+int getByteCodeSize();
+bool getConstInfo(BasicBlock_O1* bb);
+int getVirtualRegInfo(VirtualRegInfo* infoArray);
+int getTempRegInfo(TempRegInfo* infoArray);
+int createCFGHandler(Method* method);
+
+int findVirtualRegInTable(u2 vA, LowOpndRegType type, bool printError);
+int searchCompileTable(int type, int regNum);
+BasicBlock_O1* createBasicBlock(int src_pc, int end_pc);
+void handleJump(BasicBlock_O1* bb_prev, int relOff);
+void connectBasicBlock(BasicBlock_O1* src, BasicBlock_O1* dst);
+int insertWorklist(BasicBlock_O1* bb_prev, int targetOff);
+
+int collectInfoOfBasicBlock(Method* method, BasicBlock_O1* bb); //update bb->infoBasicBlock
+
+void updateCurrentBBWithConstraints(PhysicalReg reg);
+void updateConstInfo(BasicBlock_O1*);
+OpndSize getRegSize(int type);
+void invalidateVRDueToConst(int reg, OpndSize size);
+#endif
+
diff --git a/vm/compiler/codegen/x86/BytecodeVisitor.cpp b/vm/compiler/codegen/x86/BytecodeVisitor.cpp
new file mode 100644
index 0000000..1d3c70e
--- /dev/null
+++ b/vm/compiler/codegen/x86/BytecodeVisitor.cpp
@@ -0,0 +1,5468 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file BytecodeVisitor.cpp
+    \brief This file implements visitors of the bytecode
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "AnalysisO1.h"
+
+//! Returns size of the current bytecode in u2 unit
+
+//!
+int getByteCodeSize() { //uses inst, unit in u2
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return 1;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+        return 1;
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+        return 2;
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        return 3;
+    case OP_MOVE_WIDE:
+        return 1;
+    case OP_MOVE_WIDE_FROM16:
+        return 2;
+    case OP_MOVE_WIDE_16:
+        return 3;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        return 1;
+    case OP_MOVE_RESULT_WIDE:
+        return 1;
+    case OP_MOVE_EXCEPTION:
+        return 1;
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return 1;
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        return 1;
+    case OP_RETURN_WIDE:
+        return 1;
+    case OP_CONST_4:
+        return 1;
+    case OP_CONST_16:
+        return 2;
+    case OP_CONST:
+        return 3;
+    case OP_CONST_HIGH16:
+        return 2;
+    case OP_CONST_WIDE_16:
+        return 2;
+    case OP_CONST_WIDE_32:
+        return 3;
+    case OP_CONST_WIDE:
+        return 5;
+    case OP_CONST_WIDE_HIGH16:
+        return 2;
+    case OP_CONST_STRING:
+        return 2;
+    case OP_CONST_STRING_JUMBO:
+        return 3;
+    case OP_CONST_CLASS:
+        return 2;
+    case OP_MONITOR_ENTER:
+        return 1;
+    case OP_MONITOR_EXIT:
+        return 1;
+    case OP_CHECK_CAST:
+        return 2;
+    case OP_INSTANCE_OF:
+        return 2;
+    case OP_ARRAY_LENGTH:
+        return 1;
+    case OP_NEW_INSTANCE:
+        return 2;
+    case OP_NEW_ARRAY:
+        return 2;
+    case OP_FILLED_NEW_ARRAY:
+        return 3;
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        return 3;
+    case OP_FILL_ARRAY_DATA:
+        return 3;
+    case OP_THROW:
+        return 1;
+    case OP_THROW_VERIFICATION_ERROR:
+        return 2;
+    case OP_GOTO:
+        return 1;
+    case OP_GOTO_16:
+        return 2;
+    case OP_GOTO_32:
+        return 3;
+    case OP_PACKED_SWITCH:
+        return 3;
+    case OP_SPARSE_SWITCH:
+        return 3;
+    case OP_CMPL_FLOAT:
+        return 2;
+    case OP_CMPG_FLOAT:
+        return 2;
+    case OP_CMPL_DOUBLE:
+        return 2;
+    case OP_CMPG_DOUBLE:
+        return 2;
+    case OP_CMP_LONG:
+        return 2;
+    case OP_IF_EQ:
+        return 2;
+    case OP_IF_NE:
+        return 2;
+    case OP_IF_LT:
+        return 2;
+    case OP_IF_GE:
+        return 2;
+    case OP_IF_GT:
+        return 2;
+    case OP_IF_LE:
+        return 2;
+    case OP_IF_EQZ:
+        return 2;
+    case OP_IF_NEZ:
+        return 2;
+    case OP_IF_LTZ:
+        return 2;
+    case OP_IF_GEZ:
+        return 2;
+    case OP_IF_GTZ:
+        return 2;
+    case OP_IF_LEZ:
+        return 2;
+    case OP_AGET:
+        return 2;
+    case OP_AGET_WIDE:
+        return 2;
+    case OP_AGET_OBJECT:
+        return 2;
+    case OP_AGET_BOOLEAN:
+        return 2;
+    case OP_AGET_BYTE:
+        return 2;
+    case OP_AGET_CHAR:
+        return 2;
+    case OP_AGET_SHORT:
+        return 2;
+    case OP_APUT:
+        return 2;
+    case OP_APUT_WIDE:
+        return 2;
+    case OP_APUT_OBJECT:
+        return 2;
+    case OP_APUT_BOOLEAN:
+        return 2;
+    case OP_APUT_BYTE:
+        return 2;
+    case OP_APUT_CHAR:
+        return 2;
+    case OP_APUT_SHORT:
+        return 2;
+    case OP_IGET:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IPUT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+        return 2;
+    case OP_SGET:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+    case OP_SPUT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        return 2;
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        return 3;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_FLOAT:
+    case OP_NEG_DOUBLE:
+    case OP_INT_TO_LONG:
+    case OP_INT_TO_FLOAT:
+    case OP_INT_TO_DOUBLE:
+    case OP_LONG_TO_INT:
+    case OP_LONG_TO_FLOAT:
+    case OP_LONG_TO_DOUBLE:
+    case OP_FLOAT_TO_INT:
+    case OP_FLOAT_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+    case OP_DOUBLE_TO_INT:
+    case OP_DOUBLE_TO_LONG:
+    case OP_DOUBLE_TO_FLOAT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        return 1;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_DIV_INT:
+    case OP_REM_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    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:
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        return 2;
+
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+    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:
+    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:
+    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:
+        return 1;
+
+    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:
+        return 2;
+
+    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_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        return 2;
+
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+        return 3;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return 3;
+#endif
+
+    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:
+        return 2;
+
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        return 3;
+#ifdef SUPPORT_HLO
+    case kExtInstruction:
+        switch(inst) {
+        case OP_X_AGET_QUICK:
+        case OP_X_AGET_WIDE_QUICK:
+        case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+    case OP_X_APUT_QUICK:
+    case OP_X_APUT_WIDE_QUICK:
+    case OP_X_APUT_OBJECT_QUICK:
+    case OP_X_APUT_BOOLEAN_QUICK:
+    case OP_X_APUT_BYTE_QUICK:
+    case OP_X_APUT_CHAR_QUICK:
+    case OP_X_APUT_SHORT_QUICK:
+        return 3;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_WIDE:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+    case OP_X_DEREF_PUT:
+    case OP_X_DEREF_PUT_WIDE:
+    case OP_X_DEREF_PUT_OBJECT:
+    case OP_X_DEREF_PUT_BOOLEAN:
+    case OP_X_DEREF_PUT_BYTE:
+    case OP_X_DEREF_PUT_CHAR:
+    case OP_X_DEREF_PUT_SHORT:
+        return 2;
+    case OP_X_ARRAY_CHECKS:
+    case OP_X_ARRAY_OBJECT_CHECKS:
+        return 3;
+    case OP_X_CHECK_BOUNDS:
+    case OP_X_CHECK_NULL:
+    case OP_X_CHECK_TYPE:
+        return 2;
+    }
+#endif
+    }
+    return -1;
+}
+//! reduces refCount of a virtual register
+
+//!
+void touchOneVR(u2 vA, LowOpndRegType type) {
+    int index = searchCompileTable(LowOpndRegType_virtual | type, vA);
+    if(index < 0) {
+        ALOGE("virtual reg %d type %d not found in touchOneVR", vA, type);
+        return;
+    }
+    compileTable[index].refCount--;
+}
+//! reduces refCount of two virtual registers
+
+//!
+void touchTwoVRs(u2 vA, u2 vB, LowOpndRegType type) {
+    int index = searchCompileTable(LowOpndRegType_virtual | type, vA);
+    if(index < 0) {
+        ALOGE("virtual reg vA %d type %d not found in touchTwoVRs", vA, type);
+        return;
+    }
+    compileTable[index].refCount--;
+    index = searchCompileTable(LowOpndRegType_virtual | type, vB);
+    if(index < 0) {
+        ALOGE("virtual reg vB %d type %d not found in touchTwoVRs", vB, type);
+        return;
+    }
+    compileTable[index].refCount--;
+}
+int num_const_worklist;
+//! worklist to update constVRTable later
+int constWorklist[10];
+
+int num_const_vr; //in a basic block
+//! table to store the constant information for virtual registers
+ConstVRInfo constVRTable[MAX_CONST_REG];
+//! update constVRTable for a given virtual register
+
+//! set "isConst" to false
+void setVRToNonConst(int regNum, OpndSize size) {
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    if(indexL >= 0) {
+        //remove this entry??
+        constVRTable[indexL].isConst = false;
+    }
+    if(size == OpndSize_64 && indexH >= 0) {
+        constVRTable[indexH].isConst = false;
+    }
+}
+//! update constVRTable for a given virtual register
+
+//! set "isConst" to true
+void setVRToConst(int regNum, OpndSize size, int* tmpValue) {
+    int k;
+    int indexL = -1;
+    int indexH = -1;
+    for(k = 0; k < num_const_vr; k++) {
+        if(constVRTable[k].regNum == regNum) {
+            indexL = k;
+            continue;
+        }
+        if(constVRTable[k].regNum == regNum + 1 && size == OpndSize_64) {
+            indexH = k;
+            continue;
+        }
+    }
+    if(indexL < 0) {
+        indexL = num_const_vr;
+        constVRTable[indexL].regNum = regNum;
+        num_const_vr++;
+    }
+    constVRTable[indexL].isConst = true;
+    constVRTable[indexL].value = tmpValue[0];
+    if(size == OpndSize_64) {
+        if(indexH < 0) {
+            indexH = num_const_vr;
+            constVRTable[indexH].regNum = regNum+1;
+            num_const_vr++;
+        }
+        constVRTable[indexH].isConst = true;
+        constVRTable[indexH].value = tmpValue[1];
+    }
+    if(num_const_vr > MAX_CONST_REG) ALOGE("constVRTable overflows");
+    invalidateVRDueToConst(regNum, size);
+}
+
+//! perform work on constWorklist
+
+//!
+void updateConstInfo(BasicBlock_O1* bb) {
+    if(bb == NULL) return;
+    int k;
+    for(k = 0; k < num_const_worklist; k++) {
+        //int indexOrig = constWorklist[k];
+        //compileTable[indexOrig].isConst = false;
+        //int A = compileTable[indexOrig].regNum;
+        //LowOpndRegType type = compileTable[indexOrig].physicalType & MASK_FOR_TYPE;
+        setVRToNonConst(constWorklist[k], OpndSize_32);
+    }
+}
+//! check whether the current bytecode generates a const
+
+//! if yes, update constVRTable; otherwise, update constWorklist
+//! if a bytecode uses vA (const), and updates vA to non const, getConstInfo will return false and update constWorklist to make sure when lowering the bytecode, vA is treated as constant
+bool getConstInfo(BasicBlock_O1* bb) {
+    compileTableEntry* infoArray = compileTable;
+    u2 inst_op = INST_INST(inst);
+    u2 vA = 0, vB = 0, v1, v2;
+    u2 BBBB;
+    u2 tmp_u2;
+    s4 tmp_s4;
+    u4 tmp_u4;
+    int entry, tmpValue[2], tmpValue2[2];
+    num_const_worklist = 0;
+
+    switch(inst_op) {
+        //for other opcode, if update the register, set isConst to false
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        if(inst_op == OP_MOVE || inst_op == OP_MOVE_OBJECT) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+        }
+        else if(inst_op == OP_MOVE_FROM16 || inst_op == OP_MOVE_OBJECT_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+        }
+        else if(inst_op == OP_MOVE_16 || inst_op == OP_MOVE_OBJECT_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+        }
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            infoArray[entry].isConst = true;
+            infoArray[entry].value[0] = tmpValue[0];
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+            return true;
+        } else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+        }
+        return false;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        if(inst_op == OP_MOVE_WIDE) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+        }
+        else if(inst_op == OP_MOVE_WIDE_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+        }
+        else if(inst_op == OP_MOVE_WIDE_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+        }
+        if(isVirtualRegConstant(vB, LowOpndRegType_xmm, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_xmm, true);
+            setVRToConst(vA, OpndSize_64, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_xmm);
+            return true;
+        } else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            constWorklist[num_const_worklist] = vA+1;
+            num_const_worklist++;
+        }
+        return false;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+    case OP_MOVE_EXCEPTION:
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+    case OP_NEW_INSTANCE:
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+    case OP_AGET:
+    case OP_AGET_OBJECT:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+    case OP_SGET:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_MOVE_RESULT_WIDE:
+    case OP_AGET_WIDE:
+    case OP_SGET_WIDE:
+    case OP_SGET_WIDE_VOLATILE:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_INSTANCE_OF:
+    case OP_ARRAY_LENGTH:
+    case OP_NEW_ARRAY:
+    case OP_IGET:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_IGET_WIDE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_WIDE_QUICK:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+        //TODO: constant folding for float/double/long ALU
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_FLOAT:
+    case OP_INT_TO_FLOAT:
+    case OP_LONG_TO_FLOAT:
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT:
+    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:
+    case OP_DOUBLE_TO_FLOAT:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA; //change constWorklist to point to vA TODO
+        num_const_worklist++;
+        return false;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+    case OP_FLOAT_TO_DOUBLE:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_DOUBLE:
+    case OP_INT_TO_DOUBLE: //fp stack
+    case OP_LONG_TO_DOUBLE:
+    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:
+        //ops on float, double
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_LONG_TO_INT:
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_NEG_INT)
+                infoArray[entry].value[0] = -tmpValue[0];
+            if(inst_op == OP_NOT_INT)
+                infoArray[entry].value[0] = ~tmpValue[0]; //CHECK
+            if(inst_op == OP_LONG_TO_INT)
+                infoArray[entry].value[0] = tmpValue[0];
+            if(inst_op == OP_INT_TO_BYTE)// sar
+                infoArray[entry].value[0] = (tmpValue[0] << 24) >> 24;
+            if(inst_op == OP_INT_TO_CHAR) //shr
+                infoArray[entry].value[0] = ((unsigned int)(tmpValue[0] << 16)) >> 16;
+            if(inst_op == OP_INT_TO_SHORT) //sar
+                infoArray[entry].value[0] = (tmpValue[0] << 16) >> 16;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_INT_TO_LONG:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1; //fixed on 10/15/2009
+        num_const_worklist++;
+        return false;
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_REM_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_DIV_INT:
+    case OP_REM_INT:
+        if(inst_op == OP_DIV_INT || inst_op == OP_DIV_INT_LIT8 ||
+           inst_op == OP_REM_INT || inst_op == OP_REM_INT_LIT8)
+            vA = INST_AA(inst);
+        else
+            vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        if(isVirtualRegConstant(vA, LowOpndRegType_gp, tmpValue, false) == 3 &&
+           isVirtualRegConstant(v2, LowOpndRegType_gp, tmpValue2, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] + tmpValue2[0];
+            if(inst_op == OP_SUB_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] - tmpValue2[0];
+            if(inst_op == OP_MUL_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] * tmpValue2[0];
+            if(inst_op == OP_DIV_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] / tmpValue2[0];
+            if(inst_op == OP_REM_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] % tmpValue2[0];
+            if(inst_op == OP_AND_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] & tmpValue2[0];
+            if(inst_op == OP_OR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] | tmpValue2[0];
+            if(inst_op == OP_XOR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmpValue2[0];
+            if(inst_op == OP_SHL_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] << tmpValue2[0];
+            if(inst_op == OP_SHR_INT_2ADDR)
+                infoArray[entry].value[0] = tmpValue[0] >> tmpValue2[0];
+            if(inst_op == OP_USHR_INT_2ADDR)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmpValue2[0];
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(v2, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        tmp_s4 = (s2)FETCH(1);
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] + tmp_s4;
+            if(inst_op == OP_RSUB_INT)
+                infoArray[entry].value[0] = tmp_s4 - tmpValue[0];
+            if(inst_op == OP_MUL_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] * tmp_s4;
+            if(inst_op == OP_DIV_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] / tmp_s4;
+            if(inst_op == OP_REM_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] % tmp_s4;
+            if(inst_op == OP_AND_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] & tmp_s4;
+            if(inst_op == OP_OR_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] | tmp_s4;
+            if(inst_op == OP_XOR_INT_LIT16)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmp_s4;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        if(isVirtualRegConstant(v1, LowOpndRegType_gp, tmpValue, false) == 3 &&
+           isVirtualRegConstant(v2, LowOpndRegType_gp, tmpValue2, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT)
+                infoArray[entry].value[0] = tmpValue[0] + tmpValue2[0];
+            if(inst_op == OP_SUB_INT)
+                infoArray[entry].value[0] = tmpValue[0] - tmpValue2[0];
+            if(inst_op == OP_MUL_INT)
+                infoArray[entry].value[0] = tmpValue[0] * tmpValue2[0];
+            if(inst_op == OP_DIV_INT)
+                infoArray[entry].value[0] = tmpValue[0] / tmpValue2[0];
+            if(inst_op == OP_REM_INT)
+                infoArray[entry].value[0] = tmpValue[0] % tmpValue2[0];
+            if(inst_op == OP_AND_INT)
+                infoArray[entry].value[0] = tmpValue[0] & tmpValue2[0];
+            if(inst_op == OP_OR_INT)
+                infoArray[entry].value[0] = tmpValue[0] | tmpValue2[0];
+            if(inst_op == OP_XOR_INT)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmpValue2[0];
+            if(inst_op == OP_SHL_INT)
+                infoArray[entry].value[0] = tmpValue[0] << tmpValue2[0];
+            if(inst_op == OP_SHR_INT)
+                infoArray[entry].value[0] = tmpValue[0] >> tmpValue2[0];
+            if(inst_op == OP_USHR_INT)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmpValue2[0];
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(v1, LowOpndRegType_gp);
+            touchOneVR(v2, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_INT_LIT8: //INST_AA
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        tmp_s4 = (s2)FETCH(1) >> 8;
+        if(isVirtualRegConstant(vB, LowOpndRegType_gp, tmpValue, false) == 3) {
+            entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+            infoArray[entry].isConst = true;
+            if(inst_op == OP_ADD_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] + tmp_s4;
+            if(inst_op == OP_RSUB_INT_LIT8)
+                infoArray[entry].value[0] = tmp_s4 - tmpValue[0];
+            if(inst_op == OP_MUL_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] * tmp_s4;
+            if(inst_op == OP_DIV_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] / tmp_s4;
+            if(inst_op == OP_REM_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] % tmp_s4;
+            if(inst_op == OP_AND_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] & tmp_s4;
+            if(inst_op == OP_OR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] | tmp_s4;
+            if(inst_op == OP_XOR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] ^ tmp_s4;
+            if(inst_op == OP_SHL_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] << tmp_s4;
+            if(inst_op == OP_SHR_INT_LIT8)
+                infoArray[entry].value[0] = tmpValue[0] >> tmp_s4;
+            if(inst_op == OP_USHR_INT_LIT8)
+                infoArray[entry].value[0] = (unsigned int)tmpValue[0] >> tmp_s4;
+            tmpValue[0] = infoArray[entry].value[0];
+            setVRToConst(vA, OpndSize_32, tmpValue);
+            compileTable[entry].refCount--;
+            touchOneVR(vB, LowOpndRegType_gp);
+#ifdef DEBUG_CONST
+            LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+            return true;
+        }
+        else {
+            constWorklist[num_const_worklist] = vA;
+            num_const_worklist++;
+            return false;
+        }
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        //TODO bytecode is not going to update state registers
+        //constant folding
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_CMP_LONG:
+        vA = INST_AA(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_CONST_4:
+        vA = INST_A(inst);
+        tmp_s4 = (s4) (INST_B(inst) << 28) >> 28;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = tmp_s4;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, tmp_s4);
+#endif
+        return true;
+    case OP_CONST_16:
+        BBBB = FETCH(1);
+        vA = INST_AA(inst);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)BBBB;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_HIGH16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u2<<16;
+        tmpValue[0] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_32, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %d", vA, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)tmp_u2;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s2)tmp_u2>>31;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_32:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4>>31;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u8)FETCH(2) << 16;
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        tmp_u4 = (u8)FETCH(3);
+        tmp_u4 |= (u8)FETCH(4) << 16;
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u4;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+    case OP_CONST_WIDE_HIGH16:
+        vA = INST_AA(inst);
+        tmp_u2 = FETCH(1);
+        entry = findVirtualRegInTable(vA, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = 0;
+        tmpValue[0] = infoArray[entry].value[0];
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA, infoArray[entry].value[0]);
+#endif
+
+        entry = findVirtualRegInTable(vA+1, LowOpndRegType_gp, true);
+        infoArray[entry].isConst = true;
+        infoArray[entry].value[0] = (s4)tmp_u2<<16;
+        tmpValue[1] = infoArray[entry].value[0];
+        setVRToConst(vA, OpndSize_64, tmpValue);
+        compileTable[entry].refCount--;
+#ifdef DEBUG_CONST
+        LOGD("getConstInfo: set VR %d to %x", vA+1, infoArray[entry].value[0]);
+#endif
+        return true;
+#ifdef SUPPORT_HLO
+    case OP_X_AGET_QUICK:
+    case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_X_AGET_WIDE_QUICK:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        return false;
+    case OP_X_DEREF_GET_WIDE:
+        vA = FETCH(1) & 0xff;
+        constWorklist[num_const_worklist] = vA;
+        num_const_worklist++;
+        constWorklist[num_const_worklist] = vA+1;
+        num_const_worklist++;
+        return false;
+#endif
+    }
+    return false;
+}
+
+//! This function updates infoArray with virtual registers accessed when lowering the bytecode, and returns size of the bytecode in unit of u2
+
+//! uses of virtual registers are added to infoArray first
+int getVirtualRegInfo(VirtualRegInfo* infoArray) {
+    u2 inst_op = INST_INST(inst);
+    u2 vA = 0, vB = 0, vref, vindex;
+    u2 v1, v2, length, vD, vG, vE, vF, count;
+    u4 v1_u4, v2_u4;
+    int kk, num, num_entry;
+    s4 tmp_s4;
+    s2 tmp_s2;
+    u4 tmp_u4;
+    int codeSize = 0;
+    num_regs_per_bytecode = 0;
+    //update infoArray[xx].allocConstraints
+    for(num = 0; num < MAX_REG_PER_BYTECODE; num++) {
+        for(kk = 0; kk < 8; kk++) {
+            infoArray[num].allocConstraints[kk].physicalReg = (PhysicalReg)kk;
+            infoArray[num].allocConstraints[kk].count = 0;
+        }
+    }
+
+    switch (inst_op) {
+    case OP_NOP:
+        codeSize = 1;
+        break;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        if(inst_op == OP_MOVE || inst_op == OP_MOVE_OBJECT) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+            codeSize = 1;
+        }
+        else if(inst_op == OP_MOVE_FROM16 || inst_op == OP_MOVE_OBJECT_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+            codeSize = 2;
+        }
+        else if(inst_op == OP_MOVE_16 || inst_op == OP_MOVE_OBJECT_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+            codeSize = 3;
+        }
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        if(inst_op == OP_MOVE_WIDE) {
+            vA = INST_A(inst);
+            vB = INST_B(inst);
+            codeSize = 1;
+        }
+        else if(inst_op == OP_MOVE_WIDE_FROM16) {
+            vA = INST_AA(inst);
+            vB = FETCH(1);
+            codeSize = 2;
+        }
+        else if(inst_op == OP_MOVE_WIDE_16) {
+            vA = FETCH(1);
+            vB = FETCH(2);
+            codeSize = 3;
+        }
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MOVE_RESULT: //access memory
+    case OP_MOVE_RESULT_OBJECT:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MOVE_RESULT_WIDE: //note: 2 destinations
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MOVE_EXCEPTION: //access memory
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        codeSize = 1;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        codeSize = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_RETURN_WIDE:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_CONST_4:
+        vA = INST_A(inst);
+        tmp_s4 = (s4) (INST_B(inst) << 28) >> 28;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_CONST_16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_CONST:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 3;
+        break;
+    case OP_CONST_HIGH16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_CONST_WIDE_16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        codeSize = 2;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_CONST_WIDE_32:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 3;
+        break;
+    case OP_CONST_WIDE:
+        vA = INST_AA(inst);
+        tmp_u4 = FETCH(1);
+        tmp_u4 |= (u8)FETCH(2) << 16;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        tmp_u4 = (u8)FETCH(3);
+        tmp_u4 |= (u8)FETCH(4) << 16;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        codeSize = 5;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_CONST_WIDE_HIGH16:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 2;
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+        vA = INST_AA(inst);
+        if(inst_op == OP_CONST_STRING || inst_op == OP_CONST_CLASS)
+            codeSize = 2;
+        else if(inst_op == OP_CONST_STRING_JUMBO)
+            codeSize = 3;
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MONITOR_ENTER:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_MONITOR_EXIT:
+        vA = INST_AA(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX); //eax is used as return value from c function
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_CHECK_CAST:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+    case OP_INSTANCE_OF:
+        codeSize = 2;
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ARRAY_LENGTH:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        //%edx is used in this bytecode, update currentBB->allocConstraints
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_NEW_INSTANCE:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //dst
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_D;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_NEW_ARRAY:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst); //length
+        infoArray[0].regNum = vB; //src
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA; //dst
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        codeSize = 2;
+        break;
+    case OP_FILLED_NEW_ARRAY: {//update return value
+        //can use up to 5 registers to fill the content of array
+        length = INST_B(inst);
+        u2 vv = FETCH(2);
+        v1 = vv & 0xf;
+        v2 = (vv >> 4) & 0xf;
+        u2 v3 = (vv >> 8) & 0xf;
+        u2 v4 = (vv >> 12) & 0xf;
+        u2 v5 = INST_A(inst);
+        if(length >= 1) {
+            infoArray[0].regNum = v1; //src
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 2) {
+            infoArray[1].regNum = v2; //src
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 3) {
+            infoArray[2].regNum = v3; //src
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 4) {
+            infoArray[3].regNum = v4; //src
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 5) {
+            infoArray[4].regNum = v5; //src
+            infoArray[4].refCount = 1;
+            infoArray[4].accessType = REGACCESS_U;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = length;
+        codeSize = 3;
+        break;
+    }
+    case OP_FILLED_NEW_ARRAY_RANGE: {//use "length" virtual registers
+        length = INST_AA(inst);
+        u4 vC = (u4)FETCH(2);
+        for(kk = 0; kk < length; kk++) {
+            infoArray[kk].regNum = vC+kk; //src
+            infoArray[kk].refCount = 1;
+            infoArray[kk].accessType = REGACCESS_U;
+            infoArray[kk].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = length;
+        codeSize = 3;
+        break;
+    }
+    case OP_FILL_ARRAY_DATA: //update content of array, read memory
+        vA = INST_AA(inst); //use virtual register, but has side-effect, update memory
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 3;
+        break;
+    case OP_THROW: //update glue->exception
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        codeSize = 1;
+        break;
+    case OP_THROW_VERIFICATION_ERROR:
+        num_regs_per_bytecode = 0;
+        codeSize = 2;
+        break;
+    case OP_GOTO:
+        codeSize = 1;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_GOTO_16:
+        codeSize = 2;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_GOTO_32:
+        codeSize = 3;
+        num_regs_per_bytecode = 0;
+        break;
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+        vA = INST_AA(inst);
+        codeSize = 3;
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 1;
+        break;
+
+    case OP_CMPL_FLOAT: //move 32 bits from memory to lower part of XMM register
+    case OP_CMPG_FLOAT:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        v1_u4 = FETCH(1) & 0xff;
+        v2_u4 = FETCH(1) >> 8;
+        num_regs_per_bytecode = 1;
+        infoArray[0].regNum = v1_u4; //use ss or sd CHECK
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = v2_u4; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 3;
+        num_entry = 2;
+        infoArray[num_entry].regNum = vA; //define
+        infoArray[num_entry].refCount = 1;
+        infoArray[num_entry].accessType = REGACCESS_D;
+        infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_CMPL_DOUBLE: //move 64 bits from memory to lower part of XMM register
+    case OP_CMPG_DOUBLE:
+    case OP_CMP_LONG: //load v1, v1+1, v2, v2+1 to gpr
+        codeSize = 2;
+        vA = INST_AA(inst);
+        v1_u4 = FETCH(1) & 0xff;
+        v2_u4 = FETCH(1) >> 8;
+        num_regs_per_bytecode = 1;
+        if(inst_op == OP_CMP_LONG) {
+            infoArray[0].regNum = v1_u4; //use
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+            infoArray[1].regNum = v1_u4 + 1; //use
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+            infoArray[2].regNum = v2_u4; //use
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+            infoArray[3].regNum = v2_u4 + 1; //use
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+            num_regs_per_bytecode = 5;
+            num_entry = 4;
+            infoArray[num_entry].regNum = vA; //define
+            infoArray[num_entry].refCount = 2;
+            infoArray[num_entry].accessType = REGACCESS_D;
+            infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        }
+        else {
+            infoArray[0].regNum = v1_u4; //use ss or sd CHECK
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm;
+            infoArray[1].regNum = v2_u4; //use
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_xmm;
+            num_regs_per_bytecode = 3;
+            num_entry = 2;
+            infoArray[num_entry].regNum = vA; //define
+            infoArray[num_entry].refCount = 1;
+            infoArray[num_entry].accessType = REGACCESS_D;
+            infoArray[num_entry].physicalType = LowOpndRegType_gp;
+        }
+        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:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vB;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize =12;
+        break;
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = vA; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 1;
+        codeSize = 2;
+        break;
+    case OP_AGET:
+        codeSize = 2;
+    case OP_AGET_WIDE:
+        codeSize = 2;
+    case OP_AGET_OBJECT:
+        codeSize = 2;
+    case OP_AGET_BOOLEAN: //movez 8
+        codeSize = 2;
+    case OP_AGET_BYTE: //moves 8
+        codeSize = 2;
+    case OP_AGET_CHAR: //movez 16
+        codeSize = 2;
+    case OP_AGET_SHORT: //moves 16
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vref = FETCH(1) & 0xff;
+        vindex = FETCH(1) >> 8;
+        if(inst_op == OP_AGET_WIDE) {
+            infoArray[2].regNum = vA;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_xmm; //64, 128 not used in lowering
+        } else {
+            infoArray[2].regNum = vA;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[0].regNum = vref; //use
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vindex; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_APUT:
+    case OP_APUT_WIDE:
+    case OP_APUT_OBJECT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+        vA = INST_AA(inst);
+        vref = FETCH(1) & 0xff;
+        vindex = FETCH(1) >> 8;
+        codeSize = 2;
+        if(inst_op == OP_APUT_WIDE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64, 128 not used in lowering
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[1].regNum = vref; //use
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vindex; //use
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_APUT_OBJECT) {
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+            updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        }
+        num_regs_per_bytecode = 3;
+        break;
+
+    case OP_IGET:
+    case OP_IGET_WIDE:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        if(inst_op == OP_IGET_WIDE || inst_op == OP_IGET_WIDE_QUICK) {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_xmm; //64
+        } else if(inst_op == OP_IGET_WIDE_VOLATILE) {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+            infoArray[2].regNum = vA+1;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_D;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        } else {
+            infoArray[1].regNum = vA;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[0].regNum = vB; //object instance
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        if(inst_op == OP_IGET_WIDE_VOLATILE)
+            num_regs_per_bytecode = 3;
+        else
+            num_regs_per_bytecode = 2;
+        break;
+    case OP_IPUT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        if(inst_op == OP_IPUT_WIDE || inst_op == OP_IPUT_WIDE_QUICK || inst_op == OP_IPUT_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[1].regNum = vB; //object instance
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_SGET:
+    case OP_SGET_WIDE:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        vA = INST_AA(inst);
+        codeSize = 2;
+        if(inst_op == OP_SGET_WIDE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else if(inst_op == OP_SGET_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+            infoArray[1].regNum = vA+1;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_D;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_D;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(inst_op == OP_SGET_WIDE_VOLATILE)
+            num_regs_per_bytecode = 2;
+        else
+            num_regs_per_bytecode = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        break;
+    case OP_SPUT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        vA = INST_AA(inst);
+        codeSize = 2;
+        if(inst_op == OP_SPUT_WIDE || inst_op == OP_SPUT_WIDE_VOLATILE) {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_xmm; //64
+        } else {
+            infoArray[0].regNum = vA;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        num_regs_per_bytecode = 1;
+        break;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_SUPER_QUICK:
+        codeSize = 3;
+        vD = FETCH(2) & 0xf; //object for virtual,direct & interface
+        count = INST_B(inst);
+        vE = (FETCH(2) >> 4) & 0xf;
+        vF = (FETCH(2) >> 8) & 0xf;
+        vG = (FETCH(2) >> 12) & 0xf;
+        vA = INST_A(inst); //5th argument
+        if(count == 0) {
+            if(inst_op == OP_INVOKE_VIRTUAL || inst_op == OP_INVOKE_DIRECT ||
+               inst_op == OP_INVOKE_INTERFACE || inst_op == OP_INVOKE_VIRTUAL_QUICK ||
+               inst_op == OP_INVOKE_SUPER_QUICK) {
+                infoArray[0].regNum = vD;
+                infoArray[0].refCount = 1;
+                infoArray[0].accessType = REGACCESS_U;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                num_regs_per_bytecode = 1;
+            }
+            num_regs_per_bytecode = 0;
+        }
+        else num_regs_per_bytecode = count;
+        if(count >= 1) {
+            infoArray[0].regNum = vD;
+            if(inst_op == OP_INVOKE_VIRTUAL_QUICK ||
+               inst_op == OP_INVOKE_SUPER_QUICK) {
+                infoArray[0].refCount = 2;
+            } else if(inst_op == OP_INVOKE_VIRTUAL || inst_op == OP_INVOKE_DIRECT || inst_op == OP_INVOKE_INTERFACE) {
+                infoArray[0].refCount = 2;
+            } else {
+                infoArray[0].refCount = 1;
+            }
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 2) {
+            infoArray[1].regNum = vE;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 3) {
+            infoArray[2].regNum = vF;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 4) {
+            infoArray[3].regNum = vG;
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(count >= 5) {
+            infoArray[4].regNum = vA;
+            infoArray[4].refCount = 1;
+            infoArray[4].accessType = REGACCESS_U;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        if(inst_op != OP_INVOKE_VIRTUAL_QUICK && inst_op != OP_INVOKE_SUPER_QUICK)
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER_RANGE:
+    case OP_INVOKE_DIRECT_RANGE:
+    case OP_INVOKE_STATIC_RANGE:
+    case OP_INVOKE_INTERFACE_RANGE:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        codeSize = 3;
+        vD = FETCH(2);
+        count = INST_AA(inst);
+        if(count == 0) {
+            if(inst_op == OP_INVOKE_VIRTUAL_RANGE || inst_op == OP_INVOKE_DIRECT_RANGE ||
+               inst_op == OP_INVOKE_INTERFACE_RANGE || inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE ||
+               inst_op == OP_INVOKE_SUPER_QUICK_RANGE) {
+                infoArray[0].regNum = vD;
+                infoArray[0].refCount = 1;
+                infoArray[0].accessType = REGACCESS_U;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+            }
+        }
+        if(count > 0) { //same for count > 10
+            for(kk = 0; kk < count; kk++) {
+                infoArray[kk].regNum = vD+kk; //src
+                if(kk == 0 && (inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE ||
+                               inst_op == OP_INVOKE_SUPER_QUICK_RANGE))
+                    infoArray[kk].refCount = 2;
+                else if(kk == 0 && (inst_op == OP_INVOKE_VIRTUAL_RANGE ||
+                                    inst_op == OP_INVOKE_DIRECT_RANGE ||
+                                    inst_op == OP_INVOKE_INTERFACE_RANGE))
+                    infoArray[kk].refCount = 2;
+                else
+                    infoArray[kk].refCount = 1;
+                infoArray[kk].accessType = REGACCESS_U;
+                infoArray[kk].physicalType = LowOpndRegType_gp;
+            }
+        }
+        if(inst_op != OP_INVOKE_VIRTUAL_QUICK_RANGE && inst_op != OP_INVOKE_SUPER_QUICK_RANGE)
+            updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = count;
+        break;
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+    case OP_NEG_FLOAT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+    case OP_NEG_DOUBLE:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_INT_TO_LONG: //hard-coded registers
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp; //save from %eax
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[2].regNum = vA+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[2].allocConstraints[PhysicalReg_EDX].count = 1;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_INT_TO_FLOAT: //32 to 32
+    case OP_INT_TO_DOUBLE: //32 to 64
+    case OP_LONG_TO_FLOAT: //64 to 32
+    case OP_LONG_TO_DOUBLE: //64 to 64
+    case OP_FLOAT_TO_DOUBLE: //32 to 64
+    case OP_DOUBLE_TO_FLOAT: //64 to 32
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        if(inst_op == OP_INT_TO_DOUBLE || inst_op == OP_LONG_TO_DOUBLE || inst_op == OP_FLOAT_TO_DOUBLE)
+            infoArray[1].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[1].physicalType = LowOpndRegType_fs_s;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_INT_TO_FLOAT || inst_op == OP_INT_TO_DOUBLE || inst_op == OP_FLOAT_TO_DOUBLE)
+            infoArray[0].physicalType = LowOpndRegType_fs_s; //float
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_LONG_TO_INT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        break;
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT: //for reaching-def analysis
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 3;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_fs_s; //store_int_fp_stack_VR
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_DOUBLE_TO_INT)
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs_s;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 3;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_fs;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        if(inst_op == OP_DOUBLE_TO_LONG)
+            infoArray[0].physicalType = LowOpndRegType_fs;
+        else
+            infoArray[0].physicalType = LowOpndRegType_fs_s;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        vA = INST_A(inst); //destination
+        vB = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_DIV_INT:
+    case OP_REM_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1; //for v1
+        if(inst_op == OP_REM_INT)
+            infoArray[2].allocConstraints[PhysicalReg_EDX].count = 1;//vA
+        else
+            infoArray[2].allocConstraints[PhysicalReg_EAX].count = 1;//vA
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2; // in ecx
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_ECX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_MUL_LONG: //used int
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v1+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = v2+1;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_U;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = vA+1;
+        infoArray[5].refCount = 1;
+        infoArray[5].accessType = REGACCESS_D;
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 6;
+        codeSize = 2;
+        break;
+    case OP_DIV_LONG: //v1: xmm v2,vA:
+    case OP_REM_LONG:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA+1;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 5;
+        codeSize = 2;
+        break;
+    case OP_SHL_LONG: //v2: 32, move_ss; v1,vA: xmm CHECK
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        codeSize = 2;
+        break;
+    case OP_SHR_LONG: //v2: 32, move_ss; v1,vA: xmm CHECK
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[2].regNum = v1+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 4;
+        codeSize = 2;
+        break;
+    case OP_USHR_LONG: //v2: move_ss; v1,vA: move_sd
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm; //sd
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss; //ss
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm; //sd
+        num_regs_per_bytecode = 3;
+        codeSize = 2;
+        break;
+    case OP_ADD_FLOAT: //move_ss
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_ss;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_REM_FLOAT: //32 bit GPR, fp_stack for output
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_fs_s;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_ADD_DOUBLE: //move_sd
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+    case OP_REM_DOUBLE: //64 bit XMM, fp_stack for output
+        vA = INST_AA(inst);
+        v1 = *((u1*)rPC + 2);
+        v2 = *((u1*)rPC + 3);
+        codeSize = 2;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_D;
+        infoArray[2].physicalType = LowOpndRegType_fs;
+        infoArray[0].regNum = v1;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 3;
+        break;
+
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 3;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1; //for v1 is vA
+        if(inst_op == OP_REM_INT_2ADDR)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;//vA
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;//vA
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD; //use then define
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].allocConstraints[PhysicalReg_ECX].count = 1; //v2
+        updateCurrentBBWithConstraints(PhysicalReg_ECX);
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_MUL_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        num_regs_per_bytecode = 4;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = v2+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_UD;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA+1;
+        infoArray[3].refCount = 2;
+        infoArray[3].accessType = REGACCESS_UD;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_DIV_LONG_2ADDR: //vA used as xmm, then updated as gps
+    case OP_REM_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 5;
+        codeSize = 1;
+        infoArray[0].regNum = vA;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = v2;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = v2+1;
+        infoArray[2].refCount = 1;
+        infoArray[2].accessType = REGACCESS_U;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = vA;
+        infoArray[3].refCount = 1;
+        infoArray[3].accessType = REGACCESS_D;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = vA+1;
+        infoArray[4].refCount = 1;
+        infoArray[4].accessType = REGACCESS_D;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        break;
+    case OP_SHL_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        infoArray[0].regNum = v2; //ss
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        break;
+    case OP_SHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 3;
+        codeSize = 1;
+        infoArray[0].regNum = v2; //ss
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        infoArray[1].regNum = vA+1;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_U;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = vA;
+        infoArray[2].refCount = 2;
+        infoArray[2].accessType = REGACCESS_UD;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        break;
+    case OP_USHR_LONG_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        num_regs_per_bytecode = 2;
+        codeSize = 1;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss; //ss CHECK
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm; //sd
+        break;
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_ss;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_ss;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_REM_FLOAT_2ADDR: //load vA as GPR, store from fs
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_gp; //CHECK
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_REM_DOUBLE_2ADDR: //load to xmm, store from fs
+        vA = INST_A(inst);
+        v2 = INST_B(inst);
+        codeSize = 1;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 2;
+        infoArray[1].accessType = REGACCESS_UD;
+        infoArray[1].physicalType = LowOpndRegType_xmm; //CHECK
+        infoArray[0].regNum = v2;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        num_regs_per_bytecode = 2;
+        break;
+
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+        vA = INST_A(inst);
+        vB = INST_B(inst);
+        codeSize = 2;
+        tmp_s4 = (s2)FETCH(1);
+        tmp_s2 = tmp_s4;
+        if(tmp_s2 == 0) {
+            num_regs_per_bytecode = 0;
+            break;
+        }
+        infoArray[1].regNum = vA; //in edx for rem, in eax
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB; //in eax
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        if(inst_op == OP_DIV_INT_LIT16) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[1].refCount = 1;
+                break;
+            }
+        }
+        if(tmp_s2 == -1)
+            infoArray[1].refCount = 2;
+        else
+            infoArray[1].refCount = 1;
+        if(inst_op == OP_REM_INT_LIT16)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        infoArray[1].regNum = vA;
+        infoArray[1].refCount = 1;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        break;
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+        codeSize = 2;
+        vA = INST_AA(inst);
+        vB = (u2)FETCH(1) & 0xff;
+        tmp_s2 = (s2)FETCH(1) >> 8;
+        if(tmp_s2 == 0) {
+            num_regs_per_bytecode = 0;
+            break;
+        }
+
+        infoArray[1].regNum = vA;
+        infoArray[1].accessType = REGACCESS_D;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[0].regNum = vB;
+        infoArray[0].refCount = 1;
+        infoArray[0].accessType = REGACCESS_U;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        num_regs_per_bytecode = 2;
+        if(inst_op == OP_DIV_INT_LIT8) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[1].refCount = 1;
+                break;
+            }
+        }
+
+        if(tmp_s2 == -1)
+            infoArray[1].refCount = 2;
+        else
+            infoArray[1].refCount = 1;
+        if(inst_op == OP_REM_INT_LIT8)
+            infoArray[1].allocConstraints[PhysicalReg_EDX].count = 1;
+        else
+            infoArray[1].allocConstraints[PhysicalReg_EAX].count = 1;
+        infoArray[0].allocConstraints[PhysicalReg_EAX].count = 1;
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        break;
+    case OP_EXECUTE_INLINE: //update glue->retval
+    case OP_EXECUTE_INLINE_RANGE:
+        u4 vC;
+        if(inst_op == OP_EXECUTE_INLINE)
+            num = INST_B(inst);
+        else
+            num = INST_AA(inst);
+        if(inst_op == OP_EXECUTE_INLINE) {
+            vC = FETCH(2) & 0xf;
+            vD = (FETCH(2) >> 4) & 0xf;
+            vE = (FETCH(2) >> 8) & 0xf;
+            vF = FETCH(2) >> 12;
+        } else {
+            vC = FETCH(2);
+            vD = vC + 1;
+            vE = vC + 2;
+            vF = vC + 3;
+        }
+        codeSize = 3;
+        if(num >= 1) {
+            infoArray[0].regNum = vC;
+            infoArray[0].refCount = 1;
+            infoArray[0].accessType = REGACCESS_U;
+            infoArray[0].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 2) {
+            infoArray[1].regNum = vD;
+            infoArray[1].refCount = 1;
+            infoArray[1].accessType = REGACCESS_U;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 3) {
+            infoArray[2].regNum = vE;
+            infoArray[2].refCount = 1;
+            infoArray[2].accessType = REGACCESS_U;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 4) {
+            infoArray[3].regNum = vF;
+            infoArray[3].refCount = 1;
+            infoArray[3].accessType = REGACCESS_U;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        updateCurrentBBWithConstraints(PhysicalReg_EAX);
+        updateCurrentBBWithConstraints(PhysicalReg_EDX);
+        num_regs_per_bytecode = num;
+        break;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        codeSize = 3;
+        num_regs_per_bytecode = 0;
+        break;
+#endif
+    }
+    return codeSize;
+}
+//! Updates infoArray(TempRegInfo) with temporaries accessed by INVOKE_NO_RANGE
+
+//!
+int updateInvokeNoRange(TempRegInfo* infoArray, int startInd) {
+    int j = startInd;
+    //invokeMethodNoRange
+    int count = INST_B(inst);
+    if(count == 5) {
+        infoArray[j].regNum = 22;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 4) {
+        infoArray[j].regNum = 23;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 3) {
+        infoArray[j].regNum = 24;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 2) {
+        infoArray[j].regNum = 25;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 1) {
+        infoArray[j].regNum = 26;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    return j;
+}
+//! Updates infoArray(TempRegInfo) with temporaries accessed by INVOKE_RANGE
+
+//! LOOP_COUNT is used to indicate a variable is live through a loop
+int updateInvokeRange(TempRegInfo* infoArray, int startIndex) {
+    int j = startIndex;
+    int count = INST_AA(inst);
+    infoArray[j].regNum = 21;
+    if(count <= 10) {
+        infoArray[j].refCount = 1+count; //DU
+    } else {
+        infoArray[j].refCount = 2+3*LOOP_COUNT;
+    }
+    infoArray[j].physicalType = LowOpndRegType_gp;
+    j++;
+    if(count >= 1 && count <= 10) {
+        infoArray[j].regNum = 22;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 2 && count <= 10) {
+        infoArray[j].regNum = 23;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 3 && count <= 10) {
+        infoArray[j].regNum = 24;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 4 && count <= 10) {
+        infoArray[j].regNum = 25;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 5 && count <= 10) {
+        infoArray[j].regNum = 26;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 6 && count <= 10) {
+        infoArray[j].regNum = 27;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 7 && count <= 10) {
+        infoArray[j].regNum = 28;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 8 && count <= 10) {
+        infoArray[j].regNum = 29;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count >= 9 && count <= 10) {
+        infoArray[j].regNum = 30;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count == 10) {
+        infoArray[j].regNum = 31;
+        infoArray[j].refCount = 2; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    if(count > 10) {
+        //NOTE: inside a loop, LOOP_COUNT can't be 1
+        //      if LOOP_COUNT is 1, it is likely that a logical register is freed inside the loop
+        //         and the next iteration will have incorrect result
+        infoArray[j].regNum = 12;
+        infoArray[j].refCount = 1+3*LOOP_COUNT; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+        infoArray[j].regNum = 13;
+        infoArray[j].refCount = 1+LOOP_COUNT; //DU
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+        infoArray[j].regNum = 14;
+        //MUST be 2, otherwise, transferToState will think its state was in memory
+        infoArray[j].refCount = 2; //DU local
+        infoArray[j].physicalType = LowOpndRegType_gp;
+        j++;
+    }
+    return j;
+}
+
+/* update temporaries used by RETURN bytecodes
+   a temporary is represented by <number, type of the temporary>
+   */
+int updateReturnCommon(TempRegInfo* infoArray) {
+    int numTmps;
+    infoArray[0].regNum = 1;
+    infoArray[0].refCount = 4; //DU
+    infoArray[0].physicalType = LowOpndRegType_scratch;
+    infoArray[1].regNum = 2;
+    infoArray[1].refCount = 2; //DU
+    infoArray[1].physicalType = LowOpndRegType_scratch;
+    infoArray[2].regNum = PhysicalReg_EAX;
+    infoArray[2].refCount = 5; //DU
+    infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+    infoArray[3].regNum = 1;
+#if defined(ENABLE_TRACING)//WITH_DEBUGGER is true WITH_PROFILER can be false
+    infoArray[3].refCount = 6+4;
+#else
+    infoArray[3].refCount = 6; //DU
+#endif
+    infoArray[3].physicalType = LowOpndRegType_gp;
+    infoArray[4].regNum = 2;
+    infoArray[4].refCount = 4; //DU
+    infoArray[4].physicalType = LowOpndRegType_gp;
+    infoArray[5].regNum = 5;
+    infoArray[5].refCount = 2; //DU
+    infoArray[5].physicalType = LowOpndRegType_gp;
+    infoArray[6].regNum = 10;
+    infoArray[6].refCount = 3;
+    infoArray[6].physicalType = LowOpndRegType_gp;
+    infoArray[7].regNum = 6;
+    infoArray[7].refCount = 4; //DU
+    infoArray[7].physicalType = LowOpndRegType_gp;
+    infoArray[8].regNum = 3;
+    infoArray[8].refCount = 3;
+    infoArray[8].physicalType = LowOpndRegType_gp;
+    infoArray[9].regNum = 7;
+    infoArray[9].refCount = 2; //DU
+    infoArray[9].physicalType = LowOpndRegType_gp;
+    numTmps = 12;
+#if defined(ENABLE_TRACING)
+    infoArray[12].regNum = 4;
+    infoArray[12].refCount = 3; //DU
+    infoArray[12].physicalType = LowOpndRegType_gp;
+    infoArray[13].regNum = 3;
+    infoArray[13].refCount = 2; //DU
+    infoArray[13].physicalType = LowOpndRegType_scratch;
+    infoArray[14].regNum = 15;
+    infoArray[14].refCount = 2; //DU
+    infoArray[14].physicalType = LowOpndRegType_gp;
+    infoArray[15].regNum = 16;
+    infoArray[15].refCount = 2; //DU
+    infoArray[15].physicalType = LowOpndRegType_gp;
+    infoArray[16].regNum = PhysicalReg_EDX;
+    infoArray[16].refCount = 2; //DU
+    infoArray[16].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+    infoArray[17].regNum = 6;
+    infoArray[17].refCount = 2; //DU
+    infoArray[17].physicalType = LowOpndRegType_scratch;
+    numTmps = 18;
+#endif
+    infoArray[10].regNum = 14;
+    infoArray[10].refCount = 2; //DU
+    infoArray[10].physicalType = LowOpndRegType_gp;
+    infoArray[11].regNum = 4;
+    infoArray[11].refCount = 2; //DU
+    infoArray[11].physicalType = LowOpndRegType_scratch;
+#ifdef DEBUG_CALL_STACK
+    infoArray[numTmps].regNum = 5;
+    infoArray[numTmps].refCount = 2;
+    infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+    numTmps++;
+#endif
+    infoArray[numTmps].regNum = PhysicalReg_EBX;
+    /* used to hold chaining cell
+       updated to be returnAddr
+       then conditionally updated to zero
+       used to update inJitCodeCache
+       compare against zero to determine whether to jump to native code
+       jump to native code (%ebx)
+    */
+    infoArray[numTmps].refCount = 3+1+1;
+    infoArray[numTmps].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+    numTmps++;
+    infoArray[numTmps].regNum = 17;
+    infoArray[numTmps].refCount = 2; //DU
+    infoArray[numTmps].physicalType = LowOpndRegType_gp;
+    numTmps++;
+    infoArray[numTmps].regNum = 7;
+    infoArray[numTmps].refCount = 4; //DU
+    infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+    numTmps++;
+    return numTmps;
+}
+
+/* update temporaries used by predicted INVOKE_VIRTUAL & INVOKE_INTERFACE */
+int updateGenPrediction(TempRegInfo* infoArray, bool isInterface) {
+    infoArray[0].regNum = 40;
+    infoArray[0].physicalType = LowOpndRegType_gp;
+    infoArray[1].regNum = 41;
+    infoArray[1].physicalType = LowOpndRegType_gp;
+    infoArray[2].regNum = 32;
+    infoArray[2].refCount = 2;
+    infoArray[2].physicalType = LowOpndRegType_gp;
+
+    if(isInterface) {
+        infoArray[0].refCount = 2+2;
+        infoArray[1].refCount = 3+2-1; //for temp41, -1 for gingerbread
+        infoArray[3].regNum = 33;
+        infoArray[3].refCount = 4+1;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = PhysicalReg_EAX;
+        infoArray[4].refCount = 5;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = PhysicalReg_ECX;
+        infoArray[5].refCount = 1+1+2; //used in ArgsDone (twice)
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = 10;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2;
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = 8;
+        infoArray[8].refCount = 2;
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = PhysicalReg_EDX; //space holder
+        infoArray[9].refCount = 1;
+        infoArray[9].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[10].regNum = 43;
+        infoArray[10].refCount = 3;
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 44;
+        infoArray[11].refCount = 3;
+        infoArray[11].physicalType = LowOpndRegType_gp;
+        infoArray[12].regNum = 45;
+        infoArray[12].refCount = 2;
+        infoArray[12].physicalType = LowOpndRegType_gp;
+        infoArray[13].regNum = 7;
+        infoArray[13].refCount = 4;
+        infoArray[13].physicalType = LowOpndRegType_scratch;
+        return 14;
+    } else { //virtual or virtual_quick
+        infoArray[0].refCount = 2+2;
+        infoArray[1].refCount = 3+2-2; //for temp41, -2 for gingerbread
+        infoArray[2].refCount++; //for temp32 gingerbread
+        infoArray[3].regNum = 33;
+        infoArray[3].refCount = 4+1;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 34;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EAX;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX;
+        infoArray[6].refCount = 1+3+2;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = 10;
+        infoArray[7].refCount = 2;
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = PhysicalReg_EDX; //space holder
+        infoArray[8].refCount = 1;
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[9].regNum = 43;
+        infoArray[9].refCount = 3;
+        infoArray[9].physicalType = LowOpndRegType_gp;
+        infoArray[10].regNum = 44;
+        infoArray[10].refCount = 3;
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 7;
+        infoArray[11].refCount = 4;
+        infoArray[11].physicalType = LowOpndRegType_scratch;
+        return 12;
+    }
+}
+
+int updateMarkCard(TempRegInfo* infoArray, int j1/*valReg*/,
+                    int j2/*tgtAddrReg*/, int j3/*scratchReg*/) {
+    infoArray[j3].regNum = 11;
+    infoArray[j3].physicalType = LowOpndRegType_gp;
+    infoArray[j3].refCount = 3;
+    infoArray[j3].is8Bit = true;
+    infoArray[j1].refCount++;
+    infoArray[j2].refCount += 2;
+    infoArray[j3+1].regNum = 6;
+    infoArray[j3+1].physicalType = LowOpndRegType_scratch;
+    infoArray[j3+1].refCount = 2;
+    return j3+2;
+}
+
+int updateMarkCard_notNull(TempRegInfo* infoArray,
+                           int j2/*tgtAddrReg*/, int j3/*scratchReg*/) {
+    infoArray[j3].regNum = 11;
+    infoArray[j3].physicalType = LowOpndRegType_gp;
+    infoArray[j3].refCount = 3;
+    infoArray[j3].is8Bit = true;
+    infoArray[j2].refCount += 2;
+    infoArray[j3+1].regNum = 2;
+    infoArray[j3+1].refCount = 2; //DU
+    infoArray[j3+1].physicalType = LowOpndRegType_scratch;
+    return j3+2;
+}
+
+int iget_obj_inst = -1;
+//! This function updates infoArray with temporaries accessed when lowering the bytecode
+
+//! returns the number of temporaries
+int getTempRegInfo(TempRegInfo* infoArray) { //returns an array of TempRegInfo
+    int k;
+    int numTmps;
+    for(k = 0; k < MAX_TEMP_REG_PER_BYTECODE; k++) {
+        infoArray[k].linkageToVR = -1;
+        infoArray[k].versionNum = 0;
+        infoArray[k].shareWithVR = true;
+        infoArray[k].is8Bit = false;
+    }
+    u2 vA, v1, length, num, tmp;
+    u2 inst_op = INST_INST(inst);
+    s2 tmp_s2;
+    s4 tmp_s4;
+    switch(inst_op) {
+    case OP_APUT_BYTE:
+        for(k = 0; k < MAX_TEMP_REG_PER_BYTECODE; k++)
+            infoArray[k].shareWithVR = true; //false;
+        break;
+    }
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return 0;
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        return 1;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        return 1;
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        return 2;
+    case OP_MOVE_RESULT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        return 2;
+    case OP_MOVE_EXCEPTION:
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 3; //DUU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+    case OP_CONST_HIGH16:
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+        return 0;
+    case OP_CONST_STRING: //hardcode %eax
+    case OP_CONST_STRING_JUMBO:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 4;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 4;
+    case OP_CONST_CLASS:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 4;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 4;
+
+    case OP_MONITOR_ENTER:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_MONITOR_EXIT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = PhysicalReg_EDX;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 3;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        return 6;
+    case OP_CHECK_CAST:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 4;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 6;
+        infoArray[2].refCount = 3; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+
+        infoArray[5].regNum = PhysicalReg_EAX;
+        /* %eax has 3 live ranges
+           1> 5 accesses: to resolve the class object
+           2> call dvmInstanceofNonTrivial to define %eax, then use it once
+           3> move exception object to %eax, then jump to throw_exception
+           if WITH_JIT is true, the first live range has 6 accesses
+        */
+        infoArray[5].refCount = 6;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_EDX;
+        infoArray[6].refCount = 2; //export_pc
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_ECX;
+        infoArray[7].refCount = 1;
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[8].regNum = 3;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        return 9;
+    case OP_INSTANCE_OF:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 4; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 4;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 6;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = PhysicalReg_EDX;
+        infoArray[8].refCount = 2; //export_pc for class_resolve
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 9;
+
+    case OP_ARRAY_LENGTH:
+        vA = INST_A(inst);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].linkageToVR = vA;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_NEW_INSTANCE:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //6: class object
+        //3: defined by C function, used twice
+        infoArray[0].refCount = 6; //next version has 3 references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_ECX; //before common_throw_message
+        infoArray[1].refCount = 1;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].is8Bit = true;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+
+        infoArray[8].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[8].refCount = 2;
+        infoArray[8].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[9].regNum = 4;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        return 10;
+
+    case OP_NEW_ARRAY:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice
+        infoArray[0].refCount = 4; //next version has 3 references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 3;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 4;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        return 8;
+
+    case OP_FILLED_NEW_ARRAY:
+        length = INST_B(inst);
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice (array object)
+        //length: access array object to update the content
+        infoArray[0].refCount = 4; //next version has 5+length references
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 8; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[4].is8Bit = true;
+
+        if(length >= 1) {
+            infoArray[5].regNum = 7;
+            infoArray[5].refCount = 2; //DU
+            infoArray[5].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 2) {
+            infoArray[6].regNum = 8;
+            infoArray[6].refCount = 2; //DU
+            infoArray[6].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 3) {
+            infoArray[7].regNum = 9;
+            infoArray[7].refCount = 2; //DU
+            infoArray[7].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 4) {
+            infoArray[8].regNum = 10;
+            infoArray[8].refCount = 2; //DU
+            infoArray[8].physicalType = LowOpndRegType_gp;
+        }
+        if(length >= 5) {
+            infoArray[9].regNum = 11;
+            infoArray[9].refCount = 2; //DU
+            infoArray[9].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[5+length].regNum = 1;
+        infoArray[5+length].refCount = 2; //DU
+        infoArray[5+length].physicalType = LowOpndRegType_scratch;
+        infoArray[6+length].regNum = 2;
+        infoArray[6+length].refCount = 4; //DU
+        infoArray[6+length].physicalType = LowOpndRegType_scratch;
+        infoArray[7+length].regNum = 3;
+        infoArray[7+length].refCount = 2; //DU
+        infoArray[7+length].physicalType = LowOpndRegType_scratch;
+        infoArray[8+length].regNum = 4;
+        infoArray[8+length].refCount = 5; //DU
+        infoArray[8+length].physicalType = LowOpndRegType_scratch;
+        return 9+length;
+
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        length = INST_AA(inst);
+        infoArray[0].regNum = PhysicalReg_EAX;
+        //4: class object
+        //3: defined by C function, used twice (array object)
+        //if length is 0, no access to array object
+        //else, used inside a loop
+        infoArray[0].refCount = 4; //next version: 5+(length >= 1 ? LOOP_COUNT : 0)
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 8; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[4].is8Bit = true;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 4; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        infoArray[7].regNum = 3;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+
+        infoArray[8].regNum = 7;
+        infoArray[8].refCount = 3*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[8].physicalType = LowOpndRegType_gp;
+        infoArray[9].regNum = 8;
+        infoArray[9].refCount = 3*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[9].physicalType = LowOpndRegType_gp;
+        infoArray[10].regNum = 9;
+        infoArray[10].refCount = 2*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[10].physicalType = LowOpndRegType_gp;
+        infoArray[11].regNum = 10;
+        infoArray[11].refCount = 2*(length >= 1 ? LOOP_COUNT : 0);
+        infoArray[11].physicalType = LowOpndRegType_gp;
+        infoArray[12].regNum = 4;
+        infoArray[12].refCount = 5; //DU
+        infoArray[12].physicalType = LowOpndRegType_scratch;
+        return 13;
+
+    case OP_FILL_ARRAY_DATA:
+        infoArray[0].regNum = PhysicalReg_EAX;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+#if 0//def HARDREG_OPT
+        infoArray[1].refCount = 3; //next version has refCount of 2
+#else
+        infoArray[1].refCount = 5;
+#endif
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum =1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        return 5;
+
+    case OP_THROW:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX; //before common_throw_message
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        return 4;
+    case OP_THROW_VERIFICATION_ERROR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX; //export_pc
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        infoArray[3].regNum = 2;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        return 4;
+
+    case OP_GOTO: //called function common_periodicChecks4
+#if defined(ENABLE_TRACING)
+        tt = INST_AA(inst);
+        tmp_s2 = (s2)((s2)tt << 8) >> 8;
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_GOTO_16:
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_GOTO_32:
+#if defined(ENABLE_TRACING)
+        tmp_u4 = (u4)FETCH(1);
+        tmp_u4 |= (u4)FETCH(2) << 16;
+        if(((s4)tmp_u4) < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[1].regNum = PhysicalReg_EDX;
+            infoArray[1].refCount = 2;
+            infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 2;
+        }
+#endif
+        return 1;
+    case OP_IF_EQZ: //called function common_periodicChecks4
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+#if defined(ENABLE_TRACING)
+        tmp_s2 = (s2)FETCH(1);
+        if(tmp_s2 < 0) {
+            infoArray[0].regNum = PhysicalReg_EDX;
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+#endif
+        return 0;
+    case OP_PACKED_SWITCH: //jump common_backwardBranch, which calls common_periodicChecks_entry, then jump_reg %eax
+    case OP_SPARSE_SWITCH: //%edx, %eax
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 6;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = PhysicalReg_EAX; //return by dvm helper
+        infoArray[2].refCount = 2+1; //2 uses
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        infoArray[4].regNum = 2;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        return 5;
+
+    case OP_AGET:
+    case OP_AGET_OBJECT:
+    case OP_AGET_BOOLEAN:
+    case OP_AGET_BYTE:
+    case OP_AGET_CHAR:
+    case OP_AGET_SHORT:
+        vA = INST_AA(inst);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].linkageToVR = vA;
+        if(inst_op == OP_AGET_BYTE || inst_op == OP_AGET_BOOLEAN)
+            infoArray[3].is8Bit = true;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_AGET_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+
+    case OP_APUT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_APUT_BYTE || inst_op == OP_APUT_BOOLEAN)
+            infoArray[3].is8Bit = true;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_APUT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+    case OP_APUT_OBJECT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 5+1; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2; //live through function call dvmCanPut
+        infoArray[1].refCount = 3+1; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 4+1; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 6;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+
+        infoArray[6].regNum = PhysicalReg_EDX;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[0].shareWithVR = false;
+        return updateMarkCard_notNull(infoArray,
+                                      0/*index for tgtAddrReg*/, 9);
+
+    case OP_IGET:
+    case OP_IGET_OBJECT:
+    case OP_IGET_VOLATILE:
+    case OP_IGET_OBJECT_VOLATILE:
+    case OP_IGET_BOOLEAN:
+    case OP_IGET_BYTE:
+    case OP_IGET_CHAR:
+    case OP_IGET_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+#ifdef DEBUG_IGET_OBJ
+        //add hack for a specific instance (iget_obj_inst) of IGET_OBJECT within a method
+        if(inst_op == OP_IGET_OBJECT && !strncmp(currentMethod->clazz->descriptor, "Lspec/benchmarks/_228_jack/Parse", 32) &&
+           !strncmp(currentMethod->name, "buildPhase3", 11))
+        {
+#if 0
+          if(iget_obj_inst == 12) {
+            LOGD("increase count for instance %d of %s %s", iget_obj_inst, currentMethod->clazz->descriptor, currentMethod->name);
+            infoArray[5].refCount = 4; //DU
+          }
+          else
+#endif
+            infoArray[5].refCount = 3;
+          iget_obj_inst++;
+        }
+        else
+          infoArray[5].refCount = 3;
+#else
+        infoArray[5].refCount = 3; //DU
+#endif
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp;
+        return 8;
+    case OP_IPUT:
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_VOLATILE:
+    case OP_IPUT_OBJECT_VOLATILE:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+        infoArray[5].refCount = 3; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 9;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp;
+        if(inst_op == OP_IPUT_OBJECT || inst_op == OP_IPUT_OBJECT_VOLATILE) {
+            infoArray[5].shareWithVR = false;
+            return updateMarkCard(infoArray, 7/*index for valReg*/,
+                                  5/*index for tgtAddrReg*/, 8);
+        }
+        return 8;
+    case OP_IGET_WIDE:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_WIDE_VOLATILE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 3; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 3;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 7;
+        infoArray[5].refCount = 3; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        infoArray[6].regNum = 8;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp;
+        infoArray[7].regNum = 1;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_xmm;
+
+        if(inst_op == OP_IPUT_WIDE_VOLATILE || inst_op == OP_IGET_WIDE_VOLATILE) {
+            infoArray[8].regNum = 3;
+            infoArray[8].refCount = 2; //DU
+            infoArray[8].physicalType = LowOpndRegType_scratch;
+            infoArray[9].regNum = 9;
+            infoArray[9].refCount = 2; //DU
+            infoArray[9].physicalType = LowOpndRegType_gp;
+            return 10;
+        }
+        return 8;
+
+    case OP_SGET:
+    case OP_SGET_OBJECT:
+    case OP_SGET_VOLATILE:
+    case OP_SGET_OBJECT_VOLATILE:
+    case OP_SGET_BOOLEAN:
+    case OP_SGET_BYTE:
+    case OP_SGET_CHAR:
+    case OP_SGET_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 7;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 6;
+    case OP_SPUT:
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_VOLATILE:
+    case OP_SPUT_OBJECT_VOLATILE:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2+1; //access clazz of the field
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 7;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_SPUT_OBJECT || inst_op == OP_SPUT_OBJECT_VOLATILE) {
+            infoArray[2].shareWithVR = false;
+            infoArray[6].regNum = 12;
+            infoArray[6].refCount = 1; //1 def, 2 uses in updateMarkCard
+            infoArray[6].physicalType = LowOpndRegType_gp;
+            return updateMarkCard(infoArray, 4/*index for valReg*/,
+                                  6/*index for tgtAddrReg */, 7);
+        }
+        return 6;
+    case OP_SGET_WIDE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_WIDE_VOLATILE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_scratch;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_scratch;
+
+        infoArray[2].regNum = PhysicalReg_EAX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_xmm;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        if(inst_op == OP_SPUT_WIDE_VOLATILE || inst_op == OP_SGET_WIDE_VOLATILE) {
+            infoArray[6].regNum = 3;
+            infoArray[6].refCount = 2; //DU
+            infoArray[6].physicalType = LowOpndRegType_scratch;
+            infoArray[7].regNum = 9;
+            infoArray[7].refCount = 2; //DU
+            infoArray[7].physicalType = LowOpndRegType_gp;
+            return 8;
+        }
+        return 6;
+
+    case OP_IGET_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_IPUT_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_IPUT_OBJECT_QUICK) {
+            infoArray[0].shareWithVR = false;
+            return updateMarkCard(infoArray, 1/*index for valReg*/,
+                                  0/*index for tgtAddrReg*/, 3);
+        }
+        return 3;
+    case OP_IGET_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+    case OP_IPUT_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return updateReturnCommon(infoArray);
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        numTmps = updateReturnCommon(infoArray);
+
+        infoArray[numTmps].regNum = 21;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        infoArray[numTmps].regNum = 22;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        return numTmps;
+    case OP_RETURN_WIDE:
+        numTmps = updateReturnCommon(infoArray);
+
+        infoArray[numTmps].regNum = 10;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_scratch;
+        numTmps++;
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 2; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_xmm;
+        numTmps++;
+        return numTmps;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_VIRTUAL_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, false /*not interface*/);
+        infoArray[numTmps].regNum = 5;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_VIRTUAL)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 7;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 8;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 3; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //2 versions, first version DU is for exception, 2nd version: eip right before jumping to invokeArgsDone
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX; //ecx is ued in invokeArgsDone
+        infoArray[6].refCount = 1+1; //used in .invokeArgsDone
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        //when WITH_JIT is true and PREDICTED_CHAINING is false
+        //  temp 8 and EAX are not used; but it is okay to keep it here
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 4; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 2;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_VIRTUAL)
+            k = updateInvokeNoRange(infoArray, 10);
+        else
+            k = updateInvokeRange(infoArray, 10);
+        return k;
+#endif
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 7;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 8;
+        infoArray[2].refCount = 3; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 9;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+
+        infoArray[5].regNum = PhysicalReg_EDX;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_ECX;
+        infoArray[6].refCount = 1+1; //DU
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[7].regNum = PhysicalReg_EAX;
+        infoArray[7].refCount = 4; //DU
+        infoArray[7].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[8].regNum = 1;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 2;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        infoArray[10].regNum = 3;
+        infoArray[10].refCount = 2; //DU
+        infoArray[10].physicalType = LowOpndRegType_scratch;
+        infoArray[11].regNum = 4;
+        infoArray[11].refCount = 2; //DU
+        infoArray[11].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_SUPER)
+            k = updateInvokeNoRange(infoArray, 12);
+        else
+            k = updateInvokeRange(infoArray, 12);
+        return k;
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 5;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+
+        infoArray[2].regNum = PhysicalReg_EDX;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EAX;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2; //DU
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_DIRECT)
+            k = updateInvokeNoRange(infoArray, 7);
+        else
+            k = updateInvokeRange(infoArray, 7);
+        return k;
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+        infoArray[0].regNum = 3;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[2].regNum = PhysicalReg_ECX;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = 2;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_STATIC)
+            k = updateInvokeNoRange(infoArray, 6);
+        else
+            k = updateInvokeRange(infoArray, 6);
+        return k;
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, true /*interface*/);
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_INTERFACE)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 3;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 4;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = PhysicalReg_ECX;
+        infoArray[5].refCount = 1+1; //DU
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 2+1; //2 uses
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[7].regNum = 1;
+        infoArray[7].refCount = 2; //DU
+        infoArray[7].physicalType = LowOpndRegType_scratch;
+        infoArray[8].regNum = 2;
+        infoArray[8].refCount = 2; //DU
+        infoArray[8].physicalType = LowOpndRegType_scratch;
+        infoArray[9].regNum = 3;
+        infoArray[9].refCount = 2; //DU
+        infoArray[9].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_INTERFACE)
+            k = updateInvokeNoRange(infoArray, 10);
+        else
+            k = updateInvokeRange(infoArray, 10);
+        return k;
+#endif
+        ////////////////////////////////////////////// ALU
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+    case OP_NEG_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_NOT_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_NEG_FLOAT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+    case OP_NEG_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_INT_TO_LONG: //hard-code eax & edx
+        infoArray[0].regNum = PhysicalReg_EAX;
+        infoArray[0].refCount = 2+1;
+        infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 1+1; //cdq accesses edx & eax
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;
+    case OP_INT_TO_FLOAT:
+    case OP_INT_TO_DOUBLE:
+    case OP_LONG_TO_FLOAT:
+    case OP_LONG_TO_DOUBLE:
+    case OP_FLOAT_TO_DOUBLE:
+    case OP_DOUBLE_TO_FLOAT:
+        return 0; //fp stack
+    case OP_LONG_TO_INT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        return 1;
+    case OP_FLOAT_TO_INT:
+    case OP_DOUBLE_TO_INT: //fp stack
+        return 0;
+    case OP_FLOAT_TO_LONG:
+    case OP_DOUBLE_TO_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+    case OP_INT_TO_BYTE:
+    case OP_INT_TO_CHAR:
+    case OP_INT_TO_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //define, update, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        return 1;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        if(inst_op == OP_ADD_INT || inst_op == OP_SUB_INT || inst_op == OP_MUL_INT ||
+           inst_op == OP_AND_INT || inst_op == OP_OR_INT || inst_op == OP_XOR_INT) {
+            vA = INST_AA(inst);
+            v1 = *((u1*)rPC + 2);
+        } else {
+            vA = INST_A(inst);
+            v1 = vA;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        return 1; //common_alu_int
+
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR: //use %cl or %ecx?
+        if(inst_op == OP_SHL_INT || inst_op == OP_SHR_INT || inst_op == OP_USHR_INT) {
+            vA = INST_AA(inst);
+            v1 = *((u1*)rPC + 2);
+        } else {
+            vA = INST_A(inst);
+            v1 = vA;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = PhysicalReg_ECX;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;//common_shift_int
+
+    case OP_DIV_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT_2ADDR:
+    case OP_REM_INT_2ADDR: //hard-code %eax, %edx (dividend in edx:eax; quotient in eax; remainder in edx)
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 4; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX; //dividend, quotient
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = PhysicalReg_EDX; //export_pc, output for REM
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //define, use
+        infoArray[3].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_DIV_INT || inst_op == OP_DIV_INT_2ADDR) {
+            infoArray[1].refCount = 5;
+            infoArray[2].refCount = 4;
+        } else {
+            infoArray[1].refCount = 4;
+            infoArray[2].refCount = 5;
+        }
+        return 4;
+
+    case OP_ADD_INT_LIT16:
+    case OP_MUL_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+    case OP_ADD_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+        if(inst_op == OP_ADD_INT_LIT16 || inst_op == OP_MUL_INT_LIT16 ||
+           inst_op == OP_AND_INT_LIT16 || inst_op == OP_OR_INT_LIT16 || inst_op == OP_XOR_INT_LIT16) {
+            vA = INST_A(inst);
+            v1 = INST_B(inst);
+        } else {
+            vA = INST_AA(inst);
+            v1 = (u2)FETCH(1) & 0xff;
+        }
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        return 1;
+
+    case OP_RSUB_INT_LIT8:
+    case OP_RSUB_INT:
+        vA = INST_AA(inst);
+        v1 = (inst_op == OP_RSUB_INT) ? INST_B(inst) : ((u2)FETCH(1) & 0xff);
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        if(vA != v1)
+            infoArray[1].shareWithVR = false;
+        return 2;
+
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+        if(inst_op == OP_DIV_INT_LIT8 || inst_op == OP_REM_INT_LIT8) {
+            tmp_s2 = (s2)FETCH(1) >> 8;
+        }
+        else {
+            tmp_s4 = (s2)FETCH(1);
+            tmp_s2 = tmp_s4;
+        }
+        if((inst_op == OP_DIV_INT_LIT8 || inst_op == OP_DIV_INT_LIT16)) {
+            int power = isPowerOfTwo(tmp_s2);
+            if(power >= 1) { /* divide by a power of 2 constant */
+                infoArray[0].regNum = 2;
+                infoArray[0].refCount = 3; //define, use, use
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 1;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                if(power == 1) infoArray[1].refCount = 5;
+                else infoArray[1].refCount = 6;
+                return 2;
+            }
+        }
+        if(tmp_s2 == 0) {
+            //export_pc
+            infoArray[0].regNum = PhysicalReg_EDX; //export_pc, output for REM
+            infoArray[0].refCount = 2;
+            infoArray[0].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+            return 1;
+        }
+        if(inst_op == OP_DIV_INT_LIT16 || inst_op == OP_DIV_INT_LIT8) {
+            if(tmp_s2 == -1)
+                infoArray[1].refCount = 4+1;
+            else
+                infoArray[1].refCount = 4;
+            infoArray[2].refCount = 2; //edx
+        } else {
+            if(tmp_s2 == -1)
+                infoArray[1].refCount = 3+1;
+            else
+                infoArray[1].refCount = 3;
+            infoArray[2].refCount = 3; //edx
+        }
+        infoArray[0].regNum = 2;
+        infoArray[0].refCount = 2; //define, use
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EAX; //dividend, quotient
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = PhysicalReg_EDX; //export_pc, output for REM
+        infoArray[2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 3;
+
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //define, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+
+    case OP_SHL_LONG:
+    case OP_SHL_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+
+    case OP_SHR_LONG:
+    case OP_SHR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 3;
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 3;
+        infoArray[4].physicalType = LowOpndRegType_xmm;
+        return 5;
+
+    case OP_USHR_LONG:
+    case OP_USHR_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //define, update, use
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //define, update, use
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[1].shareWithVR = false;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //define, use
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        return 3;
+
+    case OP_MUL_LONG: //general purpose register
+    case OP_MUL_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 6;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2+1; //for mul_opc
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //for mul_opc
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 5;
+
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_xmm;
+        infoArray[3].regNum = PhysicalReg_EAX;
+        infoArray[3].refCount = 2; //defined by function call
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2; //next version has 2 references
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        return 6;
+
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_ADD_DOUBLE: //PhysicalReg_FP TODO
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_FLOAT:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_DIV_DOUBLE:
+    case OP_DIV_DOUBLE_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        //for ALU ops with 2ADDR, the temp variable can share the same physical
+        //reg as the virtual register, since the content of VR is updated by
+        //the content of the temp variable
+        if(inst_op == OP_ADD_FLOAT || inst_op == OP_SUB_FLOAT ||
+           inst_op == OP_MUL_FLOAT || inst_op == OP_ADD_DOUBLE ||
+           inst_op == OP_SUB_DOUBLE || inst_op == OP_MUL_DOUBLE ||
+           inst_op == OP_DIV_FLOAT || inst_op == OP_DIV_DOUBLE)
+        infoArray[0].shareWithVR = false;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_REM_FLOAT:
+    case OP_REM_FLOAT_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_REM_DOUBLE:
+    case OP_REM_DOUBLE_2ADDR:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        infoArray[2].regNum = 1;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_scratch;
+        return 3;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPL_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 2;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 4; //return
+        infoArray[4].refCount = 5;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        return 5;
+
+    case OP_CMPG_FLOAT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 5;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        return 4;
+        break;
+    case OP_CMPG_DOUBLE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_xmm;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 2;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 3;
+        infoArray[3].refCount = 5;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        return 4;
+
+    case OP_CMP_LONG:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 3;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 3;
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 5;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 6;
+        infoArray[5].refCount = 7;
+        infoArray[5].physicalType = LowOpndRegType_gp;
+        return 6;
+
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+        if(inst_op == OP_EXECUTE_INLINE)
+            num = INST_B(inst);
+        else
+            num = INST_AA(inst);
+        tmp = FETCH(1);
+        switch (tmp) {
+            case INLINE_STRING_LENGTH:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 1;
+                infoArray[3].refCount = 2;
+                infoArray[3].physicalType = LowOpndRegType_scratch;
+                return 4;
+            case INLINE_STRING_IS_EMPTY:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 4;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 1;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_scratch;
+                return 3;
+            case INLINE_STRING_FASTINDEXOF_II:
+#if defined(USE_GLOBAL_STRING_DEFS)
+                break;
+#else
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 14 * LOOP_COUNT;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3 * LOOP_COUNT;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 11 * LOOP_COUNT;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 4;
+                infoArray[3].refCount = 3 * LOOP_COUNT;
+                infoArray[3].physicalType = LowOpndRegType_gp;
+                infoArray[4].regNum = 5;
+                infoArray[4].refCount = 9 * LOOP_COUNT;
+                infoArray[4].physicalType = LowOpndRegType_gp;
+                infoArray[5].regNum = 6;
+                infoArray[5].refCount = 4 * LOOP_COUNT;
+                infoArray[5].physicalType = LowOpndRegType_gp;
+                infoArray[6].regNum = 7;
+                infoArray[6].refCount = 2;
+                infoArray[6].physicalType = LowOpndRegType_gp;
+                infoArray[7].regNum = 1;
+                infoArray[7].refCount = 2;
+                infoArray[7].physicalType = LowOpndRegType_scratch;
+                return 8;
+#endif
+            case INLINE_MATH_ABS_LONG:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 7;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                infoArray[3].regNum = 4;
+                infoArray[3].refCount = 3;
+                infoArray[3].physicalType = LowOpndRegType_gp;
+                infoArray[4].regNum = 5;
+                infoArray[4].refCount = 2;
+                infoArray[4].physicalType = LowOpndRegType_gp;
+                infoArray[5].regNum = 6;
+                infoArray[5].refCount = 5;
+                infoArray[5].physicalType = LowOpndRegType_gp;
+                return 6;
+            case INLINE_MATH_ABS_INT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 5;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 4;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_MATH_MAX_INT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 4;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 2;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_MATH_ABS_FLOAT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 3;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_MATH_ABS_DOUBLE:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 3;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_FLOAT_TO_RAW_INT_BITS:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_INT_BITS_TO_FLOAT:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                return 2;
+            case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            case INLINE_LONG_BITS_TO_DOUBLE:
+                infoArray[0].regNum = 1;
+                infoArray[0].refCount = 2;
+                infoArray[0].physicalType = LowOpndRegType_gp;
+                infoArray[1].regNum = 2;
+                infoArray[1].refCount = 2;
+                infoArray[1].physicalType = LowOpndRegType_gp;
+                infoArray[2].regNum = 3;
+                infoArray[2].refCount = 3;
+                infoArray[2].physicalType = LowOpndRegType_gp;
+                return 3;
+            default:
+                break;
+        }
+
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 4;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        if(num >= 1) {
+            infoArray[1].regNum = 2;
+            infoArray[1].refCount = 2;
+            infoArray[1].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 2) {
+            infoArray[2].regNum = 3;
+            infoArray[2].refCount = 2;
+            infoArray[2].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 3) {
+            infoArray[3].regNum = 4;
+            infoArray[3].refCount = 2;
+            infoArray[3].physicalType = LowOpndRegType_gp;
+        }
+        if(num >= 4) {
+            infoArray[4].regNum = 5;
+            infoArray[4].refCount = 2;
+            infoArray[4].physicalType = LowOpndRegType_gp;
+        }
+        infoArray[num+1].regNum = 6;
+        infoArray[num+1].refCount = 2;
+        infoArray[num+1].physicalType = LowOpndRegType_gp;
+        infoArray[num+2].regNum = PhysicalReg_EAX;
+        infoArray[num+2].refCount = 2;
+        infoArray[num+2].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[num+3].regNum = PhysicalReg_EDX;
+        infoArray[num+3].refCount = 2;
+        infoArray[num+3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[num+4].regNum = 1;
+        infoArray[num+4].refCount = 4;
+        infoArray[num+4].physicalType = LowOpndRegType_scratch;
+        return num+5;
+#if FIXME
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return 0;
+#endif
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+#ifdef PREDICTED_CHAINING
+        numTmps = updateGenPrediction(infoArray, false /*not interface*/);
+        infoArray[numTmps].regNum = 1;
+        infoArray[numTmps].refCount = 3; //DU
+        infoArray[numTmps].physicalType = LowOpndRegType_gp;
+        numTmps++;
+        if(inst_op == OP_INVOKE_VIRTUAL_QUICK)
+            k = updateInvokeNoRange(infoArray, numTmps);
+        else
+            k = updateInvokeRange(infoArray, numTmps);
+        return k;
+#else
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 1+1;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        if(inst_op == OP_INVOKE_VIRTUAL_QUICK_RANGE)
+            k = updateInvokeRange(infoArray, 5);
+        else
+            k = updateInvokeNoRange(infoArray, 5);
+        return k;
+#endif
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2;
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 4;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 5;
+        infoArray[2].refCount = 2;
+        infoArray[2].physicalType = LowOpndRegType_gp;
+
+        infoArray[3].regNum = PhysicalReg_ECX;
+        infoArray[3].refCount = 1+1;
+        infoArray[3].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        infoArray[4].regNum = PhysicalReg_EDX;
+        infoArray[4].refCount = 2;
+        infoArray[4].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = 2;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_scratch;
+        if(inst_op == OP_INVOKE_SUPER_QUICK_RANGE)
+            k = updateInvokeRange(infoArray, 7);
+        else
+            k = updateInvokeNoRange(infoArray, 7);
+        return k;
+#ifdef SUPPORT_HLO
+    case kExtInstruction:
+        switch(inst) {
+    case OP_X_AGET_QUICK:
+    case OP_X_AGET_OBJECT_QUICK:
+    case OP_X_AGET_BOOLEAN_QUICK:
+    case OP_X_AGET_BYTE_QUICK:
+    case OP_X_AGET_CHAR_QUICK:
+    case OP_X_AGET_SHORT_QUICK:
+        vA = FETCH(1) & 0xff;
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[3].linkageToVR = vA;
+        if(inst == OP_X_AGET_BYTE_QUICK || inst == OP_X_AGET_BOOLEAN_QUICK)
+            infoArray[3].is8Bit = true;
+        return 4;
+    case OP_X_AGET_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        return 4;
+    case OP_X_APUT_QUICK:
+    case OP_X_APUT_OBJECT_QUICK:
+    case OP_X_APUT_BOOLEAN_QUICK:
+    case OP_X_APUT_BYTE_QUICK:
+    case OP_X_APUT_CHAR_QUICK:
+    case OP_X_APUT_SHORT_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 4;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        if(inst == OP_X_APUT_BYTE_QUICK || inst == OP_X_APUT_BOOLEAN_QUICK)
+            infoArray[3].is8Bit = true;
+        return 4;
+    case OP_X_APUT_WIDE_QUICK:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 1;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_xmm;
+        return 4;
+    case OP_X_DEREF_GET:
+    case OP_X_DEREF_GET_OBJECT:
+    case OP_X_DEREF_GET_BOOLEAN:
+    case OP_X_DEREF_GET_BYTE:
+    case OP_X_DEREF_GET_CHAR:
+    case OP_X_DEREF_GET_SHORT:
+        vA = FETCH(1) & 0xff;
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[1].linkageToVR = vA;
+        if(inst == OP_X_DEREF_GET_BYTE || inst == OP_X_DEREF_GET_BOOLEAN)
+            infoArray[1].is8Bit = true;
+        return 2;
+    case OP_X_DEREF_GET_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_X_DEREF_PUT:
+    case OP_X_DEREF_PUT_OBJECT:
+    case OP_X_DEREF_PUT_BOOLEAN:
+    case OP_X_DEREF_PUT_BYTE:
+    case OP_X_DEREF_PUT_CHAR:
+    case OP_X_DEREF_PUT_SHORT:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        if(inst == OP_X_DEREF_PUT_BYTE || inst == OP_X_DEREF_PUT_BOOLEAN)
+            infoArray[1].is8Bit = true;
+        return 2;
+    case OP_X_DEREF_PUT_WIDE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 1;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_xmm;
+        return 2;
+    case OP_X_ARRAY_CHECKS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        return 2;
+    case OP_X_CHECK_BOUNDS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 2; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        return 2;
+    case OP_X_CHECK_NULL:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 2; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = PhysicalReg_EDX;
+        infoArray[1].refCount = 2;
+        infoArray[1].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 2;
+    case OP_X_CHECK_TYPE:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 3; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 5;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 6;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 1;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_scratch;
+        infoArray[5].regNum = PhysicalReg_EAX;
+        infoArray[5].refCount = 2;
+        infoArray[5].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 6;
+    case OP_X_ARRAY_OBJECT_CHECKS:
+        infoArray[0].regNum = 1;
+        infoArray[0].refCount = 3; //DU
+        infoArray[0].physicalType = LowOpndRegType_gp;
+        infoArray[1].regNum = 2;
+        infoArray[1].refCount = 4; //DU
+        infoArray[1].physicalType = LowOpndRegType_gp;
+        infoArray[2].regNum = 3;
+        infoArray[2].refCount = 2; //DU
+        infoArray[2].physicalType = LowOpndRegType_gp;
+        infoArray[3].regNum = 5;
+        infoArray[3].refCount = 2; //DU
+        infoArray[3].physicalType = LowOpndRegType_gp;
+        infoArray[4].regNum = 6;
+        infoArray[4].refCount = 2; //DU
+        infoArray[4].physicalType = LowOpndRegType_gp;
+        infoArray[5].regNum = 1;
+        infoArray[5].refCount = 2; //DU
+        infoArray[5].physicalType = LowOpndRegType_scratch;
+        infoArray[6].regNum = PhysicalReg_EAX;
+        infoArray[6].refCount = 2;
+        infoArray[6].physicalType = LowOpndRegType_gp | LowOpndRegType_hard;
+        return 7;
+    }
+#endif
+    }
+    return -1;
+}
diff --git a/vm/compiler/codegen/x86/CodegenInterface.cpp b/vm/compiler/codegen/x86/CodegenInterface.cpp
new file mode 100644
index 0000000..6ec5f9d
--- /dev/null
+++ b/vm/compiler/codegen/x86/CodegenInterface.cpp
@@ -0,0 +1,1541 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+#include <sys/mman.h>
+#include "Dalvik.h"
+#include "libdex/DexOpcodes.h"
+#include "compiler/Compiler.h"
+#include "compiler/CompilerIR.h"
+#include "interp/Jit.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "compiler/codegen/CompilerCodegen.h"
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT     0xe7fe
+
+/* Target-specific save/restore */
+extern "C" void dvmJitCalleeSave(double *saveArea);
+extern "C" void dvmJitCalleeRestore(double *saveArea);
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+//JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit)
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+    return DALVIK_JIT_IA32;
+}
+
+JitInstructionSetType dvmCompilerGetInterpretTemplateSet()
+{
+    return DALVIK_JIT_IA32;
+}
+
+/* we don't use template for IA32 */
+void *dvmCompilerGetInterpretTemplate()
+{
+      return NULL;
+}
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES()    (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+bool dvmCompilerArchInit() {
+    /* Target-specific configuration */
+    gDvmJit.jitTableSize = 1 << 12;
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    if (gDvmJit.threshold == 0) {
+        gDvmJit.threshold = 255;
+    }
+    if (gDvmJit.codeCacheSize == DEFAULT_CODE_CACHE_SIZE) {
+      gDvmJit.codeCacheSize = 512 * 1024;
+    } else if ((gDvmJit.codeCacheSize == 0) && (gDvm.executionMode == kExecutionModeJit)) {
+      gDvm.executionMode = kExecutionModeInterpFast;
+    }
+    gDvmJit.optLevel = kJitOptLevelO1;
+
+    //Disable Method-JIT
+    gDvmJit.disableOpt |= (1 << kMethodJit);
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    // Make sure all threads have current values
+    dvmJitUpdateThreadStateAll();
+
+    return true;
+}
+
+void dvmCompilerPatchInlineCache(void)
+{
+    int i;
+    PredictedChainingCell *minAddr, *maxAddr;
+
+    /* Nothing to be done */
+    if (gDvmJit.compilerICPatchIndex == 0) return;
+
+    /*
+     * Since all threads are already stopped we don't really need to acquire
+     * the lock. But race condition can be easily introduced in the future w/o
+     * paying attention so we still acquire the lock here.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    //ALOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+    /* Initialize the min/max address range */
+    minAddr = (PredictedChainingCell *)
+        ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+    maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+    for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+        ICPatchWorkOrder *workOrder = &gDvmJit.compilerICPatchQueue[i];
+        PredictedChainingCell *cellAddr = workOrder->cellAddr;
+        PredictedChainingCell *cellContent = &workOrder->cellContent;
+        ClassObject *clazz = dvmFindClassNoInit(workOrder->classDescriptor,
+                                                workOrder->classLoader);
+
+        assert(clazz->serialNumber == workOrder->serialNumber);
+
+        /* Use the newly resolved clazz pointer */
+        cellContent->clazz = clazz;
+
+        if (cellAddr->clazz == NULL) {
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: predicted chain %p to %s (%s) initialized",
+                      cellAddr,
+                      cellContent->clazz->descriptor,
+                      cellContent->method->name));
+        } else {
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: predicted chain %p from %s to %s (%s) "
+                      "patched",
+                      cellAddr,
+                      cellAddr->clazz->descriptor,
+                      cellContent->clazz->descriptor,
+                      cellContent->method->name));
+        }
+
+        /* Patch the chaining cell */
+        *cellAddr = *cellContent;
+        minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+        maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+    }
+
+    PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+    gDvmJit.compilerICPatchIndex = 0;
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+
+/* Target-specific cache clearing */
+void dvmCompilerCacheClear(char *start, size_t size)
+{
+    /* "0xFF 0xFF" is an invalid opcode for x86. */
+    memset(start, 0xFF, size);
+}
+
+/* for JIT debugging, to be implemented */
+void dvmJitCalleeSave(double *saveArea) {
+}
+
+void dvmJitCalleeRestore(double *saveArea) {
+}
+
+void dvmJitToInterpSingleStep() {
+}
+
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry) {
+    return NULL;
+}
+
+void dvmCompilerCodegenDump(CompilationUnit *cUnit) //in ArchUtility.c
+{
+}
+
+void dvmCompilerArchDump(void)
+{
+}
+
+char *getTraceBase(const JitEntry *p)
+{
+    return NULL;
+}
+
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo* info)
+{
+}
+
+void dvmJitInstallClassObjectPointers(CompilationUnit *cUnit, char *codeAddress)
+{
+}
+
+void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
+{
+    // Method-based JIT not supported for x86.
+}
+
+void dvmJitScanAllClassPointers(void (*callback)(void *))
+{
+}
+
+/* Handy function to retrieve the profile count */
+static inline int getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0)
+        return 0;
+    u4 *pExecutionCount = (u4 *) getTraceBase(entry);
+
+    return pExecutionCount ? *pExecutionCount : 0;
+}
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+    const JitEntry *jitEntry2 = (const JitEntry *)entry2;
+
+    JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+    JitTraceCounter_t count2 = getProfileCount(jitEntry2);
+    return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles() //in Assemble.c
+{
+    JitEntry *sortedEntries;
+    int numTraces = 0;
+    unsigned long counts = 0;
+    unsigned int i;
+
+    /* Make sure that the table is not changing */
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Sort the entries by descending order */
+    sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+    if (sortedEntries == NULL)
+        goto done;
+    memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+           sizeof(JitEntry) * gDvmJit.jitTableSize);
+    qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+          sortTraceProfileCount);
+
+    /* Dump the sorted entries */
+    for (i=0; i < gDvmJit.jitTableSize; i++) {
+        if (sortedEntries[i].dPC != 0) {
+            numTraces++;
+        }
+    }
+    if (numTraces == 0)
+        numTraces = 1;
+    ALOGI("JIT: Average execution count -> %d",(int)(counts / numTraces));
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+void jumpWithRelOffset(char* instAddr, int relOffset) {
+    stream = instAddr;
+    OpndSize immSize = estOpndSizeFromImm(relOffset);
+    relOffset -= getJmpCallInstSize(immSize, JmpCall_uncond);
+    dump_imm(Mnemonic_JMP, immSize, relOffset);
+}
+
+// works whether instructions for target basic block are generated or not
+LowOp* jumpToBasicBlock(char* instAddr, int targetId) {
+    stream = instAddr;
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = targetId;
+    relativeNCG = getRelativeNCG(targetId, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return NULL;
+}
+
+LowOp* condJumpToBasicBlock(char* instAddr, ConditionCode cc, int targetId) {
+    stream = instAddr;
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = targetId;
+    relativeNCG = getRelativeNCG(targetId, JmpCall_cond, &unknown, &size);
+    conditional_jump_int(cc, relativeNCG, size);
+    return NULL;
+}
+
+/*
+ * Attempt to enqueue a work order to patch an inline cache for a predicted
+ * chaining cell for virtual/interface calls.
+ */
+static bool inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
+                                    PredictedChainingCell *newContent)
+{
+    bool result = true;
+
+    /*
+     * Make sure only one thread gets here since updating the cell (ie fast
+     * path and queueing the request (ie the queued path) have to be done
+     * in an atomic fashion.
+     */
+    dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+    /* Fast path for uninitialized chaining cell */
+    if (cellAddr->clazz == NULL &&
+        cellAddr->branch == PREDICTED_CHAIN_BX_PAIR_INIT) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->method = newContent->method;
+        cellAddr->branch = newContent->branch;
+        cellAddr->branch2 = newContent->branch2;
+
+        /*
+         * The update order matters - make sure clazz is updated last since it
+         * will bring the uninitialized chaining cell to life.
+         */
+        android_atomic_release_store((int32_t)newContent->clazz,
+            (volatile int32_t *)(void*) &cellAddr->clazz);
+        //cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if 0
+        MEM_BARRIER();
+        cellAddr->clazz = newContent->clazz;
+        //cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+#endif
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchInit++;
+#endif
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: FAST predicted chain %p to method %s%s %p",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name, newContent->method));
+    /* Check if this is a frequently missed clazz */
+    } else if (cellAddr->stagedClazz != newContent->clazz) {
+        /* Not proven to be frequent yet - build up the filter cache */
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->stagedClazz = newContent->clazz;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchRejected++;
+#endif
+    /*
+     * Different classes but same method implementation - it is safe to just
+     * patch the class value without the need to stop the world.
+     */
+    } else if (cellAddr->method == newContent->method) {
+        UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+        cellAddr->clazz = newContent->clazz;
+        /* No need to flush the cache here since the branch is not patched */
+        UPDATE_CODE_CACHE_PATCHES();
+
+        PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchLockFree++;
+#endif
+    /*
+     * Cannot patch the chaining cell inline - queue it until the next safe
+     * point.
+     */
+    } else if (gDvmJit.compilerICPatchIndex < COMPILER_IC_PATCH_QUEUE_SIZE)  {
+        int index = gDvmJit.compilerICPatchIndex++;
+        const ClassObject *clazz = newContent->clazz;
+
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+        gDvmJit.compilerICPatchQueue[index].classDescriptor = clazz->descriptor;
+        gDvmJit.compilerICPatchQueue[index].classLoader = clazz->classLoader;
+        /* For verification purpose only */
+        gDvmJit.compilerICPatchQueue[index].serialNumber = clazz->serialNumber;
+
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchQueued++;
+#endif
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: QUEUE predicted chain %p to method %s%s",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name));
+    } else {
+    /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.icPatchDropped++;
+#endif
+
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: DROP predicted chain %p to method %s%s",
+                  cellAddr, newContent->clazz->descriptor, newContent->method->name));
+    }
+
+    dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+    return result;
+}
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ *   1) Chain is not setup because the callee is native. Reset the rechain
+ *      count to a big number so that it will take a long time before the next
+ *      rechain attempt to happen.
+ *   2) Chain is not setup because the callee has not been created yet. Reset
+ *      the rechain count to a small number and retry in the near future.
+ *   3) Ask all other threads to stop before patching this chaining cell.
+ *      This is required because another thread may have passed the class check
+ *      but hasn't reached the chaining cell yet to follow the chain. If we
+ *      patch the content before halting the other thread, there could be a
+ *      small window for race conditions to happen that it may follow the new
+ *      but wrong chain to invoke a different method.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+    /* Don't come back here for a long time if the method is native */
+    if (dvmIsNativeMethod(method)) {
+        UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+        /*
+         * Put a non-zero/bogus value in the clazz field so that it won't
+         * trigger immediate patching and will continue to fail to match with
+         * a real clazz pointer.
+         */
+        cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: predicted chain %p to native method %s ignored",
+                  cell, method->name));
+        goto done;
+    }
+    {
+    int tgtAddr = (int) dvmJitGetTraceAddr(method->insns);
+
+    /*
+     * Compilation not made yet for the callee. Reset the counter to a small
+     * value and come back to check soon.
+     */
+    if ((tgtAddr == 0) ||
+        ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: predicted chain %p to method %s%s delayed",
+                  cell, method->clazz->descriptor, method->name));
+        goto done;
+    }
+
+    PredictedChainingCell newCell;
+
+    if (cell->clazz == NULL) {
+        newRechainCount = self->icRechainCount;
+    }
+
+    int relOffset = (int) tgtAddr - (int)cell;
+    OpndSize immSize = estOpndSizeFromImm(relOffset);
+    int jumpSize = getJmpCallInstSize(immSize, JmpCall_uncond);
+    relOffset -= jumpSize;
+    COMPILER_TRACE_CHAINING(
+            ALOGI("inlineCachePatchEnqueue chain %p to method %s%s inst size %d",
+                  cell, method->clazz->descriptor, method->name, jumpSize));
+    //can't use stream here since it is used by the compilation thread
+    dump_imm_with_codeaddr(Mnemonic_JMP, immSize, relOffset, (char*) (&newCell)); //update newCell.branch
+
+    newCell.clazz = clazz;
+    newCell.method = method;
+
+    /*
+     * Enter the work order to the queue and the chaining cell will be patched
+     * the next time a safe point is entered.
+     *
+     * If the enqueuing fails reset the rechain count to a normal value so that
+     * it won't get indefinitely delayed.
+     */
+    inlineCachePatchEnqueue(cell, &newCell);
+    }
+done:
+    self->icRechainCount = newRechainCount;
+    return method;
+}
+
+/*
+ * Unchain a trace given the starting address of the translation
+ * in the code cache.  Refer to the diagram in dvmCompilerAssembleLIR.
+ * For ARM, it returns the address following the last cell unchained.
+ * For IA, it returns NULL since cacheflush is not required for IA.
+ */
+u4* dvmJitUnchain(void* codeAddr)
+{
+    /* codeAddr is 4-byte aligned, so is chain cell count offset */
+    u2* pChainCellCountOffset = (u2*)((char*)codeAddr - 4);
+    u2 chainCellCountOffset = *pChainCellCountOffset;
+    /* chain cell counts information is 4-byte aligned */
+    ChainCellCounts *pChainCellCounts =
+          (ChainCellCounts*)((char*)codeAddr + chainCellCountOffset);
+    u2* pChainCellOffset = (u2*)((char*)codeAddr - 2);
+    u2 chainCellOffset = *pChainCellOffset;
+    u1* pChainCells;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+    int padding;
+
+    /* Locate the beginning of the chain cell region */
+    pChainCells = (u1 *)((char*)codeAddr + chainCellOffset);
+
+    /* The cells are sorted in order - walk through them and reset */
+    for (i = 0; i < kChainingCellGap; i++) {
+        /* for hot, normal, singleton chaining:
+               nop  //padding.
+               jmp 0
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           after chaining:
+               nop
+               jmp imm
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           after unchaining:
+               nop
+               jmp 0
+               mov imm32, reg1
+               mov imm32, reg2
+               call reg2
+           Space occupied by the chaining cell in bytes: nop is for padding,
+                jump 0, the target 0 is 4 bytes aligned.
+           Space for predicted chaining: 5 words = 20 bytes
+        */
+        int elemSize = 0;
+        if (i == kChainingCellInvokePredicted) {
+            elemSize = 20;
+        }
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: unchaining type %d count %d", i, pChainCellCounts->u.count[i]));
+
+        for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+            switch(i) {
+                case kChainingCellNormal:
+                case kChainingCellHot:
+                case kChainingCellInvokeSingleton:
+                case kChainingCellBackwardBranch:
+                    COMPILER_TRACE_CHAINING(
+                        ALOGI("Jit Runtime: unchaining of normal, hot, or singleton"));
+                    pChainCells = (u1*) (((uint)pChainCells + 4)&(~0x03));
+                    elemSize = 4+5+5+2;
+                    memset(pChainCells, 0, 4);
+                    break;
+                case kChainingCellInvokePredicted:
+                    COMPILER_TRACE_CHAINING(
+                        ALOGI("Jit Runtime: unchaining of predicted"));
+                    /* 4-byte aligned */
+                    padding = (4 - ((u4)pChainCells & 3)) & 3;
+                    pChainCells += padding;
+                    predChainCell = (PredictedChainingCell *) pChainCells;
+                    /*
+                     * There could be a race on another mutator thread to use
+                     * this particular predicted cell and the check has passed
+                     * the clazz comparison. So we cannot safely wipe the
+                     * method and branch but it is safe to clear the clazz,
+                     * which serves as the key.
+                     */
+                    predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+                    break;
+                default:
+                    ALOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                ALOGI("Jit Runtime: unchaining 0x%x", (int)pChainCells));
+            pChainCells += elemSize;  /* Advance by a fixed number of bytes */
+        }
+    }
+    return NULL;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+    ALOGV("Jit Runtime: unchaining all");
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(ALOGI("Jit Runtime: unchaining all"));
+        dvmLockMutex(&gDvmJit.tableLock);
+
+        UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        for (size_t i = 0; i < gDvmJit.jitTableSize; i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC &&
+                !gDvmJit.pJitEntryTable[i].u.info.isMethodEntry &&
+                gDvmJit.pJitEntryTable[i].codeAddress) {
+                      dvmJitUnchain(gDvmJit.pJitEntryTable[i].codeAddress);
+            }
+        }
+
+        PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+        dvmUnlockMutex(&gDvmJit.tableLock);
+        gDvmJit.translationChains = 0;
+    }
+    gDvmJit.hasNewChain = false;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+/* Add an additional jump instruction, keep jump target 4 bytes aligned.*/
+static void insertJumpHelp()
+{
+    int rem = (uint)stream % 4;
+    int nop_size = 3 - rem;
+    dump_nop(nop_size);
+    unconditional_jump_int(0, OpndSize_32);
+    return;
+}
+
+/* Chaining cell for code that may need warmup. */
+/* ARM assembly: ldr r0, [r6, #76] (why a single instruction to access member of glue structure?)
+                 blx r0
+                 data 0xb23a //bytecode address: 0x5115b23a
+                 data 0x5115
+   IA32 assembly:
+                  jmp  0 //5 bytes
+                  movl address, %ebx
+                  movl dvmJitToInterpNormal, %eax
+                  call %eax
+                  <-- return address
+*/
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleNormalChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER NormalChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpNormal();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+                                  unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleHotChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER HotChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpTraceSelect();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+                                     unsigned int offset, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleBackwardBranchChainingCell for method %s block %d BC offset %x NCG offset %x",
+          cUnit->method->name, blockId, offset, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER BackwardBranchChainingCell at offsetPC %x offsetNCG %x @%p",
+              offset, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpNormal();
+    //move_imm_to_reg(OpndSize_32, (int) (cUnit->method->insns + offset), P_GPR_1, true); /* used when unchaining */
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+                                              const Method *callee, int blockId, LowOpBlockLabel* labelList)
+{
+    ALOGV("in handleInvokeSingletonChainingCell for method %s block %d callee %s NCG offset %x",
+          cUnit->method->name, blockId, callee->name, stream - streamMethodStart);
+    if(dump_x86_inst)
+        ALOGI("LOWER InvokeSingletonChainingCell at block %d offsetNCG %x @%p",
+              blockId, stream - streamMethodStart, stream);
+    /* Add one additional "jump 0" instruction, it may be modified during jit chaining. This helps
+     * reslove the multithreading issue.
+     */
+    insertJumpHelp();
+    move_imm_to_reg(OpndSize_32, (int) (callee->insns), P_GPR_1, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToInterpTraceSelect();
+    //move_imm_to_reg(OpndSize_32, (int) (callee->insns), P_GPR_1, true); /* used when unchaining */
+}
+#undef P_GPR_1
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit, int blockId)
+{
+    if(dump_x86_inst)
+        ALOGI("LOWER InvokePredictedChainingCell at block %d offsetNCG %x @%p",
+              blockId, stream - streamMethodStart, stream);
+#ifndef PREDICTED_CHAINING
+    //assume rPC for callee->insns in %ebx
+    scratchRegs[0] = PhysicalReg_EAX;
+#if defined(WITH_JIT_TUNING)
+    /* Predicted chaining is not enabled. Fall back to interpreter and
+     * indicate that predicted chaining was not done.
+     */
+    move_imm_to_reg(OpndSize_32, kInlineCacheMiss, PhysicalReg_EDX, true);
+#endif
+    call_dvmJitToInterpTraceSelectNoChain();
+#else
+    /* make sure section for predicited chaining cell is 4-byte aligned */
+    //int padding = (4 - ((u4)stream & 3)) & 3;
+    //stream += padding;
+    int* streamData = (int*)stream;
+    /* Should not be executed in the initial state */
+    streamData[0] = PREDICTED_CHAIN_BX_PAIR_INIT;
+    streamData[1] = 0;
+    /* To be filled: class */
+    streamData[2] = PREDICTED_CHAIN_CLAZZ_INIT;
+    /* To be filled: method */
+    streamData[3] = PREDICTED_CHAIN_METHOD_INIT;
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    streamData[4] = PREDICTED_CHAIN_COUNTER_INIT;
+#if 0
+    ALOGI("--- DATA @ %p: %x %x %x %x", stream, *((int*)stream), *((int*)(stream+4)),
+          *((int*)(stream+8)), *((int*)(stream+12)));
+#endif
+    stream += 20; //5 *4
+#endif
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+                                   LowOpBlockLabel *targetLabel)
+{
+#if 0
+    LowOp **pcrLabel =
+        (LowOp **) cUnit->pcReconstructionList.elemList;
+    int numElems = cUnit->pcReconstructionList.numUsed;
+    int i;
+    for (i = 0; i < numElems; i++) {
+        dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+        /* r0 = dalvik PC */
+        loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+        genUnconditionalBranch(cUnit, targetLabel);
+    }
+#endif
+}
+
+//use O0 code generator for hoisted checks outside of the loop
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    /*
+     * NOTE: these synthesized blocks don't have ssa names assigned
+     * for Dalvik registers.  However, because they dominate the following
+     * blocks we can simply use the Dalvik name w/ subscript 0 as the
+     * ssa name.
+     */
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int maxC = dInsn->arg[0];
+
+    /* assign array in virtual register to P_GPR_1 */
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true);
+    /* assign index in virtual register to P_GPR_2 */
+    get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, P_GPR_2, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_E, cUnit->exceptionBlockId);
+    int delta = maxC;
+    /*
+     * If the loop end condition is ">=" instead of ">", then the largest value
+     * of the index is "endCondition - 1".
+     */
+    if (dInsn->arg[2] == OP_IF_GE) {
+        delta--;
+    }
+
+    if (delta < 0) { //+delta
+        //if P_GPR_2 is mapped to a VR, we can't do this
+        alu_binary_imm_reg(OpndSize_32, sub_opc, -delta, P_GPR_2, true);
+    } else if(delta > 0) {
+        alu_binary_imm_reg(OpndSize_32, add_opc, delta, P_GPR_2, true);
+    }
+    compare_mem_reg(OpndSize_32, offArrayObject_length, P_GPR_1, true, P_GPR_2, true);
+    condJumpToBasicBlock(stream, Condition_NC, cUnit->exceptionBlockId);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int maxC = dInsn->arg[0];
+
+    /* assign array in virtual register to P_GPR_1 */
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true);
+    /* assign index in virtual register to P_GPR_2 */
+    get_virtual_reg(mir->dalvikInsn.vB, OpndSize_32, P_GPR_2, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_E, cUnit->exceptionBlockId);
+
+    if (maxC < 0) {
+        //if P_GPR_2 is mapped to a VR, we can't do this
+        alu_binary_imm_reg(OpndSize_32, sub_opc, -maxC, P_GPR_2, true);
+    } else if(maxC > 0) {
+        alu_binary_imm_reg(OpndSize_32, add_opc, maxC, P_GPR_2, true);
+    }
+    compare_mem_reg(OpndSize_32, offArrayObject_length, P_GPR_1, true, P_GPR_2, true);
+    condJumpToBasicBlock(stream, Condition_NC, cUnit->exceptionBlockId);
+
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+#define P_GPR_1 PhysicalReg_ECX
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    const int minC = dInsn->vB;
+    get_virtual_reg(mir->dalvikInsn.vA, OpndSize_32, P_GPR_1, true); //array
+    export_pc();
+    compare_imm_reg(OpndSize_32, -minC, P_GPR_1, true);
+    condJumpToBasicBlock(stream, Condition_C, cUnit->exceptionBlockId);
+}
+#undef P_GPR_1
+
+#ifdef WITH_JIT_INLINING
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+    CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, PhysicalReg_EBX, true);
+        move_imm_to_reg(OpndSize_32, (int) callsiteInfo->clazz, PhysicalReg_ECX, true);
+        compare_imm_reg(OpndSize_32, 0, PhysicalReg_EBX, true);
+        export_pc(); //use %edx
+        conditional_jump_global_API(, Condition_E, "common_errNullObject", false);
+        move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true, PhysicalReg_EAX, true);
+        compare_reg_reg(PhysicalReg_ECX, true, PhysicalReg_EAX, true);
+    } else {
+        get_virtual_reg(mir->dalvikInsn.vC, OpndSize_32, 5, false);
+        move_imm_to_reg(OpndSize_32, (int) callsiteInfo->clazz, 4, false);
+        nullCheck(5, false, 1, mir->dalvikInsn.vC);
+        move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false);
+        compare_reg_reg(4, false, 6, false);
+    }
+
+    //immdiate will be updated later in genLandingPadForMispredictedCallee
+    streamMisPred = stream;
+    callsiteInfo->misPredBranchOver = (LIR*)conditional_jump_int(Condition_NE, 0, OpndSize_8);
+}
+#endif
+
+/* Extended MIR instructions like PHI */
+void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+    ExecutionMode origMode = gDvm.executionMode;
+    gDvm.executionMode = kExecutionModeNcgO0;
+    switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) {
+        case kMirOpPhi: {
+            break;
+        }
+        case kMirOpNullNRangeUpCheck: {
+            genHoistedChecksForCountUpLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpNullNRangeDownCheck: {
+            genHoistedChecksForCountDownLoop(cUnit, mir);
+            break;
+        }
+        case kMirOpLowerBound: {
+            genHoistedLowerBoundCheck(cUnit, mir);
+            break;
+        }
+        case kMirOpPunt: {
+            break;
+        }
+#ifdef WITH_JIT_INLINING
+        case kMirOpCheckInlinePrediction: { //handled in ncg_o1_data.c
+            genValidationForPredictedInline(cUnit, mir);
+            break;
+        }
+#endif
+        default:
+            break;
+    }
+    gDvm.executionMode = origMode;
+}
+
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+                                int bodyId)
+{
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    //LowOp* branchToBody = jumpToBasicBlock(stream, bodyId);
+    //setupResourceMasks(branchToBody);
+    //cUnit->loopAnalysis->branchToBody = ((LIR*)branchToBody);
+
+#if 0
+    LowOp *branchToPCR = dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToPCR->opCode = kThumbBUncond;
+    branchToPCR->generic.target = (LIR *) pcrLabel;
+    setupResourceMasks(branchToPCR);
+    cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+#endif
+}
+
+/* check whether we can merge the block at index i with its target block */
+bool mergeBlock(BasicBlock *bb) {
+    if(bb->blockType == kDalvikByteCode &&
+       bb->firstMIRInsn != NULL &&
+       (bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO_16 ||
+        bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO ||
+        bb->lastMIRInsn->dalvikInsn.opcode == OP_GOTO_32) &&
+       bb->fallThrough == NULL) {// &&
+       //cUnit->hasLoop) {
+        //ALOGI("merge blocks ending with goto at index %d", i);
+        MIR* prevInsn = bb->lastMIRInsn->prev;
+        if(bb->taken == NULL) return false;
+        MIR* mergeInsn = bb->taken->firstMIRInsn;
+        if(mergeInsn == NULL) return false;
+        if(prevInsn == NULL) {//the block has a single instruction
+            bb->firstMIRInsn = mergeInsn;
+        } else {
+            prevInsn->next = mergeInsn; //remove goto from the chain
+        }
+        mergeInsn->prev = prevInsn;
+        bb->lastMIRInsn = bb->taken->lastMIRInsn;
+        bb->taken->firstMIRInsn = NULL; //block being merged in
+        bb->fallThrough = bb->taken->fallThrough;
+        bb->taken = bb->taken->taken;
+        return true;
+    }
+    return false;
+}
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+    cUnit->headerSize = 6;
+    if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+        (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+        return 12;
+    } else {
+        return 4;
+    }
+
+}
+
+#define PRINT_BUFFER_LEN 1024
+/* Print the code block in code cache in the range of [startAddr, endAddr)
+ * in readable format.
+ */
+void printEmittedCodeBlock(unsigned char *startAddr, unsigned char *endAddr)
+{
+    char strbuf[PRINT_BUFFER_LEN];
+    unsigned char *addr;
+    unsigned char *next_addr;
+    int n;
+
+    if (gDvmJit.printBinary) {
+        // print binary in bytes
+        n = 0;
+        for (addr = startAddr; addr < endAddr; addr++) {
+            n += snprintf(&strbuf[n], PRINT_BUFFER_LEN-n, "0x%x, ", *addr);
+            if (n > PRINT_BUFFER_LEN - 10) {
+                ALOGD("## %s", strbuf);
+                n = 0;
+            }
+        }
+        if (n > 0)
+            ALOGD("## %s", strbuf);
+    }
+
+    // print disassembled instructions
+    addr = startAddr;
+    while (addr < endAddr) {
+        next_addr = reinterpret_cast<unsigned char*>
+            (decoder_disassemble_instr(reinterpret_cast<char*>(addr),
+                                       strbuf, PRINT_BUFFER_LEN));
+        if (addr != next_addr) {
+            ALOGD("**  %p: %s", addr, strbuf);
+        } else {                // check whether this is nop padding
+            if (addr[0] == 0x90) {
+                ALOGD("**  %p: NOP (1 byte)", addr);
+                next_addr += 1;
+            } else if (addr[0] == 0x66 && addr[1] == 0x90) {
+                ALOGD("**  %p: NOP (2 bytes)", addr);
+                next_addr += 2;
+            } else if (addr[0] == 0x0f && addr[1] == 0x1f && addr[2] == 0x00) {
+                ALOGD("**  %p: NOP (3 bytes)", addr);
+                next_addr += 3;
+            } else {
+                ALOGD("** unable to decode binary at %p", addr);
+                break;
+            }
+        }
+        addr = next_addr;
+    }
+}
+
+/* 4 is the number of additional bytes needed for chaining information for trace:
+ * 2 bytes for chaining cell count offset and 2 bytes for chaining cell offset */
+#define EXTRA_BYTES_FOR_CHAINING 4
+
+/* Entry function to invoke the backend of the JIT compiler */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+    dump_x86_inst = cUnit->printMe;
+    /* Used to hold the labels of each block */
+    LowOpBlockLabel *labelList =
+        (LowOpBlockLabel *)dvmCompilerNew(sizeof(LowOpBlockLabel) * cUnit->numBlocks, true); //Utility.c
+    LowOp *headLIR = NULL;
+    GrowableList chainingListByType[kChainingCellLast];
+    unsigned int i, padding;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellLast; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    /* Clear the visited flag for each block */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+                                          kAllNodes, false /* isIterative */);
+
+    GrowableListIterator iterator;
+    dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+    /* Traces start with a profiling entry point.  Generate it here */
+    cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
+
+    //BasicBlock **blockList = cUnit->blockList;
+    GrowableList *blockList = &cUnit->blockList;
+    BasicBlock *bb;
+
+    info->codeAddress = NULL;
+    stream = (char*)gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+    streamStart = stream; /* trace start before alignment */
+
+    // TODO: compile into a temporary buffer and then copy into the code cache.
+    // That would let us leave the code cache unprotected for a shorter time.
+    size_t unprotected_code_cache_bytes =
+            gDvmJit.codeCacheSize - gDvmJit.codeCacheByteUsed;
+    UNPROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+
+    stream += EXTRA_BYTES_FOR_CHAINING; /* This is needed for chaining. Add the bytes before the alignment */
+    stream = (char*)(((unsigned int)stream + 0xF) & ~0xF); /* Align trace to 16-bytes */
+    streamMethodStart = stream; /* code start */
+    for (i = 0; i < ((unsigned int) cUnit->numBlocks); i++) {
+        labelList[i].lop.generic.offset = -1;
+    }
+    cUnit->exceptionBlockId = -1;
+    for (i = 0; i < blockList->numUsed; i++) {
+        bb = (BasicBlock *) blockList->elemList[i];
+        if(bb->blockType == kExceptionHandling)
+            cUnit->exceptionBlockId = i;
+    }
+    startOfTrace(cUnit->method, labelList, cUnit->exceptionBlockId, cUnit);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //merge blocks ending with "goto" with the fall through block
+        if (cUnit->jitMode != kJitLoop)
+            for (i = 0; i < blockList->numUsed; i++) {
+                bb = (BasicBlock *) blockList->elemList[i];
+                bool merged = mergeBlock(bb);
+                while(merged) merged = mergeBlock(bb);
+            }
+        for (i = 0; i < blockList->numUsed; i++) {
+            bb = (BasicBlock *) blockList->elemList[i];
+            if(bb->blockType == kDalvikByteCode &&
+               bb->firstMIRInsn != NULL) {
+                preprocessingBB(bb);
+            }
+        }
+        preprocessingTrace();
+    }
+
+    /* Handle the content in each basic block */
+    for (i = 0; ; i++) {
+        MIR *mir;
+        bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+        if (bb == NULL) break;
+        if (bb->visited == true) continue;
+
+        labelList[i].immOpnd.value = bb->startOffset;
+
+        if (bb->blockType >= kChainingCellLast) {
+            /*
+             * Append the label pseudo LIR first. Chaining cells will be handled
+             * separately afterwards.
+             */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+        }
+
+        if (bb->blockType == kEntryBlock) {
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_ENTRY_BLOCK;
+            if (bb->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, bb, bb->fallThrough->id);
+                                  //&labelList[blockList[i]->fallThrough->id]);
+            }
+        } else if (bb->blockType == kExitBlock) {
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_EXIT_BLOCK;
+            labelList[i].lop.generic.offset = (stream - streamMethodStart);
+            goto gen_fallthrough;
+        } else if (bb->blockType == kDalvikByteCode) {
+            if (bb->hidden == true) continue;
+            labelList[i].lop.opCode2 = ATOM_PSEUDO_NORMAL_BLOCK_LABEL;
+            /* Reset the register state */
+#if 0
+            resetRegisterScoreboard(cUnit);
+#endif
+        } else {
+            switch (bb->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_CHAINING_CELL_NORMAL;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON;
+                    labelList[i].immOpnd.value =
+                        (int) bb->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton], i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED;
+                   /*
+                     * Move the cached method pointer from operand 1 to 0.
+                     * Operand 0 was clobbered earlier in this routine to store
+                     * the block starting offset, which is not applicable to
+                     * predicted chaining cell.
+                     */
+                    //TODO
+                    //labelList[i].operands[0] = labelList[i].operands[1];
+
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted], i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_CHAINING_CELL_HOT;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot], i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].lop.opCode2 =
+                        ATOM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL;
+                    //assert (i == cUnit->numBlocks - 2);
+                    labelList[i].lop.generic.offset = (stream - streamMethodStart);
+                    handlePCReconstruction(cUnit,
+                                           &labelList[cUnit->puntBlock->id]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_EH_BLOCK_LABEL;
+                    labelList[i].lop.generic.offset = (stream - streamMethodStart);
+                    //if (cUnit->pcReconstructionList.numUsed) {
+                        scratchRegs[0] = PhysicalReg_EAX;
+                        jumpToInterpPunt();
+                        //call_dvmJitToInterpPunt();
+                    //}
+                    break;
+                case kChainingCellBackwardBranch:
+                    labelList[i].lop.opCode2 = ATOM_PSEUDO_CHAINING_CELL_BACKWARD_BRANCH;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        i);
+                    break;
+                default:
+                    break;
+            }
+            continue;
+        }
+        {
+        //LowOp *headLIR = NULL;
+        const DexCode *dexCode = dvmGetMethodCode(cUnit->method);
+        const u2 *startCodePtr = dexCode->insns;
+        const u2 *codePtr;
+        labelList[i].lop.generic.offset = (stream - streamMethodStart);
+        ALOGV("get ready to handle JIT bb %d type %d hidden %d",
+              bb->id, bb->blockType, bb->hidden);
+        for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) {
+            bb = nextBB;
+            bb->visited = true;
+            cUnit->nextCodegenBlock = NULL;
+
+        if(gDvm.executionMode == kExecutionModeNcgO1 &&
+           bb->blockType != kEntryBlock &&
+           bb->firstMIRInsn != NULL) {
+            startOfBasicBlock(bb);
+            int cg_ret = codeGenBasicBlockJit(cUnit->method, bb);
+            endOfBasicBlock(bb);
+            if(cg_ret < 0) {
+                endOfTrace(true/*freeOnly*/);
+                cUnit->baseAddr = NULL;
+                PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+                return;
+            }
+        } else {
+        for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+            startOfBasicBlock(bb); //why here for O0
+            Opcode dalvikOpCode = mir->dalvikInsn.opcode;
+            if((int)dalvikOpCode >= (int)kMirOpFirst) {
+                handleExtendedMIR(cUnit, mir);
+                continue;
+            }
+            InstructionFormat dalvikFormat =
+                dexGetFormatFromOpcode(dalvikOpCode);
+            ALOGV("ready to handle bytecode at offset %x: opcode %d format %d",
+                  mir->offset, dalvikOpCode, dalvikFormat);
+            LowOpImm *boundaryLIR = dump_special(ATOM_PSEUDO_DALVIK_BYTECODE_BOUNDARY, mir->offset);
+            /* Remember the first LIR for this block */
+            if (headLIR == NULL) {
+                headLIR = (LowOp*)boundaryLIR;
+            }
+            bool notHandled = true;
+            /*
+             * Debugging: screen the opcode first to see if it is in the
+             * do[-not]-compile list
+             */
+            bool singleStepMe =
+                gDvmJit.includeSelectedOp !=
+                ((gDvmJit.opList[dalvikOpCode >> 3] &
+                  (1 << (dalvikOpCode & 0x7))) !=
+                 0);
+            if (singleStepMe || cUnit->allSingleStep) {
+            } else {
+                codePtr = startCodePtr + mir->offset;
+                //lower each byte code, update LIR
+                notHandled = lowerByteCodeJit(cUnit->method, cUnit->method->insns+mir->offset, mir);
+                if(gDvmJit.codeCacheByteUsed + (stream - streamStart) +
+                   CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                    ALOGI("JIT code cache full after lowerByteCodeJit (trace uses %uB)", (stream - streamStart));
+                    gDvmJit.codeCacheFull = true;
+                    cUnit->baseAddr = NULL;
+                    endOfTrace(true/*freeOnly*/);
+                    PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+                    return;
+                }
+            }
+            if (notHandled) {
+                ALOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled",
+                     mir->offset,
+                     dalvikOpCode, dexGetOpcodeName(dalvikOpCode),
+                     dalvikFormat);
+                dvmAbort();
+                break;
+            }
+        } // end for
+        } // end else //JIT + O0 code generator
+        }
+        } // end for
+        /* Eliminate redundant loads/stores and delay stores into later slots */
+#if 0
+        dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+                                           cUnit->lastLIRInsn);
+#endif
+        if (headLIR) headLIR = NULL;
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (bb->needFallThroughBranch) {
+            jumpToBasicBlock(stream, bb->fallThrough->id);
+        }
+
+    }
+
+    char* streamChainingStart = (char*)stream;
+    /* Handle the chaining cells in predefined order */
+    for (i = 0; i < kChainingCellGap; i++) {
+        size_t j;
+        int *blockIdList = (int *) chainingListByType[i].elemList;
+
+        cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+        /* No chaining cells of this type */
+        if (cUnit->numChainingCells[i] == 0)
+            continue;
+
+        /* Record the first LIR for a new type of chaining cell */
+        cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+        for (j = 0; j < chainingListByType[i].numUsed; j++) {
+            int blockId = blockIdList[j];
+            BasicBlock *chainingBlock =
+                (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+                                                         blockId);
+
+            labelList[blockId].lop.generic.offset = (stream - streamMethodStart);
+
+            /* Align this chaining cell first */
+#if 0
+            newLIR0(cUnit, ATOM_PSEUDO_ALIGN4);
+#endif
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (chainingBlock->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit,
+                     chainingBlock->startOffset, blockId, labelList);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        chainingBlock->containingMethod, blockId, labelList);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit, blockId);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit,
+                        chainingBlock->startOffset, blockId, labelList);
+                    break;
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        chainingBlock->startOffset, blockId, labelList);
+                    break;
+                default:
+                    ALOGE("Bad blocktype %d", chainingBlock->blockType);
+                    dvmAbort();
+                    break;
+            }
+
+            if (gDvmJit.codeCacheByteUsed + (stream - streamStart) + CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+                ALOGI("JIT code cache full after ChainingCell (trace uses %uB)", (stream - streamStart));
+                gDvmJit.codeCacheFull = true;
+                cUnit->baseAddr = NULL;
+                endOfTrace(true); /* need to free structures */
+                PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+                return;
+            }
+        }
+    }
+#if 0
+    dvmCompilerApplyGlobalOptimizations(cUnit);
+#endif
+    endOfTrace(false);
+
+    if (gDvmJit.codeCacheFull) {
+        /* We hit code cache size limit inside endofTrace(false).
+         * Bail out for this trace!
+         */
+        ALOGI("JIT code cache full after endOfTrace (trace uses %uB)", (stream - streamStart));
+        cUnit->baseAddr = NULL;
+        PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+        return;
+    }
+
+    /* dump section for chaining cell counts, make sure it is 4-byte aligned */
+    padding = (4 - ((u4)stream & 3)) & 3;
+    stream += padding;
+    ChainCellCounts chainCellCounts;
+    /* Install the chaining cell counts */
+    for (i=0; i< kChainingCellGap; i++) {
+        chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+    }
+    char* streamCountStart = (char*)stream;
+    memcpy((char*)stream, &chainCellCounts, sizeof(chainCellCounts));
+    stream += sizeof(chainCellCounts);
+
+    cUnit->baseAddr = streamMethodStart;
+    cUnit->totalSize = (stream - streamStart);
+    if(gDvmJit.codeCacheByteUsed + cUnit->totalSize + CODE_CACHE_PADDING > gDvmJit.codeCacheSize) {
+        ALOGI("JIT code cache full after ChainingCellCounts (trace uses %uB)", (stream - streamStart));
+        gDvmJit.codeCacheFull = true;
+        cUnit->baseAddr = NULL;
+        PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+        return;
+    }
+
+    /* write chaining cell count offset & chaining cell offset */
+    u2* pOffset = (u2*)(streamMethodStart - EXTRA_BYTES_FOR_CHAINING); /* space was already allocated for this purpose */
+    *pOffset = streamCountStart - streamMethodStart; /* from codeAddr */
+    pOffset[1] = streamChainingStart - streamMethodStart;
+
+    PROTECT_CODE_CACHE(streamStart, unprotected_code_cache_bytes);
+
+    gDvmJit.codeCacheByteUsed += (stream - streamStart);
+    if (cUnit->printMe) {
+        unsigned char* codeBaseAddr = (unsigned char *) cUnit->baseAddr;
+        unsigned char* codeBaseAddrNext = ((unsigned char *) gDvmJit.codeCache) + gDvmJit.codeCacheByteUsed;
+        ALOGD("-------- Built trace for %s%s, JIT code [%p, %p) cache start %p",
+              cUnit->method->clazz->descriptor, cUnit->method->name,
+              codeBaseAddr, codeBaseAddrNext, gDvmJit.codeCache);
+        ALOGD("** %s%s@0x%x:", cUnit->method->clazz->descriptor,
+              cUnit->method->name, cUnit->traceDesc->trace[0].info.frag.startOffset);
+        printEmittedCodeBlock(codeBaseAddr, codeBaseAddrNext);
+    }
+    ALOGV("JIT CODE after trace %p to %p size %x START %p", cUnit->baseAddr,
+          (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed,
+          cUnit->totalSize, gDvmJit.codeCache);
+
+    gDvmJit.numCompilations++;
+
+    info->codeAddress = (char*)cUnit->baseAddr;// + cUnit->headerSize;
+}
+
+/*
+ * Perform translation chain operation.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+#ifdef JIT_CHAIN
+    int relOffset = (int) tgtAddr - (int)branchAddr;
+
+    if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+        (gDvmJit.codeCacheFull == false)) {
+
+        gDvmJit.translationChains++;
+
+        //OpndSize immSize = estOpndSizeFromImm(relOffset);
+        //relOffset -= getJmpCallInstSize(immSize, JmpCall_uncond);
+        /* Hard coded the jump opnd size to 32 bits, This instruction will replace the "jump 0" in
+         * the original code sequence.
+         */
+        OpndSize immSize = OpndSize_32;
+        relOffset -= 5;
+        //can't use stream here since it is used by the compilation thread
+        UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+        dump_imm_with_codeaddr(Mnemonic_JMP, immSize, relOffset, (char*)branchAddr); //dump to branchAddr
+        PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+        gDvmJit.hasNewChain = true;
+
+        COMPILER_TRACE_CHAINING(
+            ALOGI("Jit Runtime: chaining 0x%x to %p with relOffset %x",
+                  (int) branchAddr, tgtAddr, relOffset));
+    }
+#endif
+    return tgtAddr;
+}
+
+/*
+ * Accept the work and start compiling.  Returns true if compilation
+ * is attempted.
+ */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    JitTraceDescription *desc;
+    bool isCompile;
+    bool success = true;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            isCompile = true;
+            /* Start compilation with maximally allowed trace length */
+            desc = (JitTraceDescription *)work->info;
+            success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+                                        work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        case kWorkOrderProfileMode:
+            dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info);
+            isCompile = false;
+            break;
+        default:
+            isCompile = false;
+            ALOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    if (!success)
+        work->result.codeAddress = NULL;
+    return isCompile;
+}
+
+//#endif
diff --git a/vm/compiler/codegen/x86/Lower.cpp b/vm/compiler/codegen/x86/Lower.cpp
new file mode 100644
index 0000000..d0e7f92
--- /dev/null
+++ b/vm/compiler/codegen/x86/Lower.cpp
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file Lower.cpp
+    \brief This file implements the high-level wrapper for lowering
+
+*/
+
+//#include "uthash.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include <math.h>
+#include <sys/mman.h>
+#include "Translator.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+#include "vm/mterp/Mterp.h"
+#include "NcgHelper.h"
+#include "libdex/DexCatch.h"
+#include "compiler/CompilerIR.h"
+
+//statistics for optimization
+int num_removed_nullCheck;
+
+PhysicalReg scratchRegs[4];
+
+LowOp* ops[BUFFER_SIZE];
+LowOp* op;
+u2* rPC; //PC pointer to bytecode
+u2 inst; //current bytecode
+int offsetPC/*offset in bytecode*/, offsetNCG/*byte offset in native code*/;
+int ncg_rPC;
+//! map from PC in bytecode to PC in native code
+int mapFromBCtoNCG[BYTECODE_SIZE_PER_METHOD]; //initially mapped to -1
+char* streamStart = NULL; //start of the Pure CodeItem?, not include the global symbols
+char* streamCode = NULL; //start of the Pure CodeItem?, not include the global symbols
+char* streamMethodStart; //start of the method
+char* stream; //current stream pointer
+int lowOpTimeStamp = 0;
+Method* currentMethod = NULL;
+int currentExceptionBlockIdx = -1;
+LowOpBlockLabel* traceLabelList = NULL;
+BasicBlock* traceCurrentBB = NULL;
+MIR* traceCurrentMIR = NULL;
+bool scheduling_is_on = false;
+
+int common_invokeMethodNoRange();
+int common_invokeMethodRange();
+int common_invokeArgsDone(ArgsDoneType, bool);
+
+//data section of .ia32:
+char globalData[128];
+
+char strClassCastException[] = "Ljava/lang/ClassCastException;";
+char strInstantiationError[] = "Ljava/lang/InstantiationError;";
+char strInternalError[] = "Ljava/lang/InternalError;";
+char strFilledNewArrayNotImpl[] = "filled-new-array only implemented for 'int'";
+char strArithmeticException[] = "Ljava/lang/ArithmeticException;";
+char strArrayIndexException[] = "Ljava/lang/ArrayIndexOutOfBoundsException;";
+char strArrayStoreException[] = "Ljava/lang/ArrayStoreException;";
+char strDivideByZero[] = "divide by zero";
+char strNegativeArraySizeException[] = "Ljava/lang/NegativeArraySizeException;";
+char strNoSuchMethodError[] = "Ljava/lang/NoSuchMethodError;";
+char strNullPointerException[] = "Ljava/lang/NullPointerException;";
+char strStringIndexOutOfBoundsException[] = "Ljava/lang/StringIndexOutOfBoundsException;";
+
+int LstrClassCastExceptionPtr, LstrInstantiationErrorPtr, LstrInternalError, LstrFilledNewArrayNotImpl;
+int LstrArithmeticException, LstrArrayIndexException, LstrArrayStoreException, LstrStringIndexOutOfBoundsException;
+int LstrDivideByZero, LstrNegativeArraySizeException, LstrNoSuchMethodError, LstrNullPointerException;
+int LdoubNeg, LvaluePosInfLong, LvalueNegInfLong, LvalueNanLong, LshiftMask, Lvalue64, L64bits, LintMax, LintMin;
+
+void initConstDataSec() {
+    char* tmpPtr = globalData;
+
+    LdoubNeg = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x00000000;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LvaluePosInfLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x7FFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LvalueNegInfLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x00000000;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LvalueNanLong = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    LshiftMask = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x3f;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    Lvalue64 = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x40;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0;
+    tmpPtr += sizeof(u4);
+
+    L64bits = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+    *((u4*)tmpPtr) = 0xFFFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LintMin = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x80000000;
+    tmpPtr += sizeof(u4);
+
+    LintMax = (int)tmpPtr;
+    *((u4*)tmpPtr) = 0x7FFFFFFF;
+    tmpPtr += sizeof(u4);
+
+    LstrClassCastExceptionPtr = (int)strClassCastException;
+    LstrInstantiationErrorPtr = (int)strInstantiationError;
+    LstrInternalError = (int)strInternalError;
+    LstrFilledNewArrayNotImpl = (int)strFilledNewArrayNotImpl;
+    LstrArithmeticException = (int)strArithmeticException;
+    LstrArrayIndexException = (int)strArrayIndexException;
+    LstrArrayStoreException = (int)strArrayStoreException;
+    LstrDivideByZero = (int)strDivideByZero;
+    LstrNegativeArraySizeException = (int)strNegativeArraySizeException;
+    LstrNoSuchMethodError = (int)strNoSuchMethodError;
+    LstrNullPointerException = (int)strNullPointerException;
+    LstrStringIndexOutOfBoundsException = (int)strStringIndexOutOfBoundsException;
+}
+
+//declarations of functions used in this file
+int spill_reg(int reg, bool isPhysical);
+int unspill_reg(int reg, bool isPhysical);
+
+int const_string_resolve();
+int sget_sput_resolve();
+int new_instance_needinit();
+int new_instance_abstract();
+int invoke_virtual_resolve();
+int invoke_direct_resolve();
+int invoke_static_resolve();
+int filled_new_array_notimpl();
+int resolve_class2(
+                   int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+                   bool indexPhysical,
+                   int thirdArg);
+int resolve_method2(
+                    int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+                    bool indexPhysical,
+                    int thirdArg/*VIRTUAL*/);
+int resolve_inst_field2(
+                        int startLR/*logical register index*/, bool isPhysical,
+                        int indexReg/*const pool index*/,
+                        bool indexPhysical);
+int resolve_static_field2(
+                          int startLR/*logical register index*/, bool isPhysical,
+                          int indexReg/*const pool index*/,
+                          bool indexPhysical);
+
+int invokeMethodNoRange_1_helper();
+int invokeMethodNoRange_2_helper();
+int invokeMethodNoRange_3_helper();
+int invokeMethodNoRange_4_helper();
+int invokeMethodNoRange_5_helper();
+int invokeMethodRange_helper();
+
+int invoke_virtual_helper();
+int invoke_virtual_quick_helper();
+int invoke_static_helper();
+int invoke_direct_helper();
+int new_instance_helper();
+int sget_sput_helper(int flag);
+int aput_obj_helper();
+int aget_helper(int flag);
+int aput_helper(int flag);
+int monitor_enter_helper();
+int monitor_exit_helper();
+int throw_helper();
+int const_string_helper();
+int array_length_helper();
+int invoke_super_helper();
+int invoke_interface_helper();
+int iget_iput_helper(int flag);
+int check_cast_helper(bool instance);
+int new_array_helper();
+
+int common_returnFromMethod();
+
+/*!
+\brief dump helper functions
+
+*/
+int performCGWorklist() {
+    filled_new_array_notimpl();
+    freeShortMap();
+    const_string_resolve();
+    freeShortMap();
+
+    resolve_class2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, 0);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_VIRTUAL);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_DIRECT);
+    freeShortMap();
+    resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_STATIC);
+    freeShortMap();
+    resolve_inst_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true);
+    freeShortMap();
+    resolve_static_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true);
+    freeShortMap();
+    throw_exception_message(PhysicalReg_ECX, PhysicalReg_EAX, true, PhysicalReg_Null, true);
+    freeShortMap();
+    throw_exception(PhysicalReg_ECX, PhysicalReg_EAX, PhysicalReg_Null, true);
+    freeShortMap();
+    new_instance_needinit();
+    freeShortMap();
+    return 0;
+}
+
+int aput_object_count;
+int common_periodicChecks_entry();
+int common_periodicChecks4();
+/*!
+\brief for debugging purpose, dump the sequence of native code for each bytecode
+
+*/
+int ncgMethodFake(Method* method) {
+    //to measure code size expansion, no need to patch up labels
+    methodDataWorklist = NULL;
+    globalShortWorklist = NULL;
+    globalNCGWorklist = NULL;
+    streamMethodStart = stream;
+
+    //initialize mapFromBCtoNCG
+    memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0]));
+    unsigned int i;
+    u2* rStart = (u2*)malloc(5*sizeof(u2));
+    if(rStart == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    rPC = rStart;
+    method->insns = rStart;
+    for(i = 0; i < 5; i++) *rPC++ = 0;
+    for(i = 0; i < 256; i++) {
+        rPC = rStart;
+        //modify the opcode
+        char* tmp = (char*)rStart;
+        *tmp++ = i;
+        *tmp = i;
+        inst = FETCH(0);
+        char* tmpStart = stream;
+        lowerByteCode(method); //use inst, rPC, method, modify rPC
+        int size_in_u2 = rPC - rStart;
+        if(stream - tmpStart  > 0)
+            ALOGI("LOWER bytecode %x size in u2: %d ncg size in byte: %d", i, size_in_u2, stream - tmpStart);
+    }
+    exit(0);
+}
+
+bool existATryBlock(Method* method, int startPC, int endPC) {
+    const DexCode* pCode = dvmGetMethodCode(method);
+    u4 triesSize = pCode->triesSize;
+    const DexTry* pTries = dexGetTries(pCode);
+    unsigned int i;
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        u4 start = pTry->startAddr; //offsetPC
+        u4 end = start + pTry->insnCount;
+        //if [start, end] overlaps with [startPC, endPC] returns true
+        if((int)end < startPC || (int)start > endPC) { //no overlap
+        } else {
+            return true;
+        }
+    }
+    return false;
+}
+
+int mm_bytecode_size = 0;
+int mm_ncg_size = 0;
+int mm_relocation_size = 0;
+int mm_map_size = 0;
+void resetCodeSize() {
+    mm_bytecode_size = 0;
+    mm_ncg_size = 0;
+    mm_relocation_size = 0;
+    mm_map_size = 0;
+}
+
+bool bytecodeIsRemoved(const Method* method, u4 bytecodeOffset) {
+    if(gDvm.executionMode == kExecutionModeNcgO0) return false;
+    u4 ncgOff = mapFromBCtoNCG[bytecodeOffset];
+    int k = bytecodeOffset+1;
+    u2 insnsSize = dvmGetMethodInsnsSize(method);
+    while(k < insnsSize) {
+        if(mapFromBCtoNCG[k] < 0) {
+            k++;
+            continue;
+        }
+        if(mapFromBCtoNCG[k] == (int)ncgOff) return true;
+        return false;
+    }
+    return false;
+}
+
+int invoke_super_nsm();
+void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG); //forward declaration
+void initGlobalMethods(); //forward declaration
+
+//called once when compiler thread starts up
+void initJIT(const char* curFileName, DvmDex *pDvmDex) {
+    init_common(curFileName, pDvmDex, false);
+}
+
+void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG) {
+    if(!gDvm.constInit) {
+        globalMapNum = 0;
+        globalMap = NULL;
+        initConstDataSec();
+        gDvm.constInit = true;
+    }
+
+    //for initJIT: stream is already set
+    if(!gDvm.commonInit) {
+        initGlobalMethods();
+        gDvm.commonInit = true;
+    }
+}
+
+void initGlobalMethods() {
+    dump_x86_inst = false; /* DEBUG */
+    // generate native code for function ncgGetEIP
+    insertLabel("ncgGetEIP", false);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EDX, true);
+    x86_return();
+
+    //generate code for common labels
+    //jumps within a helper function is treated as short labels
+    globalShortMap = NULL;
+    common_periodicChecks_entry();
+    freeShortMap();
+    common_periodicChecks4();
+    freeShortMap();
+    //common_invokeMethodNoRange();
+    //common_invokeMethodRange();
+
+    if(dump_x86_inst) ALOGI("ArgsDone_Normal start");
+    common_invokeArgsDone(ArgsDone_Normal, false);
+    freeShortMap();
+    if(dump_x86_inst) ALOGI("ArgsDone_Native start");
+    common_invokeArgsDone(ArgsDone_Native, false);
+    freeShortMap();
+    if(dump_x86_inst) ALOGI("ArgsDone_Full start");
+    common_invokeArgsDone(ArgsDone_Full, true/*isJitFull*/);
+    if(dump_x86_inst) ALOGI("ArgsDone_Full end");
+    freeShortMap();
+
+    common_backwardBranch();
+    freeShortMap();
+    common_exceptionThrown();
+    freeShortMap();
+    common_errNullObject();
+    freeShortMap();
+    common_errArrayIndex();
+    freeShortMap();
+    common_errArrayStore();
+    freeShortMap();
+    common_errNegArraySize();
+    freeShortMap();
+    common_errNoSuchMethod();
+    freeShortMap();
+    common_errDivideByZero();
+    freeShortMap();
+    common_gotoBail();
+    freeShortMap();
+    common_gotoBail_0();
+    freeShortMap();
+    invoke_super_nsm();
+    freeShortMap();
+
+    performCGWorklist(); //generate code for helper functions
+    performLabelWorklist(); //it is likely that the common labels will jump to other common labels
+
+    dump_x86_inst = false;
+}
+
+ExecutionMode origMode;
+//when to update streamMethodStart
+bool lowerByteCodeJit(const Method* method, const u2* codePtr, MIR* mir) {
+    rPC = (u2*)codePtr;
+    inst = FETCH(0);
+    traceCurrentMIR = mir;
+    int retCode = lowerByteCode(method);
+    traceCurrentMIR = NULL;
+    freeShortMap();
+    if(retCode >= 0) return false; //handled
+    return true; //not handled
+}
+
+void startOfBasicBlock(BasicBlock* bb) {
+    traceCurrentBB = bb;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        isScratchPhysical = true;
+    } else {
+        isScratchPhysical = false;
+    }
+}
+
+void startOfTrace(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId,
+                  CompilationUnit *cUnit) {
+    origMode = gDvm.executionMode;
+    gDvm.executionMode = kExecutionModeNcgO1;
+    if(gDvm.executionMode == kExecutionModeNcgO0) {
+        isScratchPhysical = true;
+    } else {
+        isScratchPhysical = false;
+    }
+    currentMethod = (Method*)method;
+    currentExceptionBlockIdx = exceptionBlockId;
+    methodDataWorklist = NULL;
+    globalShortWorklist = NULL;
+    globalNCGWorklist = NULL;
+
+    streamMethodStart = stream;
+    //initialize mapFromBCtoNCG
+    memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0]));
+    traceLabelList = labelList;
+    if(gDvm.executionMode == kExecutionModeNcgO1)
+        startOfTraceO1(method, labelList, exceptionBlockId, cUnit);
+}
+
+void endOfTrace(bool freeOnly) {
+    if(freeOnly) {
+        freeLabelWorklist();
+        freeNCGWorklist();
+        freeDataWorklist();
+        freeChainingWorklist();
+    }
+    else {
+        performLabelWorklist();
+        performNCGWorklist(); //handle forward jump (GOTO, IF)
+        performDataWorklist(); //handle SWITCH & FILL_ARRAY_DATA
+        performChainingWorklist();
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        endOfTraceO1();
+    }
+    gDvm.executionMode = origMode;
+}
+
+///////////////////////////////////////////////////////////////////
+//!
+//! each bytecode is translated to a sequence of machine codes
+int lowerByteCode(const Method* method) { //inputs: rPC & inst & stream & streamMethodStart
+    /* offsetPC is used in O1 code generator, where it is defined as the sequence number
+       use a local version to avoid overwriting */
+    int offsetPC = rPC - (u2*)method->insns;
+
+    if(dump_x86_inst)
+        ALOGI("LOWER bytecode %x at offsetPC %x offsetNCG %x @%p",
+              INST_INST(inst), offsetPC, stream - streamMethodStart, stream);
+
+    //update mapFromBCtoNCG
+    offsetNCG = stream - streamMethodStart;
+    if(offsetPC >= BYTECODE_SIZE_PER_METHOD) ALOGE("offsetPC %d exceeds BYTECODE_SIZE_PER_METHOD", offsetPC);
+    mapFromBCtoNCG[offsetPC] = offsetNCG;
+#if defined(ENABLE_TRACING) && defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1);
+#endif
+    //return number of LowOps generated
+    switch (INST_INST(inst)) {
+    case OP_NOP:
+        return op_nop();
+    case OP_MOVE:
+    case OP_MOVE_OBJECT:
+        return op_move();
+    case OP_MOVE_FROM16:
+    case OP_MOVE_OBJECT_FROM16:
+        return op_move_from16();
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT_16:
+        return op_move_16();
+    case OP_MOVE_WIDE:
+        return op_move_wide();
+    case OP_MOVE_WIDE_FROM16:
+        return op_move_wide_from16();
+    case OP_MOVE_WIDE_16:
+        return op_move_wide_16();
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        return op_move_result();
+    case OP_MOVE_RESULT_WIDE:
+        return op_move_result_wide();
+    case OP_MOVE_EXCEPTION:
+        return op_move_exception();
+    case OP_RETURN_VOID:
+    case OP_RETURN_VOID_BARRIER:
+        return op_return_void();
+    case OP_RETURN:
+    case OP_RETURN_OBJECT:
+        return op_return();
+    case OP_RETURN_WIDE:
+        return op_return_wide();
+    case OP_CONST_4:
+        return op_const_4();
+    case OP_CONST_16:
+        return op_const_16();
+    case OP_CONST:
+        return op_const();
+    case OP_CONST_HIGH16:
+        return op_const_high16();
+    case OP_CONST_WIDE_16:
+        return op_const_wide_16();
+    case OP_CONST_WIDE_32:
+        return op_const_wide_32();
+    case OP_CONST_WIDE:
+        return op_const_wide();
+    case OP_CONST_WIDE_HIGH16:
+        return op_const_wide_high16();
+    case OP_CONST_STRING:
+        return op_const_string();
+    case OP_CONST_STRING_JUMBO:
+        return op_const_string_jumbo();
+    case OP_CONST_CLASS:
+        return op_const_class();
+    case OP_MONITOR_ENTER:
+        return op_monitor_enter();
+    case OP_MONITOR_EXIT:
+        return op_monitor_exit();
+    case OP_CHECK_CAST:
+        return op_check_cast();
+    case OP_INSTANCE_OF:
+        return op_instance_of();
+    case OP_ARRAY_LENGTH:
+        return op_array_length();
+    case OP_NEW_INSTANCE:
+        return op_new_instance();
+    case OP_NEW_ARRAY:
+        return op_new_array();
+    case OP_FILLED_NEW_ARRAY:
+        return op_filled_new_array();
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        return op_filled_new_array_range();
+    case OP_FILL_ARRAY_DATA:
+        return op_fill_array_data();
+    case OP_THROW:
+        return op_throw();
+    case OP_THROW_VERIFICATION_ERROR:
+        return op_throw_verification_error();
+    case OP_GOTO:
+        return op_goto();
+    case OP_GOTO_16:
+        return op_goto_16();
+    case OP_GOTO_32:
+        return op_goto_32();
+    case OP_PACKED_SWITCH:
+        return op_packed_switch();
+    case OP_SPARSE_SWITCH:
+        return op_sparse_switch();
+    case OP_CMPL_FLOAT:
+        return op_cmpl_float();
+    case OP_CMPG_FLOAT:
+        return op_cmpg_float();
+    case OP_CMPL_DOUBLE:
+        return op_cmpl_double();
+    case OP_CMPG_DOUBLE:
+        return op_cmpg_double();
+    case OP_CMP_LONG:
+        return op_cmp_long();
+    case OP_IF_EQ:
+        return op_if_eq();
+    case OP_IF_NE:
+        return op_if_ne();
+    case OP_IF_LT:
+        return op_if_lt();
+    case OP_IF_GE:
+        return op_if_ge();
+    case OP_IF_GT:
+        return op_if_gt();
+    case OP_IF_LE:
+        return op_if_le();
+    case OP_IF_EQZ:
+        return op_if_eqz();
+    case OP_IF_NEZ:
+        return op_if_nez();
+    case OP_IF_LTZ:
+        return op_if_ltz();
+    case OP_IF_GEZ:
+        return op_if_gez();
+    case OP_IF_GTZ:
+        return op_if_gtz();
+    case OP_IF_LEZ:
+        return op_if_lez();
+    case OP_AGET:
+        return op_aget();
+    case OP_AGET_WIDE:
+        return op_aget_wide();
+    case OP_AGET_OBJECT:
+        return op_aget_object();
+    case OP_AGET_BOOLEAN:
+        return op_aget_boolean();
+    case OP_AGET_BYTE:
+        return op_aget_byte();
+    case OP_AGET_CHAR:
+        return op_aget_char();
+    case OP_AGET_SHORT:
+        return op_aget_short();
+    case OP_APUT:
+        return op_aput();
+    case OP_APUT_WIDE:
+        return op_aput_wide();
+    case OP_APUT_OBJECT:
+        return op_aput_object();
+    case OP_APUT_BOOLEAN:
+        return op_aput_boolean();
+    case OP_APUT_BYTE:
+        return op_aput_byte();
+    case OP_APUT_CHAR:
+        return op_aput_char();
+    case OP_APUT_SHORT:
+        return op_aput_short();
+    case OP_IGET:
+    case OP_IGET_VOLATILE:
+        return op_iget();
+    case OP_IGET_WIDE:
+        return op_iget_wide(false); // isVolatile==false
+    case OP_IGET_WIDE_VOLATILE:
+        return op_iget_wide(true);  // isVolatile==true
+    case OP_IGET_OBJECT:
+    case OP_IGET_OBJECT_VOLATILE:
+        return op_iget_object();
+    case OP_IGET_BOOLEAN:
+        return op_iget_boolean();
+    case OP_IGET_BYTE:
+        return op_iget_byte();
+    case OP_IGET_CHAR:
+        return op_iget_char();
+    case OP_IGET_SHORT:
+        return op_iget_short();
+    case OP_IPUT:
+    case OP_IPUT_VOLATILE:
+        return op_iput();
+    case OP_IPUT_WIDE:
+        return op_iput_wide(false); // isVolatile==false
+    case OP_IPUT_WIDE_VOLATILE:
+        return op_iput_wide(true);  // isVolatile==true
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_OBJECT_VOLATILE:
+        return op_iput_object();
+    case OP_IPUT_BOOLEAN:
+        return op_iput_boolean();
+    case OP_IPUT_BYTE:
+        return op_iput_byte();
+    case OP_IPUT_CHAR:
+        return op_iput_char();
+    case OP_IPUT_SHORT:
+        return op_iput_short();
+    case OP_SGET:
+    case OP_SGET_VOLATILE:
+        return op_sget();
+    case OP_SGET_WIDE:
+        return op_sget_wide(false); // isVolatile==false
+    case OP_SGET_WIDE_VOLATILE:
+        return op_sget_wide(true);  // isVolatile==true
+    case OP_SGET_OBJECT:
+    case OP_SGET_OBJECT_VOLATILE:
+        return op_sget_object();
+    case OP_SGET_BOOLEAN:
+        return op_sget_boolean();
+    case OP_SGET_BYTE:
+        return op_sget_byte();
+    case OP_SGET_CHAR:
+        return op_sget_char();
+    case OP_SGET_SHORT:
+        return op_sget_short();
+    case OP_SPUT:
+    case OP_SPUT_VOLATILE:
+        return op_sput(false);
+    case OP_SPUT_WIDE:
+        return op_sput_wide(false); // isVolatile==false
+    case OP_SPUT_WIDE_VOLATILE:
+        return op_sput_wide(true);  // isVolatile==true
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_OBJECT_VOLATILE:
+        return op_sput_object();
+    case OP_SPUT_BOOLEAN:
+        return op_sput_boolean();
+    case OP_SPUT_BYTE:
+        return op_sput_byte();
+    case OP_SPUT_CHAR:
+        return op_sput_char();
+    case OP_SPUT_SHORT:
+        return op_sput_short();
+    case OP_INVOKE_VIRTUAL:
+        return op_invoke_virtual();
+    case OP_INVOKE_SUPER:
+        return op_invoke_super();
+    case OP_INVOKE_DIRECT:
+        return op_invoke_direct();
+    case OP_INVOKE_STATIC:
+        return op_invoke_static();
+    case OP_INVOKE_INTERFACE:
+        return op_invoke_interface();
+    case OP_INVOKE_VIRTUAL_RANGE:
+        return op_invoke_virtual_range();
+    case OP_INVOKE_SUPER_RANGE:
+        return op_invoke_super_range();
+    case OP_INVOKE_DIRECT_RANGE:
+        return op_invoke_direct_range();
+    case OP_INVOKE_STATIC_RANGE:
+        return op_invoke_static_range();
+    case OP_INVOKE_INTERFACE_RANGE:
+        return op_invoke_interface_range();
+    case OP_NEG_INT:
+        return op_neg_int();
+    case OP_NOT_INT:
+        return op_not_int();
+    case OP_NEG_LONG:
+        return op_neg_long();
+    case OP_NOT_LONG:
+        return op_not_long();
+    case OP_NEG_FLOAT:
+        return op_neg_float();
+    case OP_NEG_DOUBLE:
+        return op_neg_double();
+    case OP_INT_TO_LONG:
+        return op_int_to_long();
+    case OP_INT_TO_FLOAT:
+        return op_int_to_float();
+    case OP_INT_TO_DOUBLE:
+        return op_int_to_double();
+    case OP_LONG_TO_INT:
+        return op_long_to_int();
+    case OP_LONG_TO_FLOAT:
+        return op_long_to_float();
+    case OP_LONG_TO_DOUBLE:
+        return op_long_to_double();
+    case OP_FLOAT_TO_INT:
+        return op_float_to_int();
+    case OP_FLOAT_TO_LONG:
+        return op_float_to_long();
+    case OP_FLOAT_TO_DOUBLE:
+        return op_float_to_double();
+    case OP_DOUBLE_TO_INT:
+        return op_double_to_int();
+    case OP_DOUBLE_TO_LONG:
+        return op_double_to_long();
+    case OP_DOUBLE_TO_FLOAT:
+        return op_double_to_float();
+    case OP_INT_TO_BYTE:
+        return op_int_to_byte();
+    case OP_INT_TO_CHAR:
+        return op_int_to_char();
+    case OP_INT_TO_SHORT:
+        return op_int_to_short();
+    case OP_ADD_INT:
+        return op_add_int();
+    case OP_SUB_INT:
+        return op_sub_int();
+    case OP_MUL_INT:
+        return op_mul_int();
+    case OP_DIV_INT:
+        return op_div_int();
+    case OP_REM_INT:
+        return op_rem_int();
+    case OP_AND_INT:
+        return op_and_int();
+    case OP_OR_INT:
+        return op_or_int();
+    case OP_XOR_INT:
+        return op_xor_int();
+    case OP_SHL_INT:
+        return op_shl_int();
+    case OP_SHR_INT:
+        return op_shr_int();
+    case OP_USHR_INT:
+        return op_ushr_int();
+    case OP_ADD_LONG:
+        return op_add_long();
+    case OP_SUB_LONG:
+        return op_sub_long();
+    case OP_MUL_LONG:
+        return op_mul_long();
+    case OP_DIV_LONG:
+        return op_div_long();
+    case OP_REM_LONG:
+        return op_rem_long();
+    case OP_AND_LONG:
+        return op_and_long();
+    case OP_OR_LONG:
+        return op_or_long();
+    case OP_XOR_LONG:
+        return op_xor_long();
+    case OP_SHL_LONG:
+        return op_shl_long();
+    case OP_SHR_LONG:
+        return op_shr_long();
+    case OP_USHR_LONG:
+        return op_ushr_long();
+    case OP_ADD_FLOAT:
+        return op_add_float();
+    case OP_SUB_FLOAT:
+        return op_sub_float();
+    case OP_MUL_FLOAT:
+        return op_mul_float();
+    case OP_DIV_FLOAT:
+        return op_div_float();
+    case OP_REM_FLOAT:
+        return op_rem_float();
+    case OP_ADD_DOUBLE:
+        return op_add_double();
+    case OP_SUB_DOUBLE:
+        return op_sub_double();
+    case OP_MUL_DOUBLE:
+        return op_mul_double();
+    case OP_DIV_DOUBLE:
+        return op_div_double();
+    case OP_REM_DOUBLE:
+        return op_rem_double();
+    case OP_ADD_INT_2ADDR:
+        return op_add_int_2addr();
+    case OP_SUB_INT_2ADDR:
+        return op_sub_int_2addr();
+    case OP_MUL_INT_2ADDR:
+        return op_mul_int_2addr();
+    case OP_DIV_INT_2ADDR:
+        return op_div_int_2addr();
+    case OP_REM_INT_2ADDR:
+        return op_rem_int_2addr();
+    case OP_AND_INT_2ADDR:
+        return op_and_int_2addr();
+    case OP_OR_INT_2ADDR:
+        return op_or_int_2addr();
+    case OP_XOR_INT_2ADDR:
+        return op_xor_int_2addr();
+    case OP_SHL_INT_2ADDR:
+        return op_shl_int_2addr();
+    case OP_SHR_INT_2ADDR:
+        return op_shr_int_2addr();
+    case OP_USHR_INT_2ADDR:
+        return op_ushr_int_2addr();
+    case OP_ADD_LONG_2ADDR:
+        return op_add_long_2addr();
+    case OP_SUB_LONG_2ADDR:
+        return op_sub_long_2addr();
+    case OP_MUL_LONG_2ADDR:
+        return op_mul_long_2addr();
+    case OP_DIV_LONG_2ADDR:
+        return op_div_long_2addr();
+    case OP_REM_LONG_2ADDR:
+        return op_rem_long_2addr();
+    case OP_AND_LONG_2ADDR:
+        return op_and_long_2addr();
+    case OP_OR_LONG_2ADDR:
+        return op_or_long_2addr();
+    case OP_XOR_LONG_2ADDR:
+        return op_xor_long_2addr();
+    case OP_SHL_LONG_2ADDR:
+        return op_shl_long_2addr();
+    case OP_SHR_LONG_2ADDR:
+        return op_shr_long_2addr();
+    case OP_USHR_LONG_2ADDR:
+        return op_ushr_long_2addr();
+    case OP_ADD_FLOAT_2ADDR:
+        return op_add_float_2addr();
+    case OP_SUB_FLOAT_2ADDR:
+        return op_sub_float_2addr();
+    case OP_MUL_FLOAT_2ADDR:
+        return op_mul_float_2addr();
+    case OP_DIV_FLOAT_2ADDR:
+        return op_div_float_2addr();
+    case OP_REM_FLOAT_2ADDR:
+        return op_rem_float_2addr();
+    case OP_ADD_DOUBLE_2ADDR:
+        return op_add_double_2addr();
+    case OP_SUB_DOUBLE_2ADDR:
+        return op_sub_double_2addr();
+    case OP_MUL_DOUBLE_2ADDR:
+        return op_mul_double_2addr();
+    case OP_DIV_DOUBLE_2ADDR:
+        return op_div_double_2addr();
+    case OP_REM_DOUBLE_2ADDR:
+        return op_rem_double_2addr();
+    case OP_ADD_INT_LIT16:
+        return op_add_int_lit16();
+    case OP_RSUB_INT:
+        return op_rsub_int();
+    case OP_MUL_INT_LIT16:
+        return op_mul_int_lit16();
+    case OP_DIV_INT_LIT16:
+        return op_div_int_lit16();
+    case OP_REM_INT_LIT16:
+        return op_rem_int_lit16();
+    case OP_AND_INT_LIT16:
+        return op_and_int_lit16();
+    case OP_OR_INT_LIT16:
+        return op_or_int_lit16();
+    case OP_XOR_INT_LIT16:
+        return op_xor_int_lit16();
+    case OP_ADD_INT_LIT8:
+        return op_add_int_lit8();
+    case OP_RSUB_INT_LIT8:
+        return op_rsub_int_lit8();
+    case OP_MUL_INT_LIT8:
+        return op_mul_int_lit8();
+    case OP_DIV_INT_LIT8:
+        return op_div_int_lit8();
+    case OP_REM_INT_LIT8:
+        return op_rem_int_lit8();
+    case OP_AND_INT_LIT8:
+        return op_and_int_lit8();
+    case OP_OR_INT_LIT8:
+        return op_or_int_lit8();
+    case OP_XOR_INT_LIT8:
+        return op_xor_int_lit8();
+    case OP_SHL_INT_LIT8:
+        return op_shl_int_lit8();
+    case OP_SHR_INT_LIT8:
+        return op_shr_int_lit8();
+    case OP_USHR_INT_LIT8:
+        return op_ushr_int_lit8();
+    case OP_EXECUTE_INLINE:
+        return op_execute_inline(false);
+    case OP_EXECUTE_INLINE_RANGE:
+        return op_execute_inline(true);
+    case OP_BREAKPOINT:
+        ALOGE("found bytecode OP_BREAKPOINT");
+        dvmAbort();
+    case OP_INVOKE_OBJECT_INIT_RANGE:
+        return op_invoke_object_init_range();
+    case OP_IGET_QUICK:
+        return op_iget_quick();
+    case OP_IGET_WIDE_QUICK:
+        return op_iget_wide_quick();
+    case OP_IGET_OBJECT_QUICK:
+        return op_iget_object_quick();
+    case OP_IPUT_QUICK:
+        return op_iput_quick();
+    case OP_IPUT_WIDE_QUICK:
+        return op_iput_wide_quick();
+    case OP_IPUT_OBJECT_QUICK:
+        return op_iput_object_quick();
+    case OP_INVOKE_VIRTUAL_QUICK:
+        return op_invoke_virtual_quick();
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        return op_invoke_virtual_quick_range();
+    case OP_INVOKE_SUPER_QUICK:
+        return op_invoke_super_quick();
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        return op_invoke_super_quick_range();
+    }
+
+    ALOGE("No JIT support for bytecode %x at offsetPC %x",
+          INST_INST(inst), offsetPC);
+    return -1;
+}
+int op_nop() {
+    rPC++;
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/Lower.h b/vm/compiler/codegen/x86/Lower.h
new file mode 100644
index 0000000..630ccb9
--- /dev/null
+++ b/vm/compiler/codegen/x86/Lower.h
@@ -0,0 +1,1240 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file lower.h
+    \brief A header file to define interface between lowering and register allocator
+*/
+
+#ifndef _DALVIK_LOWER
+#define _DALVIK_LOWER
+
+#define CODE_CACHE_PADDING 1024 //code space for a single bytecode
+// comment out for phase 1 porting
+#define PREDICTED_CHAINING
+#define JIT_CHAIN
+
+#define NUM_DEPENDENCIES 24 /* max number of dependencies from a LowOp */
+//compilaton flags used by NCG O1
+#define DUMP_EXCEPTION //to measure performance, required to have correct exception handling
+/*! multiple versions for hardcoded registers */
+#define HARDREG_OPT
+#define CFG_OPT
+/*! remove redundant move ops when accessing virtual registers */
+#define MOVE_OPT
+/*! remove redundant spill of virtual registers */
+#define SPILL_OPT
+#define XFER_OPT
+//#define DSE_OPT //no perf improvement for cme
+/*! use live range analysis to allocate registers */
+#define LIVERANGE_OPT
+/*! remove redundant null check */
+#define NULLCHECK_OPT
+//#define BOUNDCHECK_OPT
+/*! optimize the access to glue structure */
+#define GLUE_OPT
+#define CALL_FIX
+#define NATIVE_FIX
+#define INVOKE_FIX //optimization
+#define GETVR_FIX //optimization
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+#include "AnalysisO1.h"
+#include "compiler/CompilerIR.h"
+
+//compilation flags for debugging
+//#define DEBUG_INFO
+//#define DEBUG_CALL_STACK
+//#define DEBUG_IGET_OBJ
+//#define DEBUG_NCG_CODE_SIZE
+//#define DEBUG_NCG
+//#define DEBUG_NCG_1
+//#define DEBUG_LOADING
+//#define USE_INTERPRETER
+//#define DEBUG_EACH_BYTECODE
+
+/*! registers for functions are hardcoded */
+#define HARDCODE_REG_CALL
+#define HARDCODE_REG_SHARE
+#define HARDCODE_REG_HELPER
+
+#define PhysicalReg_FP PhysicalReg_EDI
+#define PhysicalReg_Glue PhysicalReg_EBP
+
+//COPIED from interp/InterpDefs.h
+#define FETCH(_offset) (rPC[(_offset)])
+#define INST_INST(_inst) ((_inst) & 0xff)
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+//#include "vm/mterp/common/asm-constants.h"
+#define offEBP_self 8
+#define offEBP_spill -56
+#define offThread_exception 68
+#define offClassObject_descriptor 24
+#define offArrayObject_length 8
+#ifdef PROFILE_FIELD_ACCESS
+#define offStaticField_value 24
+#define offInstField_byteOffset 24
+#else
+#define offStaticField_value 16
+#define offInstField_byteOffset 16
+#endif
+
+#ifdef EASY_GDB
+#define offStackSaveArea_prevFrame 4
+#define offStackSaveArea_savedPc 8
+#define offStackSaveArea_method 12
+#define offStackSaveArea_localRefTop 16 // -> StackSaveArea.xtra.locakRefCookie
+#define offStackSaveArea_returnAddr 20
+#define offStackSaveArea_isDebugInterpreted 24
+#define sizeofStackSaveArea 24
+#else
+#define offStackSaveArea_prevFrame 0
+#define offStackSaveArea_savedPc 4
+#define offStackSaveArea_method 8
+#define offStackSaveArea_localRefTop 12 // -> StackSaveArea.xtra.locakRefCookie
+#define offStackSaveArea_returnAddr 16
+#define offStackSaveArea_isDebugInterpreted 20
+#define sizeofStackSaveArea 20
+#endif
+
+#define offClassObject_status 44
+#define offClassObject_accessFlags 32
+#ifdef MTERP_NO_UNALIGN_64
+#define offArrayObject_contents 16
+#else
+#define offArrayObject_contents 12
+#endif
+
+#define offField_clazz 0
+#define offObject_clazz 0
+#define offClassObject_vtable 116
+#define offClassObject_pDvmDex 40
+#define offClassObject_super 72
+#define offClassObject_vtableCount 112
+#define offMethod_name 16
+#define offMethod_accessFlags 4
+#define offMethod_methodIndex 8
+#define offMethod_registersSize 10
+#define offMethod_outsSize 12
+#define offGlue_interpStackEnd 32
+#define offThread_inJitCodeCache 124
+#define offThread_jniLocal_nextEntry 168
+#define offMethod_insns 32
+#ifdef ENABLE_TRACING
+#define offMethod_insns_bytecode 44
+#define offMethod_insns_ncg 48
+#endif
+
+#define offGlue_pc     0
+#define offGlue_fp     4
+#define offGlue_retval 8
+
+#define offThread_curFrame 4
+#define offGlue_method 16
+#define offGlue_methodClassDex 20
+#define offGlue_self 24
+#define offGlue_pSelfSuspendCount 36
+#define offGlue_cardTable 40
+#define offGlue_pDebuggerActive 44
+#define offGlue_pActiveProfilers 48
+#define offGlue_entryPoint 52
+#define offGlue_icRechainCount 84
+#define offGlue_espEntry 88
+#define offGlue_spillRegion 92
+#define offDvmDex_pResStrings 8
+#define offDvmDex_pResClasses 12
+#define offDvmDex_pResMethods 16
+#define offDvmDex_pResFields  20
+#define offMethod_clazz       0
+
+// Definitions must be consistent with vm/mterp/x86/header.S
+#define FRAME_SIZE     124
+
+typedef enum ArgsDoneType {
+    ArgsDone_Normal = 0,
+    ArgsDone_Native,
+    ArgsDone_Full
+} ArgsDoneType;
+
+/*! An enum type
+    to list bytecodes for AGET, APUT
+*/
+typedef enum ArrayAccess {
+    AGET, AGET_WIDE, AGET_CHAR, AGET_SHORT, AGET_BOOLEAN, AGET_BYTE,
+    APUT, APUT_WIDE, APUT_CHAR, APUT_SHORT, APUT_BOOLEAN, APUT_BYTE
+} ArrayAccess;
+/*! An enum type
+    to list bytecodes for IGET, IPUT
+*/
+typedef enum InstanceAccess {
+    IGET, IGET_WIDE, IPUT, IPUT_WIDE
+} InstanceAccess;
+/*! An enum type
+    to list bytecodes for SGET, SPUT
+*/
+typedef enum StaticAccess {
+    SGET, SGET_WIDE, SPUT, SPUT_WIDE
+} StaticAccess;
+
+typedef enum JmpCall_type {
+    JmpCall_uncond = 1,
+    JmpCall_cond,
+    JmpCall_reg, //jump reg32
+    JmpCall_call
+} JmpCall_type;
+
+////////////////////////////////////////////////////////////////
+/* data structure for native codes */
+/* Due to space considation, a lowered op (LowOp) has two operands (LowOpnd), depending on
+   the type of the operand, LowOpndReg or LowOpndImm or LowOpndMem will follow */
+/*! type of an operand can be immediate, register or memory */
+typedef enum LowOpndType {
+  LowOpndType_Imm = 0,
+  LowOpndType_Reg,
+  LowOpndType_Mem,
+  LowOpndType_Label,
+  LowOpndType_NCG,
+  LowOpndType_Chain
+} LowOpndType;
+typedef enum LowOpndDefUse {
+  LowOpndDefUse_Def = 0,
+  LowOpndDefUse_Use,
+  LowOpndDefUse_UseDef
+} LowOpndDefUse;
+
+/*!
+\brief base data structure for an operand */
+typedef struct LowOpnd {
+  LowOpndType type;
+  OpndSize size;
+  LowOpndDefUse defuse;
+} LowOpnd;
+/*!
+\brief data structure for a register operand */
+typedef struct LowOpndReg {
+  LowOpndRegType regType;
+  int logicalReg;
+  int physicalReg;
+} LowOpndReg;
+/*!
+\brief data structure for an immediate operand */
+typedef struct LowOpndImm {
+  union {
+    s4 value;
+    unsigned char bytes[4];
+  };
+} LowOpndImm;
+
+typedef struct LowOpndNCG {
+  union {
+    s4 value;
+    unsigned char bytes[4];
+  };
+} LowOpndNCG;
+
+#define LABEL_SIZE 256
+typedef struct LowOpndLabel {
+  char label[LABEL_SIZE];
+  bool isLocal;
+} LowOpndLabel;
+
+/* get ready for optimizations at LIR
+   add MemoryAccessType & virtualRegNum to memory operands */
+typedef enum MemoryAccessType {
+  MemoryAccess_GLUE,
+  MemoryAccess_VR,
+  MemoryAccess_SPILL,
+  MemoryAccess_Unknown
+} MemoryAccessType;
+typedef enum UseDefEntryType {
+  UseDefType_Ctrl = 0,
+  UseDefType_Float,
+  UseDefType_MemVR,
+  UseDefType_MemSpill,
+  UseDefType_MemUnknown,
+  UseDefType_Reg
+} UseDefEntryType;
+typedef struct UseDefProducerEntry {
+  UseDefEntryType type;
+  int index; //enum PhysicalReg for "Reg" type
+  int producerSlot;
+} UseDefProducerEntry;
+#define MAX_USE_PER_ENTRY 50 /* at most 10 uses for each entry */
+typedef struct UseDefUserEntry {
+  UseDefEntryType type;
+  int index;
+  int useSlots[MAX_USE_PER_ENTRY];
+  int num_uses_per_entry;
+} UseDefUserEntry;
+
+/*!
+\brief data structure for a memory operand */
+typedef struct LowOpndMem {
+  LowOpndImm m_disp;
+  LowOpndImm m_scale;
+  LowOpndReg m_index;
+  LowOpndReg m_base;
+  bool hasScale;
+  MemoryAccessType mType;
+  int index;
+} LowOpndMem;
+
+typedef enum AtomOpCode {
+    ATOM_PSEUDO_CHAINING_CELL_BACKWARD_BRANCH = -15,
+    ATOM_NORMAL_ALU = -14,
+    ATOM_PSEUDO_ENTRY_BLOCK = -13,
+    ATOM_PSEUDO_EXIT_BLOCK = -12,
+    ATOM_PSEUDO_TARGET_LABEL = -11,
+    ATOM_PSEUDO_CHAINING_CELL_HOT = -10,
+    ATOM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED = -9,
+    ATOM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON = -8,
+    ATOM_PSEUDO_CHAINING_CELL_NORMAL = -7,
+    ATOM_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6,
+    ATOM_PSEUDO_ALIGN4 = -5,
+    ATOM_PSEUDO_PC_RECONSTRUCTION_CELL = -4,
+    ATOM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3,
+    ATOM_PSEUDO_EH_BLOCK_LABEL = -2,
+    ATOM_PSEUDO_NORMAL_BLOCK_LABEL = -1,
+    ATOM_NORMAL,
+} AtomOpCode;
+
+typedef enum DependencyType {
+  Dependency_RAW,
+  Dependency_WAW,
+  Dependency_WAR,
+  Dependency_FLAG
+} DependencyType;
+typedef struct DependencyStruct {
+  DependencyType dType;
+  int nodeId;
+  int latency;
+} DependencyStruct;
+
+typedef struct LowOpBlock {
+  LIR generic;
+  Mnemonic opCode;
+  AtomOpCode opCode2;
+} LowOpBlock;
+
+/*!
+\brief data structure for a lowered operation */
+typedef struct LowOp {
+  LIR generic;
+  Mnemonic opCode;
+  AtomOpCode opCode2;
+  LowOpnd opnd1;
+  LowOpnd opnd2;
+  int numOperands;
+} LowOp;
+
+typedef struct LowOpLabel {
+  LowOp lop;
+  LowOpndLabel labelOpnd;
+}LowOpLabel;
+
+typedef struct LowOpNCG {
+  LowOp lop;
+  LowOpndNCG ncgOpnd;
+}LowOpNCG;
+
+typedef struct LowOpBlockLabel {
+  LowOpBlock lop;
+  LowOpndImm immOpnd;
+} LowOpBlockLabel;
+
+typedef struct LowOpImm {
+  LowOp lop;
+  LowOpndImm immOpnd;
+} LowOpImm;
+
+typedef struct LowOpMem {
+  LowOp lop;
+  LowOpndMem memOpnd;
+} LowOpMem;
+
+typedef struct LowOpReg {
+  LowOp lop;
+  LowOpndReg regOpnd;
+} LowOpReg;
+
+typedef struct LowOpImmImm {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpImmImm;
+
+typedef struct LowOpImmReg {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpImmReg;
+
+typedef struct LowOpImmMem {
+  LowOp lop;
+  LowOpndImm immOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpImmMem;
+
+typedef struct LowOpRegImm {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpRegImm;
+
+typedef struct LowOpRegReg {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpRegReg;
+
+typedef struct LowOpRegMem {
+  LowOp lop;
+  LowOpndReg regOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpRegMem;
+
+typedef struct LowOpMemImm {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndImm immOpnd2;
+} LowOpMemImm;
+
+typedef struct LowOpMemReg {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndReg regOpnd2;
+} LowOpMemReg;
+
+typedef struct LowOpMemMem {
+  LowOp lop;
+  LowOpndMem memOpnd1;
+  LowOpndMem memOpnd2;
+} LowOpMemMem;
+
+/*!
+\brief data structure for labels used when lowering a method
+
+four label maps are defined: globalMap globalShortMap globalWorklist globalShortWorklist
+globalMap: global labels where codePtr points to the label
+           freeLabelMap called in clearNCG
+globalWorklist: global labels where codePtr points to an instruciton using the label
+  standalone NCG -------
+                accessed by insertLabelWorklist & performLabelWorklist
+  code cache ------
+                inserted by performLabelWorklist(false),
+                handled & cleared by generateRelocation in NcgFile.c
+globalShortMap: local labels where codePtr points to the label
+                freeShortMap called after generation of one bytecode
+globalShortWorklist: local labels where codePtr points to an instruction using the label
+                accessed by insertShortWorklist & insertLabel
+definition of local label: life time of the label is within a bytecode or within a helper function
+extra label maps are used by code cache:
+  globalDataWorklist VMAPIWorklist
+*/
+typedef struct LabelMap {
+  char label[LABEL_SIZE];
+  char* codePtr; //code corresponding to the label or code that uses the label
+  struct LabelMap* nextItem;
+  OpndSize size;
+  uint  addend;
+} LabelMap;
+/*!
+\brief data structure to handle forward jump (GOTO, IF)
+
+accessed by insertNCGWorklist & performNCGWorklist
+*/
+typedef struct NCGWorklist {
+  //when WITH_JIT, relativePC stores the target basic block id
+  s4 relativePC; //relative offset in bytecode
+  int offsetPC;  //PC in bytecode
+  int offsetNCG; //PC in native code
+  char* codePtr; //code for native jump instruction
+  struct NCGWorklist* nextItem;
+  OpndSize size;
+}NCGWorklist;
+/*!
+\brief data structure to handle SWITCH & FILL_ARRAY_DATA
+
+two data worklist are defined: globalDataWorklist (used by code cache) & methodDataWorklist
+methodDataWorklist is accessed by insertDataWorklist & performDataWorklist
+*/
+typedef struct DataWorklist {
+  s4 relativePC; //relative offset in bytecode to access the data
+  int offsetPC;  //PC in bytecode
+  int offsetNCG; //PC in native code
+  char* codePtr; //code for native instruction add_imm_reg imm, %edx
+  char* codePtr2;//code for native instruction add_reg_reg %eax, %edx for SWITCH
+                 //                            add_imm_reg imm, %edx for FILL_ARRAY_DATA
+  struct DataWorklist* nextItem;
+}DataWorklist;
+#ifdef ENABLE_TRACING
+typedef struct MapWorklist {
+  u4 offsetPC;
+  u4 offsetNCG;
+  int isStartOfPC; //1 --> true 0 --> false
+  struct MapWorklist* nextItem;
+} MapWorklist;
+#endif
+
+#define BUFFER_SIZE 1024 //# of Low Ops buffered
+//the following three numbers are hardcoded, please CHECK
+#define BYTECODE_SIZE_PER_METHOD 81920
+#define NATIVE_SIZE_PER_DEX 19000000 //FIXME for core.jar: 16M --> 18M for O1
+#define NATIVE_SIZE_FOR_VM_STUBS 100000
+#define MAX_HANDLER_OFFSET 1024 //maximal number of handler offsets
+
+extern int LstrClassCastExceptionPtr, LstrInstantiationErrorPtr, LstrInternalError, LstrFilledNewArrayNotImpl;
+extern int LstrArithmeticException, LstrArrayIndexException, LstrArrayStoreException, LstrStringIndexOutOfBoundsException;
+extern int LstrDivideByZero, LstrNegativeArraySizeException, LstrNoSuchMethodError, LstrNullPointerException;
+extern int LdoubNeg, LvaluePosInfLong, LvalueNegInfLong, LvalueNanLong, LshiftMask, Lvalue64, L64bits, LintMax, LintMin;
+
+extern LabelMap* globalMap;
+extern LabelMap* globalShortMap;
+extern LabelMap* globalWorklist;
+extern LabelMap* globalShortWorklist;
+extern NCGWorklist* globalNCGWorklist;
+extern DataWorklist* methodDataWorklist;
+#ifdef ENABLE_TRACING
+extern MapWorklist* methodMapWorklist;
+#endif
+extern PhysicalReg scratchRegs[4];
+
+#define C_SCRATCH_1 scratchRegs[0]
+#define C_SCRATCH_2 scratchRegs[1]
+#define C_SCRATCH_3 scratchRegs[2] //scratch reg inside callee
+
+extern LowOp* ops[BUFFER_SIZE];
+extern bool isScratchPhysical;
+extern u2* rPC;
+extern u2 inst;
+extern int offsetPC;
+extern int offsetNCG;
+extern int mapFromBCtoNCG[BYTECODE_SIZE_PER_METHOD];
+extern char* streamStart;
+
+extern char* streamCode;
+
+extern char* streamMethodStart; //start of the method
+extern char* stream; //current stream pointer
+extern char* streamMisPred;
+extern int lowOpTimeStamp;
+extern Method* currentMethod;
+extern int currentExceptionBlockIdx;
+
+extern int globalMapNum;
+extern int globalWorklistNum;
+extern int globalDataWorklistNum;
+extern int globalPCWorklistNum;
+extern int chainingWorklistNum;
+extern int VMAPIWorklistNum;
+
+extern LabelMap* globalDataWorklist;
+extern LabelMap* globalPCWorklist;
+extern LabelMap* chainingWorklist;
+extern LabelMap* VMAPIWorklist;
+
+extern int ncgClassNum;
+extern int ncgMethodNum;
+
+extern LowOp* lirTable[200]; //Number of LIRs for all bytecodes do not exceed 200
+extern int num_lirs_in_table;
+
+bool existATryBlock(Method* method, int startPC, int endPC);
+// interface between register allocator & lowering
+extern int num_removed_nullCheck;
+
+int registerAlloc(int type, int reg, bool isPhysical, bool updateRef);
+int registerAllocMove(int reg, int type, bool isPhysical, int srcReg);
+int checkVirtualReg(int reg, LowOpndRegType type, int updateRef); //returns the physical register
+int updateRefCount(int reg, LowOpndRegType type);
+int updateRefCount2(int reg, int type, bool isPhysical);
+int spillVirtualReg(int vrNum, LowOpndRegType type, bool updateTable);
+int isVirtualRegConstant(int regNum, LowOpndRegType type, int* valuePtr, bool updateRef);
+int checkTempReg(int reg, int type, bool isPhysical, int vA);
+bool checkTempReg2(int reg, int type, bool isPhysical, int physicalRegForVR);
+int freeReg(bool spillGL);
+int nextVersionOfHardReg(PhysicalReg pReg, int refCount);
+int updateVirtualReg(int reg, LowOpndRegType type);
+void setVRNullCheck(int regNum, OpndSize size);
+bool isVRNullCheck(int regNum, OpndSize size);
+void setVRBoundCheck(int vr_array, int vr_index);
+bool isVRBoundCheck(int vr_array, int vr_index);
+int requestVRFreeDelay(int regNum, u4 reason);
+void cancelVRFreeDelayRequest(int regNum, u4 reason);
+bool getVRFreeDelayRequested(int regNum);
+bool isGlueHandled(int glue_reg);
+void resetGlue(int glue_reg);
+void updateGlue(int reg, bool isPhysical, int glue_reg);
+int updateVRAtUse(int reg, LowOpndRegType pType, int regAll);
+int touchEcx();
+int touchEax();
+int touchEdx();
+int beforeCall(const char* target);
+int afterCall(const char* target);
+void startBranch();
+void endBranch();
+void rememberState(int);
+void goToState(int);
+void transferToState(int);
+void globalVREndOfBB(const Method*);
+void constVREndOfBB();
+void startNativeCode(int num, int type);
+void endNativeCode();
+void donotSpillReg(int physicalReg);
+void doSpillReg(int physicalReg);
+
+#define XMM_1 PhysicalReg_XMM0
+#define XMM_2 PhysicalReg_XMM1
+#define XMM_3 PhysicalReg_XMM2
+#define XMM_4 PhysicalReg_XMM3
+
+/////////////////////////////////////////////////////////////////////////////////
+//LR[reg] = disp + PR[base_reg] or disp + LR[base_reg]
+void load_effective_addr(int disp, int base_reg, bool isBasePhysical,
+                          int reg, bool isPhysical);
+void load_effective_addr_scale(int base_reg, bool isBasePhysical,
+                                int index_reg, bool isIndexPhysical, int scale,
+                                int reg, bool isPhysical);
+void load_fpu_cw(int disp, int base_reg, bool isBasePhysical);
+void store_fpu_cw(bool checkException, int disp, int base_reg, bool isBasePhysical);
+void convert_integer(OpndSize srcSize, OpndSize dstSize);
+void load_fp_stack(LowOp* op, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void load_int_fp_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void load_int_fp_stack_imm(OpndSize size, int imm);
+void store_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void store_int_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+
+void load_fp_stack_VR(OpndSize size, int vA);
+void load_int_fp_stack_VR(OpndSize size, int vA);
+void store_fp_stack_VR(bool pop, OpndSize size, int vA);
+void store_int_fp_stack_VR(bool pop, OpndSize size, int vA);
+void compare_VR_ss_reg(int vA, int reg, bool isPhysical);
+void compare_VR_sd_reg(int vA, int reg, bool isPhysical);
+void fpu_VR(ALU_Opcode opc, OpndSize size, int vA);
+void compare_reg_mem(LowOp* op, OpndSize size, int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical);
+void compare_mem_reg(OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           int reg, bool isPhysical);
+void compare_VR_reg(OpndSize size,
+                           int vA,
+                           int reg, bool isPhysical);
+void compare_imm_reg(OpndSize size, int imm,
+                           int reg, bool isPhysical);
+void compare_imm_mem(OpndSize size, int imm,
+                           int disp, int base_reg, bool isBasePhysical);
+void compare_imm_VR(OpndSize size, int imm,
+                           int vA);
+void compare_reg_reg(int reg1, bool isPhysical1,
+                           int reg2, bool isPhysical2);
+void compare_reg_reg_16(int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2);
+void compare_ss_mem_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                              int reg, bool isPhysical);
+void compare_ss_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                              int reg2, bool isPhysical2);
+void compare_sd_mem_with_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                              int reg, bool isPhysical);
+void compare_sd_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                              int reg2, bool isPhysical2);
+void compare_fp_stack(bool pop, int reg, bool isDouble);
+void test_imm_reg(OpndSize size, int imm, int reg, bool isPhysical);
+void test_imm_mem(OpndSize size, int imm, int disp, int reg, bool isPhysical);
+
+void conditional_move_reg_to_reg(OpndSize size, ConditionCode cc, int reg1, bool isPhysical1, int reg, bool isPhysical);
+void move_ss_mem_to_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                        int reg, bool isPhysical);
+void move_ss_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+LowOpRegMem* move_ss_mem_to_reg_noalloc(int disp, int base_reg, bool isBasePhysical,
+                         MemoryAccessType mType, int mIndex,
+                         int reg, bool isPhysical);
+LowOpMemReg* move_ss_reg_to_mem_noalloc(int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical,
+                         MemoryAccessType mType, int mIndex);
+void move_sd_mem_to_reg(int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical);
+void move_sd_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+
+void conditional_jump(ConditionCode cc, const char* target, bool isShortTerm);
+void unconditional_jump(const char* target, bool isShortTerm);
+void conditional_jump_int(ConditionCode cc, int target, OpndSize size);
+void unconditional_jump_int(int target, OpndSize size);
+void unconditional_jump_reg(int reg, bool isPhysical);
+void call(const char* target);
+void call_reg(int reg, bool isPhysical);
+void call_reg_noalloc(int reg, bool isPhysical);
+void call_mem(int disp, int reg, bool isPhysical);
+void x86_return();
+
+void alu_unary_reg(OpndSize size, ALU_Opcode opc, int reg, bool isPhysical);
+void alu_unary_mem(LowOp* op, OpndSize size, ALU_Opcode opc, int disp, int base_reg, bool isBasePhysical);
+
+void alu_binary_imm_mem(OpndSize size, ALU_Opcode opc,
+                         int imm, int disp, int base_reg, bool isBasePhysical);
+void alu_binary_imm_reg(OpndSize size, ALU_Opcode opc, int imm, int reg, bool isPhysical);
+void alu_binary_mem_reg(OpndSize size, ALU_Opcode opc,
+                         int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical);
+void alu_binary_VR_reg(OpndSize size, ALU_Opcode opc, int vA, int reg, bool isPhysical);
+void alu_sd_binary_VR_reg(ALU_Opcode opc, int vA, int reg, bool isPhysical, bool isSD);
+void alu_binary_reg_reg(OpndSize size, ALU_Opcode opc,
+                         int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2);
+void alu_binary_reg_mem(OpndSize size, ALU_Opcode opc,
+                         int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical);
+
+void fpu_mem(LowOp* op, ALU_Opcode opc, OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void alu_ss_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                            int reg2, bool isPhysical2);
+void alu_sd_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                            int reg2, bool isPhysical2);
+
+void push_mem_to_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical);
+void push_reg_to_stack(OpndSize size, int reg, bool isPhysical);
+
+//returns the pointer to end of the native code
+void move_reg_to_mem(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical);
+LowOpRegMem* move_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void moves_mem_to_reg(LowOp* op, OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical);
+void movez_mem_disp_scale_to_reg(OpndSize size,
+                      int base_reg, bool isBasePhysical,
+                      int disp, int index_reg, bool isIndexPhysical, int scale,
+                      int reg, bool isPhysical);
+void moves_mem_disp_scale_to_reg(OpndSize size,
+                      int base_reg, bool isBasePhysical,
+                      int disp, int index_reg, bool isIndexPhysical, int scale,
+                      int reg, bool isPhysical);
+void move_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void move_reg_to_reg_noalloc(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2);
+void move_mem_scale_to_reg(OpndSize size,
+                            int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                            int reg, bool isPhysical);
+void move_mem_disp_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical);
+void move_reg_to_mem_scale(OpndSize size,
+                            int reg, bool isPhysical,
+                            int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale);
+void move_reg_to_mem_disp_scale(OpndSize size,
+                            int reg, bool isPhysical,
+                            int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale);
+void move_imm_to_mem(OpndSize size, int imm,
+                      int disp, int base_reg, bool isBasePhysical);
+void set_VR_to_imm(u2 vA, OpndSize size, int imm);
+void set_VR_to_imm_noalloc(u2 vA, OpndSize size, int imm);
+void set_VR_to_imm_noupdateref(LowOp* op, u2 vA, OpndSize size, int imm);
+void move_imm_to_reg(OpndSize size, int imm, int reg, bool isPhysical);
+void move_imm_to_reg_noalloc(OpndSize size, int imm, int reg, bool isPhysical);
+
+//LR[reg] = VR[vB]
+//or
+//PR[reg] = VR[vB]
+void get_virtual_reg(u2 vB, OpndSize size, int reg, bool isPhysical);
+void get_virtual_reg_noalloc(u2 vB, OpndSize size, int reg, bool isPhysical);
+//VR[v] = LR[reg]
+//or
+//VR[v] = PR[reg]
+void set_virtual_reg(u2 vA, OpndSize size, int reg, bool isPhysical);
+void set_virtual_reg_noalloc(u2 vA, OpndSize size, int reg, bool isPhysical);
+void get_VR_ss(int vB, int reg, bool isPhysical);
+void set_VR_ss(int vA, int reg, bool isPhysical);
+void get_VR_sd(int vB, int reg, bool isPhysical);
+void set_VR_sd(int vA, int reg, bool isPhysical);
+
+int spill_reg(int reg, bool isPhysical);
+int unspill_reg(int reg, bool isPhysical);
+
+void move_reg_to_mem_noalloc(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical,
+                      MemoryAccessType mType, int mIndex);
+LowOpRegMem* move_mem_to_reg_noalloc(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      MemoryAccessType mType, int mIndex,
+                      int reg, bool isPhysical);
+
+//////////////////////////////////////////////////////////////
+int insertLabel(const char* label, bool checkDup);
+int export_pc();
+int simpleNullCheck(int reg, bool isPhysical, int vr);
+int nullCheck(int reg, bool isPhysical, int exceptionNum, int vr);
+int handlePotentialException(
+                             ConditionCode code_excep, ConditionCode code_okay,
+                             int exceptionNum, const char* errName);
+int get_currentpc(int reg, bool isPhysical);
+int get_self_pointer(int reg, bool isPhysical);
+int get_res_strings(int reg, bool isPhysical);
+int get_res_classes(int reg, bool isPhysical);
+int get_res_fields(int reg, bool isPhysical);
+int get_res_methods(int reg, bool isPhysical);
+int get_glue_method_class(int reg, bool isPhysical);
+int get_glue_method(int reg, bool isPhysical);
+int set_glue_method(int reg, bool isPhysical);
+int get_glue_dvmdex(int reg, bool isPhysical);
+int set_glue_dvmdex(int reg, bool isPhysical);
+int get_suspendCount(int reg, bool isPhysical);
+int get_return_value(OpndSize size, int reg, bool isPhysical);
+int set_return_value(OpndSize size, int reg, bool isPhysical);
+int clear_exception();
+int get_exception(int reg, bool isPhysical);
+int set_exception(int reg, bool isPhysical);
+int save_pc_fp_to_glue();
+int savearea_from_fp(int reg, bool isPhysical);
+
+int call_moddi3();
+int call_divdi3();
+int call_fmod();
+int call_fmodf();
+int call_dvmFindCatchBlock();
+int call_dvmThrowVerificationError();
+int call_dvmAllocObject();
+int call_dvmAllocArrayByClass();
+int call_dvmResolveMethod();
+int call_dvmResolveClass();
+int call_dvmInstanceofNonTrivial();
+int call_dvmThrow();
+int call_dvmThrowWithMessage();
+int call_dvmCheckSuspendPending();
+int call_dvmLockObject();
+int call_dvmUnlockObject();
+int call_dvmInitClass();
+int call_dvmAllocPrimitiveArray();
+int call_dvmInterpHandleFillArrayData();
+int call_dvmNcgHandlePackedSwitch();
+int call_dvmNcgHandleSparseSwitch();
+int call_dvmJitHandlePackedSwitch();
+int call_dvmJitHandleSparseSwitch();
+int call_dvmJitToInterpTraceSelectNoChain();
+int call_dvmJitToPatchPredictedChain();
+int call_dvmJitToInterpNormal();
+int call_dvmJitToInterpTraceSelect();
+int call_dvmQuasiAtomicSwap64();
+int call_dvmQuasiAtomicRead64();
+int call_dvmCanPutArrayElement();
+int call_dvmFindInterfaceMethodInCache();
+int call_dvmHandleStackOverflow();
+int call_dvmResolveString();
+int call_dvmResolveInstField();
+int call_dvmResolveStaticField();
+
+//labels and branches
+//shared branch to resolve class: 2 specialized versions
+//OPTION 1: call & ret
+//OPTION 2: store jump back label in a fixed register or memory
+//jump to .class_resolve, then jump back
+//OPTION 3: share translator code
+/* global variables: ncg_rPC */
+int resolve_class(
+                  int startLR/*logical register index*/, bool isPhysical, int tmp/*const pool index*/,
+                  int thirdArg);
+/* EXPORT_PC; movl exceptionPtr, -8(%esp); movl descriptor, -4(%esp); lea; call; lea; jmp */
+int throw_exception_message(int exceptionPtr, int obj_reg, bool isPhysical,
+                            int startLR/*logical register index*/, bool startPhysical);
+/* EXPORT_PC; movl exceptionPtr, -8(%esp); movl imm, -4(%esp); lea; call; lea; jmp */
+int throw_exception(int exceptionPtr, int imm,
+                    int startLR/*logical register index*/, bool startPhysical);
+
+void freeShortMap();
+int insertDataWorklist(s4 relativePC, char* codePtr1);
+#ifdef ENABLE_TRACING
+int insertMapWorklist(s4 BCOffset, s4 NCGOffset, int isStartOfPC);
+#endif
+int performNCGWorklist();
+int performDataWorklist();
+void performLabelWorklist();
+void performMethodLabelWorklist();
+void freeLabelMap();
+void performSharedWorklist();
+void performChainingWorklist();
+void freeNCGWorklist();
+void freeDataWorklist();
+void freeLabelWorklist();
+void freeChainingWorklist();
+
+int common_invokeArgsDone(ArgsDoneType form, bool isJitFull);
+int common_backwardBranch();
+int common_exceptionThrown();
+int common_errNullObject();
+int common_errArrayIndex();
+int common_errArrayStore();
+int common_errNegArraySize();
+int common_errNoSuchMethod();
+int common_errDivideByZero();
+int common_periodicChecks_entry();
+int common_periodicChecks4();
+int common_gotoBail();
+int common_gotoBail_0();
+int common_StringIndexOutOfBounds();
+void goto_invokeArgsDone();
+
+//lower a bytecode
+int lowerByteCode(const Method* method);
+
+int op_nop();
+int op_move();
+int op_move_from16();
+int op_move_16();
+int op_move_wide();
+int op_move_wide_from16();
+int op_move_wide_16();
+int op_move_result();
+int op_move_result_wide();
+int op_move_exception();
+
+int op_return_void();
+int op_return();
+int op_return_wide();
+int op_const_4();
+int op_const_16();
+int op_const();
+int op_const_high16();
+int op_const_wide_16();
+int op_const_wide_32();
+int op_const_wide();
+int op_const_wide_high16();
+int op_const_string();
+int op_const_string_jumbo();
+int op_const_class();
+int op_monitor_enter();
+int op_monitor_exit();
+int op_check_cast();
+int op_instance_of();
+
+int op_array_length();
+int op_new_instance();
+int op_new_array();
+int op_filled_new_array();
+int op_filled_new_array_range();
+int op_fill_array_data();
+int op_throw();
+int op_throw_verification_error();
+int op_goto();
+int op_goto_16();
+int op_goto_32();
+int op_packed_switch();
+int op_sparse_switch();
+int op_if_ge();
+int op_aget();
+int op_aget_wide();
+int op_aget_object();
+int op_aget_boolean();
+int op_aget_byte();
+int op_aget_char();
+int op_aget_short();
+int op_aput();
+int op_aput_wide();
+int op_aput_object();
+int op_aput_boolean();
+int op_aput_byte();
+int op_aput_char();
+int op_aput_short();
+int op_iget();
+int op_iget_wide(bool isVolatile);
+int op_iget_object();
+int op_iget_boolean();
+int op_iget_byte();
+int op_iget_char();
+int op_iget_short();
+int op_iput();
+int op_iput_wide(bool isVolatile);
+int op_iput_object();
+int op_iput_boolean();
+int op_iput_byte();
+int op_iput_char();
+int op_iput_short();
+int op_sget();
+int op_sget_wide(bool isVolatile);
+int op_sget_object();
+int op_sget_boolean();
+int op_sget_byte();
+int op_sget_char();
+int op_sget_short();
+int op_sput(bool isObj);
+int op_sput_wide(bool isVolatile);
+int op_sput_object();
+int op_sput_boolean();
+int op_sput_byte();
+int op_sput_char();
+int op_sput_short();
+int op_invoke_virtual();
+int op_invoke_super();
+int op_invoke_direct();
+int op_invoke_static();
+int op_invoke_interface();
+int op_invoke_virtual_range();
+int op_invoke_super_range();
+int op_invoke_direct_range();
+int op_invoke_static_range();
+int op_invoke_interface_range();
+int op_int_to_long();
+int op_add_long_2addr();
+int op_add_int_lit8();
+int op_cmpl_float();
+int op_cmpg_float();
+int op_cmpl_double();
+int op_cmpg_double();
+int op_cmp_long();
+int op_if_eq();
+int op_if_ne();
+int op_if_lt();
+int op_if_gt();
+int op_if_le();
+int op_if_eqz();
+int op_if_nez();
+int op_if_ltz();
+int op_if_gez();
+int op_if_gtz();
+int op_if_lez();
+int op_neg_int();
+int op_not_int();
+int op_neg_long();
+int op_not_long();
+int op_neg_float();
+int op_neg_double();
+int op_int_to_float();
+int op_int_to_double();
+int op_long_to_int();
+int op_long_to_float();
+int op_long_to_double();
+int op_float_to_int();
+int op_float_to_long();
+int op_float_to_double();
+int op_double_to_int();
+int op_double_to_long();
+int op_double_to_float();
+int op_int_to_byte();
+int op_int_to_char();
+int op_int_to_short();
+int op_add_int();
+int op_sub_int();
+int op_mul_int();
+int op_div_int();
+int op_rem_int();
+int op_and_int();
+int op_or_int();
+int op_xor_int();
+int op_shl_int();
+int op_shr_int();
+int op_ushr_int();
+int op_add_long();
+int op_sub_long();
+int op_mul_long();
+int op_div_long();
+int op_rem_long();
+int op_and_long();
+int op_or_long();
+int op_xor_long();
+int op_shl_long();
+int op_shr_long();
+int op_ushr_long();
+int op_add_float();
+int op_sub_float();
+int op_mul_float();
+int op_div_float();
+int op_rem_float();
+int op_add_double();
+int op_sub_double();
+int op_mul_double();
+int op_div_double();
+int op_rem_double();
+int op_add_int_2addr();
+int op_sub_int_2addr();
+int op_mul_int_2addr();
+int op_div_int_2addr();
+int op_rem_int_2addr();
+int op_and_int_2addr();
+int op_or_int_2addr();
+int op_xor_int_2addr();
+int op_shl_int_2addr();
+int op_shr_int_2addr();
+int op_ushr_int_2addr();
+int op_sub_long_2addr();
+int op_mul_long_2addr();
+int op_div_long_2addr();
+int op_rem_long_2addr();
+int op_and_long_2addr();
+int op_or_long_2addr();
+int op_xor_long_2addr();
+int op_shl_long_2addr();
+int op_shr_long_2addr();
+int op_ushr_long_2addr();
+int op_add_float_2addr();
+int op_sub_float_2addr();
+int op_mul_float_2addr();
+int op_div_float_2addr();
+int op_rem_float_2addr();
+int op_add_double_2addr();
+int op_sub_double_2addr();
+int op_mul_double_2addr();
+int op_div_double_2addr();
+int op_rem_double_2addr();
+int op_add_int_lit16();
+int op_rsub_int();
+int op_mul_int_lit16();
+int op_div_int_lit16();
+int op_rem_int_lit16();
+int op_and_int_lit16();
+int op_or_int_lit16();
+int op_xor_int_lit16();
+int op_rsub_int_lit8();
+int op_mul_int_lit8();
+int op_div_int_lit8();
+int op_rem_int_lit8();
+int op_and_int_lit8();
+int op_or_int_lit8();
+int op_xor_int_lit8();
+int op_shl_int_lit8();
+int op_shr_int_lit8();
+int op_ushr_int_lit8();
+int op_execute_inline(bool isRange);
+int op_invoke_object_init_range();
+int op_iget_quick();
+int op_iget_wide_quick();
+int op_iget_object_quick();
+int op_iput_quick();
+int op_iput_wide_quick();
+int op_iput_object_quick();
+int op_invoke_virtual_quick();
+int op_invoke_virtual_quick_range();
+int op_invoke_super_quick();
+int op_invoke_super_quick_range();
+
+///////////////////////////////////////////////
+void set_reg_opnd(LowOpndReg* op_reg, int reg, bool isPhysical, LowOpndRegType type);
+void set_mem_opnd(LowOpndMem* mem, int disp, int base, bool isPhysical);
+void set_mem_opnd_scale(LowOpndMem* mem, int base, bool isPhysical, int disp, int index, bool indexPhysical, int scale);
+LowOpImm* dump_imm(Mnemonic m, OpndSize size,
+               int imm);
+LowOpNCG* dump_ncg(Mnemonic m, OpndSize size, int imm);
+LowOpImm* dump_imm_with_codeaddr(Mnemonic m, OpndSize size,
+               int imm, char* codePtr);
+LowOpImm* dump_special(AtomOpCode cc, int imm);
+LowOpMem* dump_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical);
+LowOpReg* dump_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type);
+LowOpReg* dump_reg_noalloc(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type);
+LowOpMemImm* dump_imm_mem_noalloc(Mnemonic m, OpndSize size,
+                           int imm,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex);
+LowOpRegReg* dump_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type);
+LowOpRegReg* dump_movez_reg_reg(Mnemonic m, OpndSize size,
+                        int reg, bool isPhysical,
+                        int reg2, bool isPhysical2);
+LowOpRegMem* dump_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex,
+                   int reg, bool isPhysical, LowOpndRegType type);
+LowOpRegMem* dump_mem_reg_noalloc(Mnemonic m, OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex,
+                           int reg, bool isPhysical, LowOpndRegType type);
+LowOpRegMem* dump_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type);
+LowOpMemReg* dump_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type);
+LowOpMemReg* dump_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, LowOpndRegType type);
+LowOpMemReg* dump_reg_mem_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex, LowOpndRegType type);
+LowOpRegImm* dump_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, bool chaining);
+LowOpMemImm* dump_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, bool chaining);
+LowOpMemReg* dump_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex);
+LowOpRegMem* dump_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg);
+LowOpLabel* dump_label(Mnemonic m, OpndSize size, int imm,
+               const char* label, bool isLocal);
+
+unsigned getJmpCallInstSize(OpndSize size, JmpCall_type type);
+bool lowerByteCodeJit(const Method* method, const u2* codePtr, MIR* mir);
+void startOfBasicBlock(struct BasicBlock* bb);
+extern LowOpBlockLabel* traceLabelList;
+extern struct BasicBlock* traceCurrentBB;
+extern struct MIR* traceCurrentMIR;
+void startOfTrace(const Method* method, LowOpBlockLabel* labelList, int, CompilationUnit*);
+void endOfTrace(bool freeOnly);
+LowOp* jumpToBasicBlock(char* instAddr, int targetId);
+LowOp* condJumpToBasicBlock(char* instAddr, ConditionCode cc, int targetId);
+bool jumpToException(const char* target);
+int codeGenBasicBlockJit(const Method* method, BasicBlock* bb);
+void endOfBasicBlock(struct BasicBlock* bb);
+void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir);
+int insertChainingWorklist(int bbId, char * codeStart);
+void startOfTraceO1(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, CompilationUnit *cUnit);
+void endOfTraceO1();
+int isPowerOfTwo(int imm);
+void move_chain_to_mem(OpndSize size, int imm,
+                        int disp, int base_reg, bool isBasePhysical);
+void move_chain_to_reg(OpndSize size, int imm, int reg, bool isPhysical);
+
+void dumpImmToMem(int vrNum, OpndSize size, int value);
+bool isInMemory(int regNum, OpndSize size);
+int touchEbx();
+int boundCheck(int vr_array, int reg_array, bool isPhysical_array,
+               int vr_index, int reg_index, bool isPhysical_index,
+               int exceptionNum);
+int getRelativeOffset(const char* target, bool isShortTerm, JmpCall_type type, bool* unknown,
+                      OpndSize* immSize);
+int getRelativeNCG(s4 tmp, JmpCall_type type, bool* unknown, OpndSize* size);
+void freeAtomMem();
+OpndSize estOpndSizeFromImm(int target);
+
+void preprocessingBB(BasicBlock* bb);
+void preprocessingTrace();
+void dump_nop(int size);
+#endif
diff --git a/vm/compiler/codegen/x86/LowerAlu.cpp b/vm/compiler/codegen/x86/LowerAlu.cpp
new file mode 100644
index 0000000..c8c4d66
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerAlu.cpp
@@ -0,0 +1,1980 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerAlu.cpp
+    \brief This file lowers ALU bytecodes.
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+/////////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode NEG_INT
+
+//!
+int op_neg_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_unary_reg(OpndSize_32, neg_opc, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode NOT_INT
+
+//!
+int op_not_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_unary_reg(OpndSize_32, not_opc, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode NEG_LONG
+
+//! This implementation uses XMM registers
+int op_neg_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, xor_opc, 2, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, sub_opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode NOT_LONG
+
+//! This implementation uses XMM registers
+int op_not_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    load_global_data_API("64bits", OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, andn_opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode NEG_FLOAT
+
+//! This implementation uses GPR
+int op_neg_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, add_opc, 0x80000000, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+
+//! lower bytecode NEG_DOUBLE
+
+//! This implementation uses XMM registers
+int op_neg_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    load_global_data_API("doubNeg", OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, xor_opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    rPC += 1;
+    return 0;
+}
+
+//! lower bytecode INT_TO_LONG
+
+//! It uses native instruction cdq
+int op_int_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, PhysicalReg_EAX, true);
+    convert_integer(OpndSize_32, OpndSize_64);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_int_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_32, vB); //fildl
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_int_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_32, vB); //fildl
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode LONG_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_long_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_64, vB); //fildll
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode LONG_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_long_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_int_fp_stack_VR(OpndSize_64, vB); //fildll
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode FLOAT_TO_DOUBLE
+
+//! This implementation uses FP stack
+int op_float_to_double() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_fp_stack_VR(OpndSize_32, vB); //flds
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode DOUBLE_TO_FLOAT
+
+//! This implementation uses FP stack
+int op_double_to_float() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    load_fp_stack_VR(OpndSize_64, vB); //fldl
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    rPC += 1;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode LONG_TO_INT
+
+//! This implementation uses GPR
+int op_long_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+
+//! common code to convert a float or double to integer
+
+//! It uses FP stack
+int common_fp_to_int(bool isDouble, u2 vA, u2 vB) {
+    if(isDouble) {
+        load_fp_stack_VR(OpndSize_64, vB); //fldl
+    }
+    else {
+        load_fp_stack_VR(OpndSize_32, vB); //flds
+    }
+
+    load_fp_stack_global_data_API("intMax", OpndSize_32);
+    load_fp_stack_global_data_API("intMin", OpndSize_32);
+
+    //ST(0) ST(1) ST(2) --> LintMin LintMax value
+    compare_fp_stack(true, 2, false/*isDouble*/); //ST(2)
+    //ST(0) ST(1) --> LintMax value
+    conditional_jump(Condition_AE, ".float_to_int_negInf", true);
+    rememberState(1);
+    compare_fp_stack(true, 1, false/*isDouble*/); //ST(1)
+    //ST(0) --> value
+    rememberState(2);
+    conditional_jump(Condition_C, ".float_to_int_nanInf", true);
+    //fnstcw, orw, fldcw, xorw
+    load_effective_addr(-2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fpu_cw(false/*checkException*/, 0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, or_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    alu_binary_imm_mem(OpndSize_16, xor_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    store_int_fp_stack_VR(true/*pop*/, OpndSize_32, vA); //fistpl
+    //fldcw
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    load_effective_addr(2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    rememberState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_nanInf", true);
+    conditional_jump(Condition_NP, ".float_to_int_posInf", true);
+    //fstps CHECK
+    goToState(2);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    transferToState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_posInf", true);
+    //fstps CHECK
+    goToState(2);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0x7fffffff);
+    transferToState(3);
+    unconditional_jump(".float_to_int_okay", true);
+    insertLabel(".float_to_int_negInf", true);
+    goToState(1);
+    //fstps CHECK
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    store_fp_stack_VR(true, OpndSize_32, vA);
+    set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+    transferToState(3);
+    insertLabel(".float_to_int_okay", true);
+    return 0;
+}
+//! lower bytecode FLOAT_TO_INT by calling common_fp_to_int
+
+//!
+int op_float_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_int(false, vA, vB);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode DOUBLE_TO_INT by calling common_fp_to_int
+
+//!
+int op_double_to_int() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_int(true, vA, vB);
+    rPC += 1;
+    return retval;
+}
+
+//! common code to convert float or double to long
+
+//! It uses FP stack
+int common_fp_to_long(bool isDouble, u2 vA, u2 vB) {
+    if(isDouble) {
+        load_fp_stack_VR(OpndSize_64, vB); //fldl
+    }
+    else {
+        load_fp_stack_VR(OpndSize_32, vB); //flds
+    }
+
+    //Check if it is the special Negative Infinity value
+    load_fp_stack_global_data_API("valueNegInfLong", OpndSize_64);
+    //Stack status: ST(0) ST(1) --> LlongMin value
+    compare_fp_stack(true, 1, false/*isDouble*/); // Pops ST(1)
+    conditional_jump(Condition_AE, ".float_to_long_negInf", true);
+    rememberState(1);
+
+    //Check if it is the special Positive Infinity value
+    load_fp_stack_global_data_API("valuePosInfLong", OpndSize_64);
+    //Stack status: ST(0) ST(1) --> LlongMax value
+    compare_fp_stack(true, 1, false/*isDouble*/); // Pops ST(1)
+    rememberState(2);
+    conditional_jump(Condition_C, ".float_to_long_nanInf", true);
+
+    //Normal Case
+    //We want to truncate to 0 for conversion. That will be rounding mode 0x11
+    load_effective_addr(-2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fpu_cw(false/*checkException*/, 0, PhysicalReg_ESP, true);
+    //Change control word to rounding mode 11:
+    alu_binary_imm_mem(OpndSize_16, or_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    //Load the control word
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    //Reset the control word
+    alu_binary_imm_mem(OpndSize_16, xor_opc, 0xc00, 0, PhysicalReg_ESP, true);
+    //Perform the actual conversion
+    store_int_fp_stack_VR(true/*pop*/, OpndSize_64, vA); //fistpll
+    // Restore the original control word
+    load_fpu_cw(0, PhysicalReg_ESP, true);
+    load_effective_addr(2, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    rememberState(3);
+    /* NOTE: We do not need to pop out the original value we pushed
+     * since load_fpu_cw above already clears the stack for
+     * normal values.
+     */
+    unconditional_jump(".float_to_long_okay", true);
+
+    //We can be here for positive infinity or NaN. Check parity bit
+    insertLabel(".float_to_long_nanInf", true);
+    conditional_jump(Condition_NP, ".float_to_long_posInf", true);
+    goToState(2);
+    //Save corresponding Long NaN value
+    load_global_data_API("valueNanLong", OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
+    unconditional_jump(".float_to_long_okay", true);
+
+    insertLabel(".float_to_long_posInf", true);
+    goToState(2);
+    //Save corresponding Long Positive Infinity value
+    load_global_data_API("valuePosInfLong", OpndSize_64, 2, false);
+    set_virtual_reg(vA, OpndSize_64, 2, false);
+    transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
+    unconditional_jump(".float_to_long_okay", true);
+
+    insertLabel(".float_to_long_negInf", true);
+    //fstpl
+    goToState(1);
+    //Load corresponding Long Negative Infinity value
+    load_global_data_API("valueNegInfLong", OpndSize_64, 3, false);
+    set_virtual_reg(vA, OpndSize_64, 3, false);
+    transferToState(3);
+    //Pop out the original value we pushed
+    compare_fp_stack(true, 0, false/*isDouble*/); //ST(0)
+
+    insertLabel(".float_to_long_okay", true);
+    return 0;
+}
+//! lower bytecode FLOAT_TO_LONG by calling common_fp_to_long
+
+//!
+int op_float_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_long(false, vA, vB);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode DOUBLE_TO_LONG by calling common_fp_to_long
+
+//!
+int op_double_to_long() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    int retval = common_fp_to_long(true, vA, vB);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode INT_TO_BYTE
+
+//! It uses GPR
+int op_int_to_byte() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 24, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sar_opc, 24, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_CHAR
+
+//! It uses GPR
+int op_int_to_char() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 16, 1, false);
+    alu_binary_imm_reg(OpndSize_32, shr_opc, 16, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode INT_TO_SHORT
+
+//! It uses GPR
+int op_int_to_short() {
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sal_opc, 16, 1, false);
+    alu_binary_imm_reg(OpndSize_32, sar_opc, 16, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! common code to handle integer ALU ops
+
+//! It uses GPR
+int common_alu_int(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) { //except div and rem
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    //in encoder, reg is first operand, which is the destination
+    //gpr_1 op v2(rFP) --> gpr_1
+    //shift only works with reg cl, v2 should be in %ecx
+    alu_binary_VR_reg(OpndSize_32, opc, v2, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+#undef P_GPR_1
+#define P_GPR_1 PhysicalReg_EBX
+//! common code to handle integer shift ops
+
+//! It uses GPR
+int common_shift_int(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, PhysicalReg_ECX, true);
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    //in encoder, reg2 is first operand, which is the destination
+    //gpr_1 op v2(rFP) --> gpr_1
+    //shift only works with reg cl, v2 should be in %ecx
+    alu_binary_reg_reg(OpndSize_32, opc, PhysicalReg_ECX, true, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+#undef p_GPR_1
+//! lower bytecode ADD_INT by calling common_alu_int
+
+//!
+int op_add_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_INT by calling common_alu_int
+
+//!
+int op_sub_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT by calling common_alu_int
+
+//!
+int op_mul_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(imul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT by calling common_alu_int
+
+//!
+int op_and_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(and_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT by calling common_alu_int
+
+//!
+int op_or_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(or_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT by calling common_alu_int
+
+//!
+int op_xor_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_alu_int(xor_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT by calling common_shift_int
+
+//!
+int op_shl_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(shl_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT by calling common_shift_int
+
+//!
+int op_shr_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(sar_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT by calling common_shift_int
+
+//!
+int op_ushr_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_shift_int(shr_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_INT_2ADDR by calling common_alu_int
+
+//!
+int op_add_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_INT_2ADDR by calling common_alu_int
+
+//!
+int op_sub_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_INT_2ADDR by calling common_alu_int
+
+//!
+int op_mul_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(imul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode AND_INT_2ADDR by calling common_alu_int
+
+//!
+int op_and_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(and_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode OR_INT_2ADDR by calling common_alu_int
+
+//!
+int op_or_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(or_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode XOR_INT_2ADDR by calling common_alu_int
+
+//!
+int op_xor_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_alu_int(xor_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHL_INT_2ADDR by calling common_shift_int
+
+//!
+int op_shl_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(shl_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHR_INT_2ADDR by calling common_shift_int
+
+//!
+int op_shr_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(sar_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode USHR_INT_2ADDR by calling common_shift_int
+
+//!
+int op_ushr_int_2addr() {
+    u2 vA, v1, v2;
+    vA = INST_A(inst);
+    v1 = vA;
+    v2 = INST_B(inst);
+    int retval = common_shift_int(shr_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//!common code to handle integer DIV & REM, it used GPR
+
+//!The special case: when op0 == minint && op1 == -1, return 0 for isRem, return 0x80000000 for isDiv
+//!There are two merge points in the control flow for this bytecode
+//!make sure the reg. alloc. state is the same at merge points by calling transferToState
+int common_div_rem_int(bool isRem, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_32, PhysicalReg_EAX, true);
+    get_virtual_reg(v2, OpndSize_32, 2, false);
+    compare_imm_reg(OpndSize_32, 0, 2, false);
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       1, "common_errDivideByZero");
+    /////////////////// handle special cases
+    //conditional move 0 to $edx for rem for the two special cases
+    //conditional move 0x80000000 to $eax for div
+    //handle -1 special case divide error
+    compare_imm_reg(OpndSize_32, -1, 2, false);
+    conditional_jump(Condition_NE, ".common_div_rem_int_normal", true);
+    //handle min int special case divide error
+    rememberState(1);
+    compare_imm_reg(OpndSize_32, 0x80000000, PhysicalReg_EAX, true);
+    transferToState(1);
+    conditional_jump(Condition_E, ".common_div_rem_int_special", true);
+
+    insertLabel(".common_div_rem_int_normal", true); //merge point
+    convert_integer(OpndSize_32, OpndSize_64); //cdq
+    //idiv: dividend in edx:eax; quotient in eax; remainder in edx
+    alu_unary_reg(OpndSize_32, idiv_opc, 2, false);
+    if(isRem)
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EDX, true);
+    else //divide: quotient in %eax
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    rememberState(2);
+    unconditional_jump(".common_div_rem_int_okay", true);
+
+    insertLabel(".common_div_rem_int_special", true);
+    goToState(1);
+    if(isRem)
+        set_VR_to_imm(vA, OpndSize_32, 0);
+    else
+        set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+    transferToState(2);
+    insertLabel(".common_div_rem_int_okay", true); //merge point 2
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode DIV_INT by calling common_div_rem_int
+
+//!
+int op_div_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_int(false, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT by calling common_div_rem_int
+
+//!
+int op_rem_int() {
+    u2 vA, v1, v2;
+    vA = INST_AA(inst);
+    v1 = *((u1*)rPC + 2);
+    v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_int(true, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_INT_2ADDR by calling common_div_rem_int
+
+//!
+int op_div_int_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_int(false, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode REM_INT_2ADDR by calling common_div_rem_int
+
+//!
+int op_rem_int_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_int(true, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+//! common code to handle integer ALU ops with literal
+
+//! It uses GPR
+int common_alu_int_lit(ALU_Opcode opc, u2 vA, u2 vB, s2 imm) { //except div and rem
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_imm_reg(OpndSize_32, opc, imm, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    return 0;
+}
+//! calls common_alu_int_lit
+int common_shift_int_lit(ALU_Opcode opc, u2 vA, u2 vB, s2 imm) {
+    return common_alu_int_lit(opc, vA, vB, imm);
+}
+#undef p_GPR_1
+//! lower bytecode ADD_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_add_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(add_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+
+int alu_rsub_int(ALU_Opcode opc, u2 vA, s2 imm, u2 vB) {
+    move_imm_to_reg(OpndSize_32, imm, 2, false);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    alu_binary_reg_reg(OpndSize_32, opc, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    return 0;
+}
+
+
+//! lower bytecode RSUB_INT by calling common_alu_int_lit
+
+//!
+int op_rsub_int() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = alu_rsub_int(sub_opc, vA, tmp, vB);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_mul_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(imul_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_and_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(and_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_or_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(or_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT_LIT16 by calling common_alu_int_lit
+
+//!
+int op_xor_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_alu_int_lit(xor_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_shl_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(shl_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_shr_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(sar_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT_LIT16 by calling common_shift_int_lit
+
+//!
+int op_ushr_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_shift_int_lit(shr_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_add_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(add_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode RSUB_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_rsub_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = alu_rsub_int(sub_opc, vA, tmp, vB);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_mul_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(imul_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_and_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(and_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_or_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(or_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_INT_LIT8 by calling common_alu_int_lit
+
+//!
+int op_xor_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_alu_int_lit(xor_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_shl_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(shl_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_shr_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(sar_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_INT_LIT8 by calling common_shift_int_lit
+
+//!
+int op_ushr_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_shift_int_lit(shr_opc, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+
+int isPowerOfTwo(int imm) {
+    int i;
+    for(i = 1; i < 17; i++) {
+        if(imm == (1 << i)) return i;
+    }
+    return -1;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+int div_lit_strength_reduction(u2 vA, u2 vB, s2 imm) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //strength reduction for div by 2,4,8,...
+        int power = isPowerOfTwo(imm);
+        if(power < 1) return 0;
+        //tmp2 is not updated, so it can share with vB
+        get_virtual_reg(vB, OpndSize_32, 2, false);
+        //if imm is 2, power will be 1
+        if(power == 1) {
+            /* mov tmp1, tmp2
+               shrl $31, tmp1
+               addl tmp2, tmp1
+               sarl $1, tmp1 */
+            move_reg_to_reg(OpndSize_32, 2, false, 1, false);
+            alu_binary_imm_reg(OpndSize_32, shr_opc, 31, 1, false);
+            alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 1, 1, false);
+            set_virtual_reg(vA, OpndSize_32, 1, false);
+            return 1;
+        }
+        //power > 1
+        /* mov tmp1, tmp2
+           sarl $power-1, tmp1
+           shrl 32-$power, tmp1
+           addl tmp2, tmp1
+           sarl $power, tmp1 */
+        move_reg_to_reg(OpndSize_32, 2, false, 1, false);
+        alu_binary_imm_reg(OpndSize_32, sar_opc, power-1, 1, false);
+        alu_binary_imm_reg(OpndSize_32, shr_opc, 32-power, 1, false);
+        alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+        alu_binary_imm_reg(OpndSize_32, sar_opc, power, 1, false);
+        set_virtual_reg(vA, OpndSize_32, 1, false);
+        return 1;
+    }
+    return 0;
+}
+
+////////// throws exception!!!
+//! common code to handle integer DIV & REM with literal
+
+//! It uses GPR
+int common_div_rem_int_lit(bool isRem, u2 vA, u2 vB, s2 imm) {
+    if(!isRem) {
+        int retCode = div_lit_strength_reduction(vA, vB, imm);
+        if(retCode > 0) return 0;
+    }
+    if(imm == 0) {
+        export_pc(); //use %edx
+#ifdef DEBUG_EXCEPTION
+        LOGI("EXTRA code to handle exception");
+#endif
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        unconditional_jump_global_API(
+                          "common_errDivideByZero", false);
+
+        return 0;
+    }
+    get_virtual_reg(vB, OpndSize_32, PhysicalReg_EAX, true);
+    //check against -1 for DIV_INT??
+    if(imm == -1) {
+        compare_imm_reg(OpndSize_32, 0x80000000, PhysicalReg_EAX, true);
+        conditional_jump(Condition_E, ".div_rem_int_lit_special", true);
+        rememberState(1);
+    }
+    move_imm_to_reg(OpndSize_32, imm, 2, false);
+    convert_integer(OpndSize_32, OpndSize_64); //cdq
+    //idiv: dividend in edx:eax; quotient in eax; remainder in edx
+    alu_unary_reg(OpndSize_32, idiv_opc, 2, false);
+    if(isRem)
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EDX, true);
+    else
+        set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+
+    if(imm == -1) {
+        unconditional_jump(".div_rem_int_lit_okay", true);
+        rememberState(2);
+        insertLabel(".div_rem_int_lit_special", true);
+        goToState(1);
+        if(isRem)
+            set_VR_to_imm(vA, OpndSize_32, 0);
+        else
+            set_VR_to_imm(vA, OpndSize_32, 0x80000000);
+        transferToState(2);
+    }
+
+    insertLabel(".div_rem_int_lit_okay", true); //merge point 2
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode DIV_INT_LIT16 by calling common_div_rem_int_lit
+
+//!
+int op_div_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_div_rem_int_lit(false, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT_LIT16 by calling common_div_rem_int_lit
+
+//!
+int op_rem_int_lit16() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s4 tmp = (s2)FETCH(1);
+    int retval = common_div_rem_int_lit(true, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_INT_LIT8 by calling common_div_rem_int_lit
+
+//!
+int op_div_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_div_rem_int_lit(false, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_INT_LIT8 by calling common_div_rem_int_lit
+
+//!
+int op_rem_int_lit8() {
+    u2 vA = INST_AA(inst);
+    u2 vB = (u2)FETCH(1) & 0xff;
+    s2 tmp = (s2)FETCH(1) >> 8;
+    int retval = common_div_rem_int_lit(true, vA, vB, tmp);
+    rPC += 2;
+    return retval;
+}
+//! common code to hanle long ALU ops
+
+//! It uses XMM
+int common_alu_long(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) { //except div and rem
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    get_virtual_reg(v2, OpndSize_64, 2, false);
+    alu_binary_reg_reg(OpndSize_64, opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_LONG by calling common_alu_long
+
+//!
+int op_add_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_LONG by calling common_alu_long
+
+//!
+int op_sub_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AND_LONG by calling common_alu_long
+
+//!
+int op_and_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(and_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode OR_LONG by calling common_alu_long
+
+//!
+int op_or_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(or_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode XOR_LONG by calling common_alu_long
+
+//!
+int op_xor_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_long(xor_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_add_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_sub_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode AND_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_and_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(and_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode OR_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_or_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(or_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode XOR_LONG_2ADDR by calling common_alu_long
+
+//!
+int op_xor_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_long(xor_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+//signed vs unsigned imul and mul?
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! common code to handle multiplication of long
+
+//! It uses GPR
+int common_mul_long(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, 1, false);
+    move_reg_to_reg(OpndSize_32, 1, false, PhysicalReg_EAX, true);
+    //imul: 2L * 1H update temporary 1
+    alu_binary_VR_reg(OpndSize_32, imul_opc, (v1+1), 1, false);
+    get_virtual_reg(v1, OpndSize_32, 3, false);
+    move_reg_to_reg(OpndSize_32, 3, false, 2, false);
+    //imul: 1L * 2H
+    alu_binary_VR_reg(OpndSize_32, imul_opc, (v2+1), 2, false);
+    alu_binary_reg_reg(OpndSize_32, add_opc, 2, false, 1, false);
+    alu_unary_reg(OpndSize_32, mul_opc, 3, false);
+    alu_binary_reg_reg(OpndSize_32, add_opc, PhysicalReg_EDX, true, 1, false);
+    set_virtual_reg(vA+1, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+//! lower bytecode MUL_LONG by calling common_mul_long
+
+//!
+int op_mul_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_mul_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_LONG_2ADDR by calling common_mul_long
+
+//!
+int op_mul_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_mul_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! common code to handle DIV & REM of long
+
+//! It uses GPR & XMM; and calls call_moddi3 & call_divdi3
+int common_div_rem_long(bool isRem, u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v2, OpndSize_32, 1, false);
+    get_virtual_reg(v2+1, OpndSize_32, 2, false);
+    //save to native stack before changing register P_GPR_1
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 8, PhysicalReg_ESP, true);
+    alu_binary_reg_reg(OpndSize_32, or_opc, 2, false, 1, false);
+
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       1, "common_errDivideByZero");
+    move_reg_to_mem(OpndSize_32, 2, false, 12, PhysicalReg_ESP, true);
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    move_reg_to_mem(OpndSize_64, 1, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 refs
+    if(isRem)
+        call_moddi3();
+    else
+        call_divdi3();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    set_virtual_reg(vA+1, OpndSize_32,PhysicalReg_EDX, true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+//! lower bytecode DIV_LONG by calling common_div_rem_long
+
+//!
+int op_div_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_long(false, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_LONG by calling common_div_rem_long
+
+//!
+int op_rem_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_div_rem_long(true, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_LONG_2ADDR by calling common_div_rem_long
+
+//!
+int op_div_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_long(false, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode REM_LONG_2ADDR by calling common_div_rem_long
+
+//!
+int op_rem_long_2addr() { //call __moddi3 instead of __divdi3
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_div_rem_long(true, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+
+//! common code to handle SHL long
+
+//! It uses XMM
+int common_shl_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_ss(v2, 2, false);
+
+    load_global_data_API("shiftMask", OpndSize_64, 3, false);
+
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, sll_opc, 2, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+
+//! common code to handle SHR long
+
+//! It uses XMM
+int common_shr_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_ss(v2, 2, false);
+
+    load_global_data_API("shiftMask", OpndSize_64, 3, false);
+
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, srl_opc, 2, false, 1, false);
+    compare_imm_VR(OpndSize_32, 0, (v1+1));
+    conditional_jump(Condition_GE, ".common_shr_long_special", true);
+    rememberState(1);
+
+    load_global_data_API("value64", OpndSize_64, 4, false);
+
+    alu_binary_reg_reg(OpndSize_64, sub_opc, 2, false, 4, false);
+
+    load_global_data_API("64bits", OpndSize_64, 5, false);
+
+    alu_binary_reg_reg(OpndSize_64, sll_opc, 4, false, 5, false);
+    alu_binary_reg_reg(OpndSize_64, or_opc, 5, false, 1, false);
+    rememberState(2);
+    //check whether the target is next instruction TODO
+    unconditional_jump(".common_shr_long_done", true);
+
+    insertLabel(".common_shr_long_special", true);
+    goToState(1);
+    transferToState(2);
+    insertLabel(".common_shr_long_done", true);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    return 0;
+}
+
+//! common code to handle USHR long
+
+//! It uses XMM
+int common_ushr_long(u2 vA, u2 v1, u2 v2) {
+    get_VR_sd(v1, 1, false);
+    get_VR_ss(v2, 2, false);
+
+    load_sd_global_data_API("shiftMask", 3, false);
+
+    alu_binary_reg_reg(OpndSize_64, and_opc, 3, false, 2, false);
+    alu_binary_reg_reg(OpndSize_64, srl_opc, 2, false, 1, false);
+    set_VR_sd(vA, 1, false);
+    return 0;
+}
+//! lower bytecode SHL_LONG by calling common_shl_long
+
+//!
+int op_shl_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_shl_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHL_LONG_2ADDR by calling common_shl_long
+
+//!
+int op_shl_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_shl_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SHR_LONG by calling common_shr_long
+
+//!
+int op_shr_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_shr_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SHR_LONG_2ADDR by calling common_shr_long
+
+//!
+int op_shr_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_shr_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode USHR_LONG by calling common_ushr_long
+
+//!
+int op_ushr_long() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_ushr_long(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode USHR_LONG_2ADDR by calling common_ushr_long
+
+//!
+int op_ushr_long_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_ushr_long(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define USE_MEM_OPERAND
+///////////////////////////////////////////
+//! common code to handle ALU of floats
+
+//! It uses XMM
+int common_alu_float(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {//add, sub, mul
+    get_VR_ss(v1, 1, false);
+#ifdef USE_MEM_OPERAND
+    alu_sd_binary_VR_reg(opc, v2, 1, false, false/*isSD*/);
+#else
+    get_VR_ss(v2, 2, false);
+    alu_ss_binary_reg_reg(opc, 2, false, 1, false);
+#endif
+    set_VR_ss(vA, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_FLOAT by calling common_alu_float
+
+//!
+int op_add_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_FLOAT by calling common_alu_float
+
+//!
+int op_sub_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_FLOAT by calling common_alu_float
+
+//!
+int op_mul_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(mul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_add_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_sub_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_FLOAT_2ADDR by calling common_alu_float
+
+//!
+int op_mul_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(mul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of float
+
+//! It uses FP stack
+int common_div_float(u2 vA, u2 v1, u2 v2) {
+    load_fp_stack_VR(OpndSize_32, v1); //flds
+    fpu_VR(div_opc, OpndSize_32, v2);
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    return 0;
+}
+//! lower bytecode DIV_FLOAT by calling common_div_float
+
+//!
+int op_div_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_float(div_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_FLOAT_2ADDR by calling common_div_float
+
+//!
+int op_div_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_float(div_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of double
+
+//! It uses XMM
+int common_alu_double(ALU_Opcode opc, u2 vA, u2 v1, u2 v2) {//add, sub, mul
+    get_VR_sd(v1, 1, false);
+#ifdef USE_MEM_OPERAND
+    alu_sd_binary_VR_reg(opc, v2, 1, false, true /*isSD*/);
+#else
+    get_VR_sd(v2, 2, false);
+    alu_sd_binary_reg_reg(opc, 2, false, 1, false);
+#endif
+    set_VR_sd(vA, 1, false);
+    return 0;
+}
+//! lower bytecode ADD_DOUBLE by calling common_alu_double
+
+//!
+int op_add_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(add_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SUB_DOUBLE by calling common_alu_double
+
+//!
+int op_sub_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(sub_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode MUL_DOUBLE by calling common_alu_double
+
+//!
+int op_mul_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(mul_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode ADD_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_add_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(add_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode SUB_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_sub_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(sub_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode MUL_DOUBLE_2ADDR by calling common_alu_double
+
+//!
+int op_mul_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(mul_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle DIV of double
+
+//! It uses FP stack
+int common_div_double(u2 vA, u2 v1, u2 v2) {
+    load_fp_stack_VR(OpndSize_64, v1); //fldl
+    fpu_VR(div_opc, OpndSize_64, v2); //fdivl
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    return 0;
+}
+//! lower bytecode DIV_DOUBLE by calling common_div_double
+
+//!
+int op_div_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_alu_double(div_opc, vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode DIV_DOUBLE_2ADDR by calling common_div_double
+
+//!
+int op_div_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_alu_double(div_opc, vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! common code to handle REM of float
+
+//! It uses GPR & calls call_fmodf
+int common_rem_float(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    get_virtual_reg(v2, OpndSize_32, 2, false);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 2, false, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_fmodf(); //(float x, float y) return float
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fp_stack_VR(true, OpndSize_32, vA); //fstps
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+//! lower bytecode REM_FLOAT by calling common_rem_float
+
+//!
+int op_rem_float() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_rem_float(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_FLOAT_2ADDR by calling common_rem_float
+
+//!
+int op_rem_float_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_rem_float(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! common code to handle REM of double
+
+//! It uses XMM & calls call_fmod
+int common_rem_double(u2 vA, u2 v1, u2 v2) {
+    get_virtual_reg(v1, OpndSize_64, 1, false);
+    get_virtual_reg(v2, OpndSize_64, 2, false);
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_64, 1, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_64, 2, false, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_fmod(); //(long double x, long double y) return double
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    store_fp_stack_VR(true, OpndSize_64, vA); //fstpl
+    return 0;
+}
+//! lower bytecode REM_DOUBLE by calling common_rem_double
+
+//!
+int op_rem_double() {
+    u2 vA = INST_AA(inst);
+    u2 v1 = *((u1*)rPC + 2);
+    u2 v2 = *((u1*)rPC + 3);
+    int retval = common_rem_double(vA, v1, v2);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode REM_DOUBLE_2ADDR by calling common_rem_double
+
+//!
+int op_rem_double_2addr() {
+    u2 vA = INST_A(inst);
+    u2 v1 = vA;
+    u2 v2 = INST_B(inst);
+    int retval = common_rem_double(vA, v1, v2);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode CMPL_FLOAT
+
+//!
+int op_cmpl_float() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_ss(v1, 1, false); //xmm
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    compare_VR_ss_reg(v2, 1, false);
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32,
+                                 0xffffffff, 4, false);
+    //ORDER of cmov matters !!! (Z,P,A)
+    //finalNaN: unordered 0xffffffff
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             3, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 4, false);
+    set_virtual_reg(vA, OpndSize_32, 4, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPG_FLOAT
+
+//!
+int op_cmpg_float() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_ss(v1, 1, false);
+    compare_VR_ss_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                1, false, 3, false);
+    //finalNaN: unordered
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                2, false, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                2, false, 3, false);
+    set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPL_DOUBLE
+
+//!
+int op_cmpl_double() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_sd(v1, 1, false);
+    compare_VR_sd_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             3, false, 4, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 4, false);
+    set_virtual_reg(vA, OpndSize_32, 4, false);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode CMPG_DOUBLE
+
+//!
+int op_cmpg_double() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_VR_sd(v1, 1, false);
+    compare_VR_sd_reg(v2, 1, false);
+    move_imm_to_reg(OpndSize_32, 0, 1, false);
+    move_imm_to_reg(OpndSize_32, 1, 2, false);
+
+    //default: 0xffffffff??
+    move_imm_to_reg(OpndSize_32,
+                                 0xffffffff, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_Z,
+                                             1, false, 3, false);
+    //finalNaN: unordered
+    conditional_move_reg_to_reg(OpndSize_32, Condition_P,
+                                             2, false, 3, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A,
+                                             2, false, 3, false);
+   set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 2;
+    return 0;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define OPTION_OLD //for simpler cfg
+//! lower bytecode CMP_LONG
+
+//!
+int op_cmp_long() {
+    u2 vA = INST_AA(inst);
+    u4 v1 = FETCH(1) & 0xff;
+    u4 v2 = FETCH(1) >> 8;
+    get_virtual_reg(v1+1, OpndSize_32, 2, false);
+#ifdef OPTION_OLD
+    move_imm_to_reg(OpndSize_32, 0xffffffff, 3, false);
+    move_imm_to_reg(OpndSize_32, 1, 4, false);
+    move_imm_to_reg(OpndSize_32, 0, 5, false);
+#endif
+    compare_VR_reg(OpndSize_32,
+                                v2+1, 2, false);
+#ifndef OPTION_OLD
+    conditional_jump(Condition_L, ".cmp_long_less", true);
+    conditional_jump(Condition_G, ".cmp_long_greater", true);
+#else
+    conditional_jump(Condition_E, ".cmp_long_equal", true);
+    rememberState(1);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_L, //below vs less
+                                             3, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_G, //above vs greater
+                                             4, false, 6, false);
+    set_virtual_reg(vA, OpndSize_32, 6, false);
+    rememberState(2);
+    unconditional_jump(".cmp_long_okay", true);
+    insertLabel(".cmp_long_equal", true);
+    goToState(1);
+#endif
+
+    get_virtual_reg(v1, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32,
+                                v2, 1, false);
+#ifdef OPTION_OLD
+    conditional_move_reg_to_reg(OpndSize_32, Condition_E,
+                                             5, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_B, //below vs less
+                                             3, false, 6, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_A, //above vs greater
+                                             4, false, 6, false);
+    set_virtual_reg(vA, OpndSize_32, 6, false);
+    transferToState(2);
+#else
+    conditional_jump(Condition_A, ".cmp_long_greater", true);
+    conditional_jump(Condition_NE, ".cmp_long_less", true);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    unconditional_jump(".cmp_long_okay", true);
+
+    insertLabel(".cmp_long_less", true);
+    set_VR_to_imm(vA, OpndSize_32, 0xffffffff);
+    unconditional_jump(".cmp_long_okay", true);
+
+    insertLabel(".cmp_long_greater", true);
+    set_VR_to_imm(vA, OpndSize_32, 1);
+#endif
+    insertLabel(".cmp_long_okay", true);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
diff --git a/vm/compiler/codegen/x86/LowerConst.cpp b/vm/compiler/codegen/x86/LowerConst.cpp
new file mode 100644
index 0000000..62e0ed1
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerConst.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerConst.cpp
+    \brief This file lowers the following bytecodes: CONST_XXX
+
+    Functions are called from the lowered native sequence:
+    1> const_string_resolve
+       INPUT: const pool index in %eax
+       OUTPUT: resolved string in %eax
+       The only register that is still live after this function is ebx
+    2> class_resolve
+       INPUT: const pool index in %eax
+       OUTPUT: resolved class in %eax
+       The only register that is still live after this function is ebx
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+
+//! LOWER bytecode CONST_STRING without usage of helper function
+
+//! It calls const_string_resolve (%ebx is live across the call)
+//! Since the register allocator does not handle control flow within the lowered native sequence,
+//!   we define an interface between the lowering module and register allocator:
+//!     rememberState, gotoState, transferToState
+//!   to make sure at the control flow merge point the state of registers is the same
+int const_string_common_nohelper(u4 tmp, u2 vA) {
+    /* for trace-based JIT, the string is already resolved since this code has been executed */
+    void *strPtr = (void*)
+              (currentMethod->clazz->pDvmDex->pResStrings[tmp]);
+    assert(strPtr != NULL);
+    set_VR_to_imm(vA, OpndSize_32, (int) strPtr );
+    return 0;
+}
+//! dispatcher to select either const_string_common_helper or const_string_common_nohelper
+
+//!
+int const_string_common(u4 tmp, u2 vA) {
+    return const_string_common_nohelper(tmp, vA);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+//! lower bytecode CONST_4
+
+//!
+int op_const_4() {
+    u2 vA = INST_A(inst);
+    s4 tmp = (s4) (INST_B(inst) << 28) >> 28;
+    set_VR_to_imm(vA, OpndSize_32, tmp);
+    rPC += 1;
+    return 1;
+}
+//! lower bytecode CONST_16
+
+//!
+int op_const_16() {
+    u2 BBBB = FETCH(1);
+    u2 vA = INST_AA(inst);
+    set_VR_to_imm(vA, OpndSize_32, (s2)BBBB);
+    rPC += 2;
+    return 1;
+}
+//! lower bytecode CONST
+
+//!
+int op_const() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    rPC += 3;
+    return 1;
+}
+//! lower bytecode CONST_HIGH16
+
+//!
+int op_const_high16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp<<16); //??
+    rPC += 2;
+    return 1;
+}
+//! lower bytecode CONST_WIDE_16
+
+//!
+int op_const_wide_16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, (s2)tmp);
+    set_VR_to_imm(vA+1, OpndSize_32, (s2)tmp>>31);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode CONST_WIDE_32
+
+//!
+int op_const_wide_32() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp>>31);
+    rPC += 3;
+    return 2;
+}
+//! lower bytecode CONST_WIDE
+
+//!
+int op_const_wide() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u8)FETCH(2) << 16;
+    set_VR_to_imm(vA, OpndSize_32, (s4)tmp);
+    tmp = (u8)FETCH(3);
+    tmp |= (u8)FETCH(4) << 16;
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp);
+    rPC += 5;
+    return 2;
+}
+//! lower bytecode CONST_WIDE_HIGH16
+
+//!
+int op_const_wide_high16() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    set_VR_to_imm(vA, OpndSize_32, 0);
+    set_VR_to_imm(vA+1, OpndSize_32, (s4)tmp<<16);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode CONST_STRING
+
+//!
+int op_const_string() {
+    u2 vB = FETCH(1);
+    u2 vA = INST_AA(inst);
+    u4 tmp = vB;
+    int retval = const_string_common(tmp, vA);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode CONST_STRING_JUMBO
+
+//!
+int op_const_string_jumbo() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    int retval = const_string_common(tmp, vA);
+    rPC += 3;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode CONST_CLASS
+
+//! It calls class_resolve (%ebx is live across the call)
+//! Since the register allocator does not handle control flow within the lowered native sequence,
+//!   we define an interface between the lowering module and register allocator:
+//!     rememberState, gotoState, transferToState
+//!   to make sure at the control flow merge point the state of registers is the same
+int op_const_class() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    /* for trace-based JIT, the class is already resolved since this code has been executed */
+    void *classPtr = (void*)
+       (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    set_VR_to_imm(vA, OpndSize_32, (int) classPtr );
+    rPC += 2;
+    return 0;
+}
+
+#undef P_GPR_1
+
diff --git a/vm/compiler/codegen/x86/LowerGetPut.cpp b/vm/compiler/codegen/x86/LowerGetPut.cpp
new file mode 100644
index 0000000..be519b1
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerGetPut.cpp
@@ -0,0 +1,943 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerGetPut.cpp
+    \brief This file lowers the following bytecodes: XGET|PUT_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_GPR_4 PhysicalReg_EDX
+//! LOWER bytecode AGET without usage of helper function
+
+//! It has null check and length check
+int aget_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) {
+    ////////////////////////////
+    // Request VR free delays before register allocation for the temporaries
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        //last argument is the exception number for this bytecode
+        nullCheck(1, false, 1, vref); //maybe optimized away, if not, call
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        boundCheck(vref, 1, false,
+                             vindex, 2, false,
+                             2);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    if(flag == AGET) {
+        move_mem_disp_scale_to_reg(OpndSize_32, 1, false, offArrayObject_contents, 2, false, 4, 4, false);
+    }
+    else if(flag == AGET_WIDE) {
+        move_mem_disp_scale_to_reg(OpndSize_64, 1, false, offArrayObject_contents, 2, false, 8, 1, false);
+    }
+    else if(flag == AGET_CHAR) {
+        movez_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false);
+    }
+    else if(flag == AGET_SHORT) {
+        moves_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false);
+    }
+    else if(flag == AGET_BOOLEAN) {
+        movez_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false);
+    }
+    else if(flag == AGET_BYTE) {
+        moves_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false);
+    }
+    if(flag == AGET_WIDE) {
+        set_virtual_reg(vA, OpndSize_64, 1, false);
+    }
+    else {
+        set_virtual_reg(vA, OpndSize_32, 4, false);
+    }
+    //////////////////////////////////
+    return 0;
+}
+//! wrapper to call either aget_common_helper or aget_common_nohelper
+
+//!
+int aget_common(int flag, u2 vA, u2 vref, u2 vindex) {
+    return aget_common_nohelper(flag, vA, vref, vindex);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_GPR_4
+//! lower bytecode AGET by calling aget_common
+
+//!
+int op_aget() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_WIDE by calling aget_common
+
+//!
+int op_aget_wide() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_WIDE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_OBJECT by calling aget_common
+
+//!
+int op_aget_object() {
+    return op_aget();
+}
+//! lower bytecode BOOLEAN by calling aget_common
+
+//!
+int op_aget_boolean() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_BOOLEAN, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_BYTE by calling aget_common
+
+//!
+int op_aget_byte() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_BYTE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_CHAR by calling aget_common
+
+//!
+int op_aget_char() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_CHAR, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode AGET_SHORT by calling aget_common
+
+//!
+int op_aget_short() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aget_common(AGET_SHORT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_GPR_4 PhysicalReg_EDX
+//! LOWER bytecode APUT without usage of helper function
+
+//! It has null check and length check
+int aput_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) {
+    //////////////////////////////////////
+    // Request VR free delays before register allocation for the temporaries.
+    // No need to request delay for vA since it will be transferred to temporary
+    // after the null check and bound check.
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        //last argument is the exception number for this bytecode
+        nullCheck(1, false, 1, vref); //maybe optimized away, if not, call
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        boundCheck(vref, 1, false,
+                             vindex, 2, false,
+                             2);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    if(flag == APUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+    }
+    else {
+        get_virtual_reg(vA, OpndSize_32, 4, false);
+    }
+    if(flag == APUT)
+        move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+    else if(flag == APUT_WIDE)
+        move_reg_to_mem_disp_scale(OpndSize_64, 1, false, 1, false, offArrayObject_contents, 2, false, 8);
+    else if(flag == APUT_CHAR || flag == APUT_SHORT)
+        move_reg_to_mem_disp_scale(OpndSize_16, 4, false, 1, false, offArrayObject_contents, 2, false, 2);
+    else if(flag == APUT_BOOLEAN || flag == APUT_BYTE)
+        move_reg_to_mem_disp_scale(OpndSize_8, 4, false, 1, false, offArrayObject_contents, 2, false, 1);
+    //////////////////////////////////
+    return 0;
+}
+//! wrapper to call either aput_common_helper or aput_common_nohelper
+
+//!
+int aput_common(int flag, u2 vA, u2 vref, u2 vindex) {
+    return aput_common_nohelper(flag, vA, vref, vindex);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_GPR_4
+//! lower bytecode APUT by calling aput_common
+
+//!
+int op_aput() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_WIDE by calling aput_common
+
+//!
+int op_aput_wide() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_WIDE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_BOOLEAN by calling aput_common
+
+//!
+int op_aput_boolean() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_BOOLEAN, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_BYTE by calling aput_common
+
+//!
+int op_aput_byte() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_BYTE, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_CHAR by calling aput_common
+
+//!
+int op_aput_char() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_CHAR, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode APUT_SHORT by calling aput_common
+
+//!
+int op_aput_short() {
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+    int retval = aput_common(APUT_SHORT, vA, vref, vindex);
+    rPC += 2;
+    return retval;
+}
+
+#define P_GPR_1 PhysicalReg_EBX //callee-saved valid after CanPutArray
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI //callee-saved
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define P_SCRATCH_3 PhysicalReg_EDX
+
+void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical);
+
+//! lower bytecode APUT_OBJECT
+
+//! Lower the bytecode using helper function ".aput_obj_helper" if helper switch is on
+int op_aput_object() { //type checking
+    u2 vA = INST_AA(inst);
+    u2 vref = FETCH(1) & 0xff;
+    u2 vindex = FETCH(1) >> 8;
+
+    ///////////////////////////
+    // Request VR free delays before register allocation for the temporaries
+    // No need to request delay for vA since it will be transferred to temporary
+    // after the null check and bound check.
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK))
+        requestVRFreeDelay(vref,VRDELAY_NULLCHECK);
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK);
+        requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK);
+    }
+
+    get_virtual_reg(vref, OpndSize_32, 1, false); //array
+    export_pc(); //use %edx
+
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+        compare_imm_reg(OpndSize_32, 0, 1, false);
+        conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+        cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+    }
+
+    get_virtual_reg(vindex, OpndSize_32, 2, false); //index
+    if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+        compare_mem_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false);
+        conditional_jump_global_API(Condition_NC, "common_errArrayIndex", false);
+        cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK);
+        cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK);
+    } else {
+        updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1
+        updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2
+    }
+
+    get_virtual_reg(vA, OpndSize_32, 4, false);
+    compare_imm_reg(OpndSize_32, 0, 4, false);
+    conditional_jump(Condition_E, ".aput_object_skip_check", true);
+    rememberState(1);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 4, false, 5, false);
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false);
+    move_reg_to_mem(OpndSize_32, 6, false, 4, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_dvmCanPutArrayElement(); //scratch??
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump_global_API(Condition_E, "common_errArrayStore", false);
+
+    //NOTE: "2, false" is live through function call
+    move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+    markCard_notNull(1, 11, false);
+    rememberState(2);
+    ////TODO NCG O1 + code cache
+    unconditional_jump(".aput_object_after_check", true);
+
+    insertLabel(".aput_object_skip_check", true);
+    goToState(1);
+    //NOTE: "2, false" is live through function call
+    move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4);
+
+    transferToState(2);
+    insertLabel(".aput_object_after_check", true);
+    ///////////////////////////////
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+
+//////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_ECX
+#define P_GPR_2 PhysicalReg_EBX //should be callee-saved to avoid overwritten by inst_field_resolve
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+
+/*
+   movl offThread_cardTable(self), scratchReg
+   compare_imm_reg 0, valReg (testl valReg, valReg)
+   je .markCard_skip
+   shrl $GC_CARD_SHIFT, tgtAddrReg
+   movb %, (scratchReg, tgtAddrReg)
+   NOTE: scratchReg can be accessed with the corresponding byte
+         tgtAddrReg will be updated
+   for O1, update the corresponding reference count
+*/
+void markCard(int valReg, int tgtAddrReg, bool targetPhysical, int scratchReg, bool isPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_6, isScratchPhysical);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_6, isScratchPhysical, scratchReg, isPhysical);
+   compare_imm_reg(OpndSize_32, 0, valReg, isPhysical);
+   conditional_jump(Condition_E, ".markCard_skip", true);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, targetPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, targetPhysical, 1);
+   insertLabel(".markCard_skip", true);
+}
+
+void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_2, isScratchPhysical);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isPhysical);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, isPhysical, 1);
+}
+
+void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical) {
+   get_self_pointer(PhysicalReg_SCRATCH_2, false/*isPhysical*/);
+   move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isScratchPhysical);
+   alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isTgtPhysical);
+   move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isScratchPhysical, scratchReg, isScratchPhysical, 0, tgtAddrReg, isTgtPhysical, 1);
+}
+//! LOWER bytecode IGET,IPUT without usage of helper function
+
+//! It has null check and calls assembly function inst_field_resolve
+int iget_iput_common_nohelper(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) {
+#ifdef WITH_JIT_INLINING
+    const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ?
+        traceCurrentMIR->meta.calleeMethod : currentMethod;
+    InstField *pInstField = (InstField *)
+            method->clazz->pDvmDex->pResFields[tmp];
+#else
+    InstField *pInstField = (InstField *)
+            currentMethod->clazz->pDvmDex->pResFields[tmp];
+#endif
+    int fieldOffset;
+
+    assert(pInstField != NULL);
+    fieldOffset = pInstField->byteOffset;
+    move_imm_to_reg(OpndSize_32, fieldOffset, 8, false);
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 7, false);
+    nullCheck(7, false, 2, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+    if(flag == IGET) {
+        move_mem_scale_to_reg(OpndSize_32, 7, false, 8, false, 1, 9, false);
+        set_virtual_reg(vA, OpndSize_32, 9, false);
+#ifdef DEBUG_IGET_OBJ
+        if(isObj > 0) {
+            pushAllRegs();
+            load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            move_reg_to_mem(OpndSize_32, 9, false, 12, PhysicalReg_ESP, true); //field
+            move_reg_to_mem(OpndSize_32, 7, false, 8, PhysicalReg_ESP, true); //object
+            move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); //field
+            move_imm_to_mem(OpndSize_32, 0, 0, PhysicalReg_ESP, true); //iget
+            call_dvmDebugIgetIput();
+            load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            popAllRegs();
+        }
+#endif
+    } else if(flag == IGET_WIDE) {
+        if(isVolatile) {
+            /* call dvmQuasiAtomicRead64(addr) */
+            load_effective_addr(fieldOffset, 7, false, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            nextVersionOfHardReg(PhysicalReg_EAX, 2);
+            nextVersionOfHardReg(PhysicalReg_EDX, 2);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicRead64();
+            load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            //memory content in %edx, %eax
+            set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+            set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+        } else {
+            move_mem_scale_to_reg(OpndSize_64, 7, false, 8, false, 1, 1, false); //access field
+            set_virtual_reg(vA, OpndSize_64, 1, false);
+        }
+    } else if(flag == IPUT) {
+        get_virtual_reg(vA, OpndSize_32, 9, false);
+        move_reg_to_mem_scale(OpndSize_32, 9, false, 7, false, 8, false, 1); //access field
+        if(isObj) {
+            markCard(9, 7, false, 11, false);
+        }
+    } else if(flag == IPUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+        if(isVolatile) {
+            /* call dvmQuasiAtomicSwap64(val, addr) */
+            load_effective_addr(fieldOffset, 7, false, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument
+            move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicSwap64();
+            load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+        }
+        else {
+            move_reg_to_mem_scale(OpndSize_64, 1, false, 7, false, 8, false, 1);
+        }
+    }
+    ///////////////////////////
+    return 0;
+}
+//! wrapper to call either iget_iput_common_helper or iget_iput_common_nohelper
+
+//!
+int iget_iput_common(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) {
+    return iget_iput_common_nohelper(tmp, flag, vA, vB, isObj, isVolatile);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+//! lower bytecode IGET by calling iget_iput_common
+
+//!
+int op_iget() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET, vA, vB, 0, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_WIDE by calling iget_iput_common
+
+//!
+int op_iget_wide(bool isVolatile) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET_WIDE, vA, vB, 0, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_OBJECT by calling iget_iput_common
+
+//!
+int op_iget_object() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IGET, vA, vB, 1, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IGET_BOOLEAN by calling iget_iput_common
+
+//!
+int op_iget_boolean() {
+    return op_iget();
+}
+//! lower bytecode IGET_BYTE by calling iget_iput_common
+
+//!
+int op_iget_byte() {
+    return op_iget();
+}
+//! lower bytecode IGET_CHAR by calling iget_iput_common
+
+//!
+int op_iget_char() {
+    return op_iget();
+}
+//! lower bytecode IGET_SHORT by calling iget_iput_common
+
+//!
+int op_iget_short() {
+    return op_iget();
+}
+//! lower bytecode IPUT by calling iget_iput_common
+
+//!
+int op_iput() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT, vA, vB, 0, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_WIDE by calling iget_iput_common
+
+//!
+int op_iput_wide(bool isVolatile) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT_WIDE, vA, vB, 0, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_OBJECT by calling iget_iput_common
+
+//!
+int op_iput_object() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    u2 tmp = FETCH(1);
+    int retval = iget_iput_common(tmp, IPUT, vA, vB, 1, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode IPUT_BOOLEAN by calling iget_iput_common
+
+//!
+int op_iput_boolean() {
+    return op_iput();
+}
+//! lower bytecode IPUT_BYTE by calling iget_iput_common
+
+//!
+int op_iput_byte() {
+    return op_iput();
+}
+//! lower bytecode IPUT_CHAR by calling iget_iput_common
+
+//!
+int op_iput_char() {
+    return op_iput();
+}
+//! lower bytecode IPUT_SHORT by calling iget_iput_common
+
+//!
+int op_iput_short() {
+    return op_iput();
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX //used by helper only
+
+//! common section to lower IGET & IPUT
+
+//! It will use helper function sget_helper if the switch is on
+int sget_sput_common(int flag, u2 vA, u2 tmp, bool isObj, bool isVolatile) {
+    //call assembly static_field_resolve
+    //no exception
+    //glue: get_res_fields
+    //hard-coded: eax (one version?)
+    //////////////////////////////////////////
+#ifdef WITH_JIT_INLINING
+    const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ? traceCurrentMIR->meta.calleeMethod : currentMethod;
+    void *fieldPtr = (void*)
+        (method->clazz->pDvmDex->pResFields[tmp]);
+#else
+    void *fieldPtr = (void*)
+        (currentMethod->clazz->pDvmDex->pResFields[tmp]);
+#endif
+
+    /* Usually, fieldPtr should not be null. The interpreter should resolve
+     * it before we come here, or not allow this opcode in a trace. However,
+     * we can be in a loop trace and this opcode might have been picked up
+     * by exhaustTrace. Sending a -1 here will terminate the loop formation
+     * and fall back to normal trace, which will not have this opcode.
+     */
+    if (!fieldPtr) {
+        return -1;
+    }
+
+    move_imm_to_reg(OpndSize_32, (int)fieldPtr, PhysicalReg_EAX, true);
+    if(flag == SGET) {
+        move_mem_to_reg(OpndSize_32, offStaticField_value, PhysicalReg_EAX, true, 7, false); //access field
+        set_virtual_reg(vA, OpndSize_32, 7, false);
+    } else if(flag == SGET_WIDE) {
+        if(isVolatile) {
+            /* call dvmQuasiAtomicRead64(addr) */
+            load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            nextVersionOfHardReg(PhysicalReg_EAX, 2);
+            nextVersionOfHardReg(PhysicalReg_EDX, 2);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicRead64();
+            load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            //memory content in %edx, %eax
+            set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+            set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true);
+        }
+        else {
+            move_mem_to_reg(OpndSize_64, offStaticField_value, PhysicalReg_EAX, true, 1, false); //access field
+            set_virtual_reg(vA, OpndSize_64, 1, false);
+        }
+    } else if(flag == SPUT) {
+        get_virtual_reg(vA, OpndSize_32, 7, false);
+        move_reg_to_mem(OpndSize_32, 7, false, offStaticField_value, PhysicalReg_EAX, true); //access field
+        if(isObj) {
+            /* get clazz object, then use clazz object to mark card */
+            move_mem_to_reg(OpndSize_32, offField_clazz, PhysicalReg_EAX, true, 12, false);
+            markCard(7/*valReg*/, 12, false, 11, false);
+        }
+    } else if(flag == SPUT_WIDE) {
+        get_virtual_reg(vA, OpndSize_64, 1, false);
+        if(isVolatile) {
+            /* call dvmQuasiAtomicSwap64(val, addr) */
+            load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false);
+            move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument
+            move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument
+            load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_3;
+            call_dvmQuasiAtomicSwap64();
+            load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+        }
+        else {
+            move_reg_to_mem(OpndSize_64, 1, false, offStaticField_value, PhysicalReg_EAX, true); //access field
+        }
+    }
+    //////////////////////////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+//! lower bytecode SGET by calling sget_sput_common
+
+//!
+int op_sget() {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SGET, vA, tmp, false, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SGET_WIDE by calling sget_sput_common
+
+//!
+int op_sget_wide(bool isVolatile) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SGET_WIDE, vA, tmp, false, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SGET_OBJECT by calling sget_sput_common
+
+//!
+int op_sget_object() {
+    return op_sget();
+}
+//! lower bytecode SGET_BOOLEAN by calling sget_sput_common
+
+//!
+int op_sget_boolean() {
+    return op_sget();
+}
+//! lower bytecode SGET_BYTE by calling sget_sput_common
+
+//!
+int op_sget_byte() {
+    return op_sget();
+}
+//! lower bytecode SGET_CHAR by calling sget_sput_common
+
+//!
+int op_sget_char() {
+    return op_sget();
+}
+//! lower bytecode SGET_SHORT by calling sget_sput_common
+
+//!
+int op_sget_short() {
+    return op_sget();
+}
+//! lower bytecode SPUT by calling sget_sput_common
+
+//!
+int op_sput(bool isObj) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SPUT, vA, tmp, isObj, false);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SPUT_WIDE by calling sget_sput_common
+
+//!
+int op_sput_wide(bool isVolatile) {
+    u2 vA = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    int retval = sget_sput_common(SPUT_WIDE, vA, tmp, false, isVolatile);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode SPUT_OBJECT by calling sget_sput_common
+
+//!
+int op_sput_object() {
+    return op_sput(true);
+}
+//! lower bytecode SPUT_OBJECT by calling sget_sput_common
+
+//!
+int op_sput_boolean() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_BOOLEAN by calling sget_sput_common
+
+//!
+int op_sput_byte() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_BYTE by calling sget_sput_common
+
+//!
+int op_sput_char() {
+    return op_sput(false);
+}
+//! lower bytecode SPUT_SHORT by calling sget_sput_common
+
+//!
+int op_sput_short() {
+    return op_sput(false);
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode IGET_QUICK
+
+//!
+int op_iget_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_32, tmp, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IGET_WIDE_QUICK
+
+//!
+int op_iget_wide_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_64, tmp, 1, false, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IGET_OBJECT_QUICK
+
+//!
+int op_iget_object_quick() {
+    return op_iget_quick();
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode IPUT_QUICK
+
+//!
+int iput_quick_common(bool isObj) {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1);
+
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    get_virtual_reg(vA, OpndSize_32, 2, false);
+    move_reg_to_mem(OpndSize_32, 2, false, tmp, 1, false);
+    if(isObj) {
+        markCard(2/*valReg*/, 1, false, 11, false);
+    }
+    rPC += 2;
+    return 0;
+}
+int op_iput_quick() {
+    return iput_quick_common(false);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IPUT_WIDE_QUICK
+
+//!
+int op_iput_wide_quick() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst); //object
+    u2 tmp = FETCH(1); //byte offset
+
+    // Request VR delay before transfer to temporary. Only vB needs delay.
+    // vA will have non-zero reference count since transfer to temporary for
+    // it happens after null check, thus no delay is needed.
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away, if not, call
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    get_virtual_reg(vA, OpndSize_64, 1, false);
+    move_reg_to_mem(OpndSize_64, 1, false, tmp, 1, false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IPUT_OBJECT_QUICK
+
+//!
+int op_iput_object_quick() {
+    return iput_quick_common(true);
+}
+
diff --git a/vm/compiler/codegen/x86/LowerHelper.cpp b/vm/compiler/codegen/x86/LowerHelper.cpp
new file mode 100644
index 0000000..3fec038
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerHelper.cpp
@@ -0,0 +1,2993 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerHelper.cpp
+    \brief This file implements helper functions for lowering
+
+With NCG O0: all registers are hard-coded ;
+With NCG O1: the lowering module will use variables that will be allocated to a physical register by the register allocator.
+
+register types: FS 32-bit or 64-bit;
+                XMM: SS(32-bit) SD (64-bit);
+                GPR: 8-bit, 16-bit, 32-bit;
+LowOpndRegType tells whether it is gpr, xmm or fs;
+OpndSize can be OpndSize_8, OpndSize_16, OpndSize_32, OpndSize_64
+
+A single native instruction can use multiple physical registers.
+  we can't call freeReg in the middle of emitting a native instruction,
+  since it may free the physical register used by an operand and cause two operands being allocated to the same physical register.
+
+When allocating a physical register for an operand, we can't spill the operands that are already allocated. To avoid that, we call startNativeCode before each native instruction, here flag "canSpill" is set to true for each physical register;
+  when a physical register is allocated, we set its flag "canSpill" to false;
+  at end of each native instruction, call endNativeCode to set flag "canSpill" to true.
+*/
+
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+#include "vm/mterp/Mterp.h"
+#include "vm/mterp/common/FindInterface.h"
+#include "NcgHelper.h"
+#include <math.h>
+#include "interp/InterpState.h"
+
+extern "C" int64_t __divdi3(int64_t, int64_t);
+extern "C" int64_t __moddi3(int64_t, int64_t);
+bool isScratchPhysical;
+LowOp* lirTable[200];
+int num_lirs_in_table = 0;
+
+//4 tables are defined: GPR integer ALU ops, ALU ops in FPU, SSE 32-bit, SSE 64-bit
+//the index to the table is the opcode
+//add_opc,    or_opc,     adc_opc,    sbb_opc,
+//and_opc,    sub_opc,    xor_opc,    cmp_opc,
+//mul_opc,    imul_opc,   div_opc,    idiv_opc,
+//sll_opc,    srl_opc,    sra, (SSE)
+//shl_opc,    shr_opc,    sal_opc,    sar_opc, //integer shift
+//neg_opc,    not_opc,    andn_opc, (SSE)
+//n_alu
+//!mnemonic for integer ALU operations
+const  Mnemonic map_of_alu_opcode_2_mnemonic[] = {
+    Mnemonic_ADD,  Mnemonic_OR,   Mnemonic_ADC,  Mnemonic_SBB,
+    Mnemonic_AND,  Mnemonic_SUB,  Mnemonic_XOR,  Mnemonic_CMP,
+    Mnemonic_MUL,  Mnemonic_IMUL, Mnemonic_DIV,  Mnemonic_IDIV,
+    Mnemonic_Null, Mnemonic_Null, Mnemonic_Null,
+    Mnemonic_SHL,  Mnemonic_SHR,  Mnemonic_SAL,  Mnemonic_SAR,
+    Mnemonic_NEG,  Mnemonic_NOT,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for ALU operations in FPU
+const  Mnemonic map_of_fpu_opcode_2_mnemonic[] = {
+    Mnemonic_FADD,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_FSUB,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_FMUL,  Mnemonic_Null,  Mnemonic_FDIV,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for SSE 32-bit
+const  Mnemonic map_of_sse_opcode_2_mnemonic[] = {
+    Mnemonic_ADDSD,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_SUBSD, Mnemonic_XORPD, Mnemonic_Null,
+    Mnemonic_MULSD,  Mnemonic_Null,  Mnemonic_DIVSD,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,   Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null
+};
+//!mnemonic for SSE 64-bit integer
+const  Mnemonic map_of_64_opcode_2_mnemonic[] = {
+    Mnemonic_PADDQ, Mnemonic_POR,   Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_PAND,  Mnemonic_PSUBQ, Mnemonic_PXOR,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_PSLLQ, Mnemonic_PSRLQ, Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,  Mnemonic_Null,
+    Mnemonic_Null,  Mnemonic_Null,  Mnemonic_PANDN,
+    Mnemonic_Null
+};
+
+////////////////////////////////////////////////
+//!update fields of LowOpndReg
+
+//!
+void set_reg_opnd(LowOpndReg* op_reg, int reg, bool isPhysical, LowOpndRegType type) {
+    op_reg->regType = type;
+    if(isPhysical) {
+        op_reg->logicalReg = -1;
+        op_reg->physicalReg = reg;
+    }
+    else
+        op_reg->logicalReg = reg;
+    return;
+}
+//!update fields of LowOpndMem
+
+//!
+void set_mem_opnd(LowOpndMem* mem, int disp, int base, bool isPhysical) {
+    mem->m_disp.value = disp;
+    mem->hasScale = false;
+    mem->m_base.regType = LowOpndRegType_gp;
+    if(isPhysical) {
+        mem->m_base.logicalReg = -1;
+        mem->m_base.physicalReg = base;
+    } else {
+        mem->m_base.logicalReg = base;
+    }
+    return;
+}
+//!update fields of LowOpndMem
+
+//!
+void set_mem_opnd_scale(LowOpndMem* mem, int base, bool isPhysical, int disp, int index, bool indexPhysical, int scale) {
+    mem->hasScale = true;
+    mem->m_base.regType = LowOpndRegType_gp;
+    if(isPhysical) {
+        mem->m_base.logicalReg = -1;
+        mem->m_base.physicalReg = base;
+    } else {
+        mem->m_base.logicalReg = base;
+    }
+    if(indexPhysical) {
+        mem->m_index.logicalReg = -1;
+        mem->m_index.physicalReg = index;
+    } else {
+        mem->m_index.logicalReg = index;
+    }
+    mem->m_disp.value = disp;
+    mem->m_scale.value = scale;
+    return;
+}
+//!return either LowOpndRegType_xmm or LowOpndRegType_gp
+
+//!
+inline LowOpndRegType getTypeFromIntSize(OpndSize size) {
+    return size == OpndSize_64 ? LowOpndRegType_xmm : LowOpndRegType_gp;
+}
+
+// copied from JIT compiler
+typedef struct AtomMemBlock {
+    size_t bytesAllocated;
+    struct AtomMemBlock *next;
+    char ptr[0];
+} AtomMemBlock;
+
+#define ATOMBLOCK_DEFAULT_SIZE 4096
+AtomMemBlock *atomMemHead = NULL;
+AtomMemBlock *currentAtomMem = NULL;
+void * atomNew(size_t size) {
+    lowOpTimeStamp++; //one LowOp constructed
+    if(atomMemHead == NULL) {
+        atomMemHead = (AtomMemBlock*)malloc(sizeof(AtomMemBlock) + ATOMBLOCK_DEFAULT_SIZE);
+        if(atomMemHead == NULL) {
+            ALOGE("Memory allocation failed");
+            return NULL;
+        }
+        currentAtomMem = atomMemHead;
+        currentAtomMem->bytesAllocated = 0;
+        currentAtomMem->next = NULL;
+    }
+    size = (size + 3) & ~3;
+    if (size > ATOMBLOCK_DEFAULT_SIZE) {
+        ALOGE("Requesting %d bytes which exceed the maximal size allowed", size);
+        return NULL;
+    }
+retry:
+    if (size + currentAtomMem->bytesAllocated <= ATOMBLOCK_DEFAULT_SIZE) {
+        void *ptr;
+        ptr = &currentAtomMem->ptr[currentAtomMem->bytesAllocated];
+        return ptr;
+    }
+    if (currentAtomMem->next) {
+        currentAtomMem = currentAtomMem->next;
+        goto retry;
+    }
+    /* Time to allocate a new arena */
+    AtomMemBlock *newAtomMem = (AtomMemBlock*)malloc(sizeof(AtomMemBlock) + ATOMBLOCK_DEFAULT_SIZE);
+    if(newAtomMem == NULL) {
+        ALOGE("Memory allocation failed");
+        return NULL;
+    }
+    newAtomMem->bytesAllocated = 0;
+    newAtomMem->next = NULL;
+    currentAtomMem->next = newAtomMem;
+    currentAtomMem = newAtomMem;
+    goto retry;
+    ALOGE("atomNew requesting %d bytes", size);
+    return NULL;
+}
+
+void freeAtomMem() {
+    //LOGI("free all atom memory");
+    AtomMemBlock * tmpMem = atomMemHead;
+    while(tmpMem != NULL) {
+        tmpMem->bytesAllocated = 0;
+        tmpMem = tmpMem->next;
+    }
+    currentAtomMem = atomMemHead;
+}
+
+LowOpImm* dump_special(AtomOpCode cc, int imm) {
+    LowOpImm* op = (LowOpImm*)atomNew(sizeof(LowOpImm));
+    op->lop.opCode = Mnemonic_NULL;
+    op->lop.opCode2 = cc;
+    op->lop.opnd1.type = LowOpndType_Imm;
+    op->lop.numOperands = 1;
+    op->immOpnd.value = imm;
+    //stream = encoder_imm(m, size, imm, stream);
+    return op;
+}
+
+LowOpLabel* lower_label(Mnemonic m, OpndSize size, int imm, const char* label, bool isLocal) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+LowOpLabel* dump_label(Mnemonic m, OpndSize size, int imm,
+               const char* label, bool isLocal) {
+    return lower_label(m, size, imm, label, isLocal);
+}
+
+LowOpNCG* dump_ncg(Mnemonic m, OpndSize size, int imm) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction with a single immediate operand
+
+//!
+LowOpImm* lower_imm(Mnemonic m, OpndSize size, int imm, bool updateTable) {
+    stream = encoder_imm(m, size, imm, stream);
+    return NULL;
+}
+
+LowOpImm* dump_imm(Mnemonic m, OpndSize size, int imm) {
+    return lower_imm(m, size, imm, true);
+}
+
+LowOpImm* dump_imm_with_codeaddr(Mnemonic m, OpndSize size,
+               int imm, char* codePtr) {
+    encoder_imm(m, size, imm, codePtr);
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes a single memory operand
+
+//!With NCG O1, we call freeReg to free up physical registers, then call registerAlloc to allocate a physical register for memory base
+LowOpMem* lower_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg) {
+    stream = encoder_mem(m, size, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMem* dump_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        //type of the base is gpr
+        int regAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_mem(m, m2, size, disp, regAll);
+    } else {
+        stream = encoder_mem(m, size, disp, base_reg, isBasePhysical, stream);
+        return NULL;
+    }
+}
+//!update fields of LowOp and generate a x86 instruction that takes a single reg operand
+
+//!With NCG O1, wecall freeReg to free up physical registers, then call registerAlloc to allocate a physical register for the single operand
+LowOpReg* lower_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, LowOpndRegType type) {
+    stream = encoder_reg(m, size, reg, true, type, stream);
+    return NULL;
+}
+
+LowOpReg* dump_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        if(m == Mnemonic_MUL || m == Mnemonic_IDIV) {
+            //these two instructions use eax & edx implicitly
+            touchEax();
+            touchEdx();
+        }
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_reg(m, m2, size, regAll, type);
+    } else {
+        stream = encoder_reg(m, size, reg, isPhysical, type, stream);
+        return NULL;
+    }
+}
+LowOpReg* dump_reg_noalloc(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_reg(m, ATOM_NORMAL, size, reg, type);
+}
+
+LowOpRegReg* lower_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int reg, int reg2, LowOpndRegType type) {
+    if(m == Mnemonic_FUCOMP || m == Mnemonic_FUCOM) {
+        stream = encoder_compare_fp_stack(m == Mnemonic_FUCOMP,
+                                          reg-reg2, size==OpndSize_64, stream);
+    }
+    else {
+        stream = encoder_reg_reg(m, size, reg, true, reg2, true, type, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//Here, both registers are physical
+LowOpRegReg* dump_reg_reg_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int reg2, bool isPhysical2, LowOpndRegType type) {
+    return lower_reg_reg(m, ATOM_NORMAL, size, reg, reg2, type);
+}
+
+inline bool isMnemonicMove(Mnemonic m) {
+    return (m == Mnemonic_MOV || m == Mnemonic_MOVQ ||
+            m == Mnemonic_MOVSS || m == Mnemonic_MOVSD);
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!here dst reg is already allocated to a physical reg
+//! we should not spill the physical register for dst when allocating for src
+LowOpRegReg* dump_reg_reg_noalloc_dst(Mnemonic m, OpndSize size,
+                               int reg, bool isPhysical,
+                               int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        /* remove move from one register to the same register */
+        if(isMnemonicMove(m) && regAll == reg2) return NULL;
+        return lower_reg_reg(m, ATOM_NORMAL, size, regAll, reg2, type);
+    } else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+        return NULL;
+    }
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!here src reg is already allocated to a physical reg
+LowOpRegReg* dump_reg_reg_noalloc_src(Mnemonic m, AtomOpCode m2, OpndSize size,
+                               int reg, bool isPhysical,
+                               int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll2;
+        if(isMnemonicMove(m) && checkTempReg2(reg2, type, isPhysical2, reg)) { //dst reg is logical
+            //only from get_virtual_reg_all
+            regAll2 = registerAllocMove(reg2, type, isPhysical2, reg);
+        } else {
+            regAll2 = registerAlloc(type, reg2, isPhysical2, true);
+            return lower_reg_reg(m, m2, size, reg, regAll2, type);
+        }
+    } else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+        return NULL;
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes two reg operands
+
+//!
+LowOpRegReg* dump_reg_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        //reg is source if m is MOV
+        freeReg(true);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        int regAll2;
+        LowOpRegReg* op = NULL;
+#ifdef MOVE_OPT2
+        if(isMnemonicMove(m) &&
+           ((reg != PhysicalReg_EDI && reg != PhysicalReg_ESP && reg != PhysicalReg_EBP) || (!isPhysical)) &&
+           isPhysical2 == false) { //dst reg is logical
+            //called from move_reg_to_reg
+            regAll2 = registerAllocMove(reg2, type, isPhysical2, regAll);
+        } else {
+#endif
+            donotSpillReg(regAll);
+            regAll2 = registerAlloc(type, reg2, isPhysical2, true);
+            op = lower_reg_reg(m, m2, size, regAll, regAll2, type);
+#ifdef MOVE_OPT2
+        }
+#endif
+        endNativeCode();
+        return op;
+    }
+    else {
+        stream = encoder_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2, type, stream);
+    }
+    return NULL;
+}
+
+LowOpRegMem* lower_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int disp, int base_reg,
+                 MemoryAccessType mType, int mIndex,
+                 int reg, LowOpndRegType type, bool isMoves) {
+    if(m == Mnemonic_MOVSX) {
+        stream = encoder_moves_mem_to_reg(size, disp, base_reg, true,
+                                          reg, true, stream);
+    }
+    else if(m == Mnemonic_MOVZX) {
+        stream = encoder_movez_mem_to_reg(size, disp, base_reg, true,
+                                          reg, true, stream);
+    }
+    else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, true,
+                                 reg, true, type, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here, operands are already allocated to physical registers
+LowOpRegMem* dump_mem_reg_noalloc(Mnemonic m, OpndSize size,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex,
+                           int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, mType, mIndex, reg, type, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here, memory operand is already allocated to physical register
+LowOpRegMem* dump_mem_reg_noalloc_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                               int disp, int base_reg, bool isBasePhysical,
+                               MemoryAccessType mType, int mIndex,
+                               int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_mem_reg(m, m2, size, disp, base_reg, mType, mIndex, regAll, type, false);
+    } else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, isBasePhysical,
+                                 reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_mem_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex,
+                   int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        //it is okay to use the same physical register
+        if(isMnemonicMove(m)) {
+            freeReg(true);
+        } else {
+            donotSpillReg(baseAll);
+        }
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, m2, size, disp, baseAll, mType, mIndex, regAll, type, false);
+    } else {
+        stream = encoder_mem_reg(m, size, disp, base_reg, isBasePhysical,
+                                 reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_moves_mem_reg(Mnemonic m, OpndSize size,
+                         int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, ATOM_NORMAL, size, disp, baseAll, MemoryAccess_Unknown, -1,
+            regAll, LowOpndRegType_gp, true/*moves*/);
+    } else {
+        stream = encoder_moves_mem_to_reg(size, disp, base_reg, isBasePhysical, reg, isPhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* dump_movez_mem_reg(Mnemonic m, OpndSize size,
+             int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_reg(m, ATOM_NORMAL, size, disp, baseAll, MemoryAccess_Unknown, -1,
+            regAll, LowOpndRegType_gp, true/*moves*/);
+    } else {
+        stream = encoder_movez_mem_to_reg(size, disp, base_reg, isBasePhysical, reg, isPhysical, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one reg operand
+
+//!
+LowOpRegReg* dump_movez_reg_reg(Mnemonic m, OpndSize size,
+             int reg, bool isPhysical,
+             int reg2, bool isPhysical2) {
+    LowOpRegReg* op = (LowOpRegReg*)atomNew(sizeof(LowOpRegReg));
+    op->lop.opCode = m;
+    op->lop.opnd1.size = OpndSize_32;
+    op->lop.opnd1.type = LowOpndType_Reg;
+    op->lop.opnd2.size = size;
+    op->lop.opnd2.type = LowOpndType_Reg;
+    set_reg_opnd(&(op->regOpnd1), reg2, isPhysical2, LowOpndRegType_gp);
+    set_reg_opnd(&(op->regOpnd2), reg, isPhysical, LowOpndRegType_gp);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        //reg is source if m is MOV
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, reg, isPhysical, true);
+        donotSpillReg(regAll);
+        int regAll2 = registerAlloc(LowOpndRegType_gp, reg2, isPhysical2, true);
+        stream = encoder_movez_reg_to_reg(size, regAll, true, regAll2, true,
+                                          LowOpndRegType_gp, stream);
+        endNativeCode();
+    }
+    else {
+        stream = encoder_movez_reg_to_reg(size, reg, isPhysical, reg2,
+                                        isPhysical2, LowOpndRegType_gp, stream);
+    }
+    return NULL;
+}
+
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpRegMem* lower_mem_scale_reg(Mnemonic m, OpndSize size, int base_reg, int disp, int index_reg,
+                 int scale, int reg, LowOpndRegType type) {
+    bool isMovzs = (m == Mnemonic_MOVZX || m == Mnemonic_MOVSX);
+    if(isMovzs)
+        stream = encoder_movzs_mem_disp_scale_reg(m, size, base_reg, true, disp, index_reg, true,
+                                                  scale, reg, true, type, stream);
+    else {
+        if(disp == 0)
+            stream = encoder_mem_scale_reg(m, size, base_reg, true, index_reg, true,
+                                           scale, reg, true, type, stream);
+        else
+            stream = encoder_mem_disp_scale_reg(m, size, base_reg, true, disp, index_reg, true,
+                                                scale, reg, true, type, stream);
+    }
+    return NULL;
+}
+
+LowOpRegMem* dump_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll); //make sure index will not use the same physical reg
+        int indexAll = registerAlloc(LowOpndRegType_gp, index_reg, isIndexPhysical, true);
+        if(isMnemonicMove(m)) {
+            freeReg(true);
+            doSpillReg(baseAll); //base can be used now
+        } else {
+            donotSpillReg(indexAll);
+        }
+        bool isMovzs = (m == Mnemonic_MOVZX || m == Mnemonic_MOVSX);
+        int regAll = registerAlloc(isMovzs ? LowOpndRegType_gp : type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_mem_scale_reg(m, size, baseAll, disp, indexAll, scale, regAll, type);
+    } else {
+        stream = encoder_mem_scale_reg(m, size, base_reg, isBasePhysical, index_reg,
+                                       isIndexPhysical, scale, reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpMemReg* lower_reg_mem_scale(Mnemonic m, OpndSize size, int reg,
+                 int base_reg, int disp, int index_reg, int scale, LowOpndRegType type) {
+    if(disp == 0)
+        stream = encoder_reg_mem_scale(m, size, reg, true, base_reg, true,
+                                       index_reg, true, scale, type, stream);
+    else
+        stream = encoder_reg_mem_disp_scale(m, size, reg, true, base_reg, true,
+                                            disp, index_reg, true, scale, type, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int indexAll = registerAlloc(LowOpndRegType_gp, index_reg, isIndexPhysical, true);
+        donotSpillReg(indexAll);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_reg_mem_scale(m, size, regAll, baseAll, disp, indexAll, scale, type);
+    } else {
+        stream = encoder_reg_mem_scale(m, size, reg, isPhysical, base_reg, isBasePhysical,
+                                       index_reg, isIndexPhysical, scale, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!Here operands are already allocated
+LowOpMemReg* lower_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size, int reg,
+                 int disp, int base_reg, MemoryAccessType mType, int mIndex,
+                 LowOpndRegType type) {
+    stream = encoder_reg_mem(m, size, reg, true, disp, base_reg, true, type, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_reg_mem_noalloc(Mnemonic m, OpndSize size,
+                           int reg, bool isPhysical,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex, LowOpndRegType type) {
+    return lower_reg_mem(m, ATOM_NORMAL, size, reg, disp, base_reg, mType, mIndex, type);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one reg operand and one mem operand
+
+//!
+LowOpMemReg* dump_reg_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, LowOpndRegType type) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        donotSpillReg(baseAll);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        endNativeCode();
+        return lower_reg_mem(m, m2, size, regAll, disp, baseAll, mType, mIndex, type);
+    } else {
+        stream = encoder_reg_mem(m, size, reg, isPhysical, disp, base_reg, isBasePhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one reg operand
+
+//!The reg operand is allocated already
+LowOpRegImm* lower_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                 int imm, int reg, LowOpndRegType type, bool chaining) {
+    stream = encoder_imm_reg(m, size, imm, reg, true, type, stream);
+    return NULL;
+}
+
+LowOpRegImm* dump_imm_reg_noalloc(Mnemonic m, OpndSize size,
+                           int imm, int reg, bool isPhysical, LowOpndRegType type) {
+    return lower_imm_reg(m, ATOM_NORMAL, size, imm, reg, type, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one reg operand
+
+//!
+LowOpRegImm* dump_imm_reg(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, bool chaining) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int regAll = registerAlloc(type, reg, isPhysical, true);
+        return lower_imm_reg(m, m2, size, imm, regAll, type, chaining);
+    } else {
+        stream = encoder_imm_reg(m, size, imm, reg, isPhysical, type, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one mem operand
+
+//!The mem operand is already allocated
+LowOpMemImm* lower_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size, int imm,
+                 int disp, int base_reg, MemoryAccessType mType, int mIndex,
+                 bool chaining) {
+    stream = encoder_imm_mem(m, size, imm, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMemImm* dump_imm_mem_noalloc(Mnemonic m, OpndSize size,
+                           int imm,
+                           int disp, int base_reg, bool isBasePhysical,
+                           MemoryAccessType mType, int mIndex) {
+    return lower_imm_mem(m, ATOM_NORMAL, size, imm, disp, base_reg, mType, mIndex, false);
+}
+//!update fields of LowOp and generate a x86 instruction that takes one immediate and one mem operand
+
+//!
+LowOpMemImm* dump_imm_mem(Mnemonic m, AtomOpCode m2, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical,
+                   MemoryAccessType mType, int mIndex, bool chaining) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        /* do not free register if the base is %edi, %esp, or %ebp
+           make sure dump_imm_mem will only generate a single instruction */
+        if(!isBasePhysical || (base_reg != PhysicalReg_EDI &&
+                               base_reg != PhysicalReg_ESP &&
+                               base_reg != PhysicalReg_EBP)) {
+            freeReg(true);
+        }
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_imm_mem(m, m2, size, imm, disp, baseAll, mType, mIndex, chaining);
+    } else {
+        stream = encoder_imm_mem(m, size, imm, disp, base_reg, isBasePhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that uses the FP stack and takes one mem operand
+
+//!
+LowOpMemReg* lower_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, MemoryAccessType mType, int mIndex) {
+    stream = encoder_fp_mem(m, size, reg, disp, base_reg, true, stream);
+    return NULL;
+}
+
+LowOpMemReg* dump_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_fp_mem(m, size, reg, disp, baseAll, mType, mIndex);
+    } else {
+        stream = encoder_fp_mem(m, size, reg, disp, base_reg, isBasePhysical, stream);
+    }
+    return NULL;
+}
+//!update fields of LowOp and generate a x86 instruction that uses the FP stack and takes one mem operand
+
+//!
+LowOpRegMem* lower_mem_fp(Mnemonic m, OpndSize size, int disp, int base_reg,
+                 MemoryAccessType mType, int mIndex, int reg) {
+    stream = encoder_mem_fp(m, size, disp, base_reg, true, reg, stream);
+    return NULL;
+}
+
+LowOpRegMem* dump_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        freeReg(true);
+        int baseAll = registerAlloc(LowOpndRegType_gp, base_reg, isBasePhysical, true);
+        return lower_mem_fp(m, size, disp, baseAll, mType, mIndex, reg);
+    } else {
+        stream = encoder_mem_fp(m, size, disp, base_reg, isBasePhysical, reg, stream);
+    }
+    return NULL;
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+//OPERAND ORDER:
+//LowOp same as EncoderBase destination first
+//parameter order of function: src first
+
+////////////////////////////////// IA32 native instructions //////////////
+//! generate a native instruction lea
+
+//!
+void load_effective_addr(int disp, int base_reg, bool isBasePhysical,
+                          int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_LEA;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+}
+//! generate a native instruction lea
+
+//!
+void load_effective_addr_scale(int base_reg, bool isBasePhysical,
+                int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_LEA;
+    dump_mem_scale_reg(m, OpndSize_32,
+                              base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, LowOpndRegType_gp);
+}
+//!fldcw
+
+//!
+void load_fpu_cw(int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_FLDCW;
+    dump_mem(m, ATOM_NORMAL, OpndSize_16, disp, base_reg, isBasePhysical);
+}
+//!fnstcw
+
+//!
+void store_fpu_cw(bool checkException, int disp, int base_reg, bool isBasePhysical) {
+    assert(!checkException);
+    Mnemonic m = Mnemonic_FNSTCW;
+    dump_mem(m, ATOM_NORMAL, OpndSize_16, disp, base_reg, isBasePhysical);
+}
+//!cdq
+
+//!
+void convert_integer(OpndSize srcSize, OpndSize dstSize) { //cbw, cwd, cdq
+    assert(srcSize == OpndSize_32 && dstSize == OpndSize_64);
+    Mnemonic m = Mnemonic_CDQ;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_EDX, true, LowOpndRegType_gp);
+}
+//!fld: load from memory (float or double) to stack
+
+//!
+void load_fp_stack(LowOp* op, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fld(s|l)
+    Mnemonic m = Mnemonic_FLD;
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0); //ST0
+}
+//! fild: load from memory (int or long) to stack
+
+//!
+void load_int_fp_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fild(ll|l)
+    Mnemonic m = Mnemonic_FILD;
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0); //ST0
+}
+//!fild: load from memory (absolute addr)
+
+//!
+void load_int_fp_stack_imm(OpndSize size, int imm) {//fild(ll|l)
+    return load_int_fp_stack(size, imm, PhysicalReg_Null, true);
+}
+//!fst: store from stack to memory (float or double)
+
+//!
+void store_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fst(p)(s|l)
+    Mnemonic m = pop ? Mnemonic_FSTP : Mnemonic_FST;
+    dump_fp_mem(m, size, 0, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1);
+}
+//!fist: store from stack to memory (int or long)
+
+//!
+void store_int_fp_stack(LowOp* op, bool pop, OpndSize size, int disp, int base_reg, bool isBasePhysical) {//fist(p)(l)
+    Mnemonic m = pop ? Mnemonic_FISTP : Mnemonic_FIST;
+    dump_fp_mem(m, size, 0, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1);
+}
+//!cmp reg, mem
+
+//!
+void compare_reg_mem(LowOp* op, OpndSize size, int reg, bool isPhysical,
+              int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!cmp mem, reg
+
+//!
+void compare_mem_reg(OpndSize size,
+              int disp, int base_reg, bool isBasePhysical,
+              int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+//! compare a VR with a temporary variable
+
+//!
+void compare_VR_reg_all(OpndSize size,
+             int vA,
+             int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;
+    if(m == Mnemonic_COMISS) {
+        size = OpndSize_32;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, type, tmpValue, true/*updateRefCount*/);
+        if(isConst == 3) {
+            if(m == Mnemonic_COMISS) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and SS in compare_VR_reg");
+#endif
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                //dumpImmToMem(vA+1, OpndSize_32, 0); //CHECK necessary? will overwrite vA+1!!!
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, reg, isPhysical, pType);
+                return;
+            }
+            else if(size != OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 32 bits in compare_VR_reg");
+#endif
+                dump_imm_reg(m, ATOM_NORMAL, size, tmpValue[0], reg, isPhysical, pType, false);
+                return;
+            }
+            else if(size == OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 64 bits in compare_VR_reg");
+#endif
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vA, reg, isPhysical, pType);
+                return;
+            }
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, type, 0/*do not update*/);
+        if(regAll != PhysicalReg_Null) { //do not spill regAll when allocating register for dst
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL, size, regAll, true, reg, isPhysical, pType);
+            endNativeCode();
+        }
+        else {
+            //virtual register is not allocated to a physical register
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, pType);
+        }
+        updateRefCount(vA, type);
+        return;
+    } else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, reg, isPhysical, pType);
+        return;
+    }
+}
+void compare_VR_reg(OpndSize size,
+             int vA,
+             int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    return compare_VR_reg_all(size, vA, reg, isPhysical, m);
+}
+void compare_VR_ss_reg(int vA, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISS;
+    return compare_VR_reg_all(OpndSize_32, vA, reg, isPhysical, m);
+}
+void compare_VR_sd_reg(int vA, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISD;
+    return compare_VR_reg_all(OpndSize_64, vA, reg, isPhysical, m);
+}
+//!load VR to stack
+
+//!
+void load_fp_stack_VR_all(OpndSize size, int vB, Mnemonic m) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //can't load from immediate to fp stack
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vB, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            if(size != OpndSize_64) {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 32 bits in load_fp_stack");
+#endif
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+            }
+            else {
+#ifdef DEBUG_NCG_O1
+                LOGI("VR is const and 64 bits in load_fp_stack_VR");
+#endif
+                if(isConst == 1 || isConst == 3) dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                if(isConst == 2 || isConst == 3) dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+            }
+        }
+        else { //if VR was updated by a def of gp, a xfer point was inserted
+            //if VR was updated by a def of xmm, a xfer point was inserted
+#if 0
+            int regAll = checkVirtualReg(vB, size, 1);
+            if(regAll != PhysicalReg_Null) //dump from register to memory
+                dump_reg_mem_noalloc(m, size, regAll, true, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, getTypeFromIntSize(size));
+#endif
+        }
+        dump_mem_fp(m, size, 4*vB, PhysicalReg_FP, true, MemoryAccess_VR, vB, 0);
+    } else {
+        dump_mem_fp(m, size, 4*vB, PhysicalReg_FP, true, MemoryAccess_VR, vB, 0);
+    }
+}
+//!load VR(float or double) to stack
+
+//!
+void load_fp_stack_VR(OpndSize size, int vA) {//fld(s|l)
+    Mnemonic m = Mnemonic_FLD;
+    return load_fp_stack_VR_all(size, vA, m);
+}
+//!load VR(int or long) to stack
+
+//!
+void load_int_fp_stack_VR(OpndSize size, int vA) {//fild(ll|l)
+    Mnemonic m = Mnemonic_FILD;
+    return load_fp_stack_VR_all(size, vA, m);
+}
+//!store from stack to VR (float or double)
+
+//!
+void store_fp_stack_VR(bool pop, OpndSize size, int vA) {//fst(p)(s|l)
+    Mnemonic m = pop ? Mnemonic_FSTP : Mnemonic_FST;
+    dump_fp_mem(m, size, 0, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size == OpndSize_32)
+            updateVirtualReg(vA, LowOpndRegType_fs_s);
+        else
+            updateVirtualReg(vA, LowOpndRegType_fs);
+    }
+}
+//!store from stack to VR (int or long)
+
+//!
+void store_int_fp_stack_VR(bool pop, OpndSize size, int vA) {//fist(p)(l)
+    Mnemonic m = pop ? Mnemonic_FISTP : Mnemonic_FIST;
+    dump_fp_mem(m, size, 0, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size == OpndSize_32)
+            updateVirtualReg(vA, LowOpndRegType_fs_s);
+        else
+            updateVirtualReg(vA, LowOpndRegType_fs);
+    }
+}
+//! ALU ops in FPU, one operand is a VR
+
+//!
+void fpu_VR(ALU_Opcode opc, OpndSize size, int vA) {
+    Mnemonic m = map_of_fpu_opcode_2_mnemonic[opc];
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            if(size != OpndSize_64) {
+                //allocate a register for dst
+                dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            }
+            else {
+                if((isConst == 1 || isConst == 3) && size == OpndSize_64) {
+                    dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+                }
+                if((isConst == 2 || isConst == 3) && size == OpndSize_64) {
+                    dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+                }
+            }
+        }
+        if(!isInMemory(vA, size)) {
+            ALOGE("fpu_VR");
+        }
+        dump_mem_fp(m, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, 0);
+    } else {
+        dump_mem_fp(m, size, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, 0);
+    }
+}
+//! cmp imm reg
+
+//!
+void compare_imm_reg(OpndSize size, int imm,
+              int reg, bool isPhysical) {
+    if(imm == 0) {
+        LowOpndRegType type = getTypeFromIntSize(size);
+        Mnemonic m = Mnemonic_TEST;
+        if(gDvm.executionMode == kExecutionModeNcgO1) {
+            freeReg(true);
+            int regAll = registerAlloc(type, reg, isPhysical, true);
+            lower_reg_reg(m, ATOM_NORMAL, size, regAll, regAll, type);
+        } else {
+            stream = encoder_reg_reg(m, size, reg, isPhysical, reg, isPhysical, type, stream);
+        }
+        return;
+    }
+    Mnemonic m = Mnemonic_CMP;
+    dump_imm_reg(m, ATOM_NORMAL, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//! cmp imm mem
+
+//!
+void compare_imm_mem(OpndSize size, int imm,
+              int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_imm_mem(m, ATOM_NORMAL, size, imm, disp,
+                        base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//! cmp imm VR
+
+//!
+void compare_imm_VR(OpndSize size, int imm,
+             int vA) {
+    Mnemonic m = Mnemonic_CMP;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(size != OpndSize_32) ALOGE("only 32 bits supported in compare_imm_VR");
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue, false/*updateRefCount*/);
+        if(isConst > 0) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        }
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null)
+            dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+        else
+            dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA);
+        updateRefCount(vA, getTypeFromIntSize(size));
+    } else {
+        dump_imm_mem(m, ATOM_NORMAL, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, false);
+    }
+}
+//! cmp reg reg
+
+//!
+void compare_reg_reg(int reg1, bool isPhysical1,
+              int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_32, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_gp);
+}
+void compare_reg_reg_16(int reg1, bool isPhysical1,
+              int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_CMP;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_16, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_gp);
+}
+
+//! comiss mem reg
+
+//!SSE, XMM: comparison of floating point numbers
+void compare_ss_mem_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISS;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//! comiss reg reg
+
+//!
+void compare_ss_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_COMISS;
+    dump_reg_reg(m,  ATOM_NORMAL, OpndSize_32, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//! comisd mem reg
+
+//!
+void compare_sd_mem_with_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                  int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_COMISD;
+    dump_mem_reg(m, ATOM_NORMAL, OpndSize_64, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//! comisd reg reg
+
+//!
+void compare_sd_reg_with_reg(LowOp* op, int reg1, bool isPhysical1,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_COMISD;
+    dump_reg_reg(m, ATOM_NORMAL, OpndSize_64, reg1, isPhysical1, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//! fucom[p]
+
+//!
+void compare_fp_stack(bool pop, int reg, bool isDouble) { //compare ST(0) with ST(reg)
+    Mnemonic m = pop ? Mnemonic_FUCOMP : Mnemonic_FUCOM;
+    lower_reg_reg(m, ATOM_NORMAL, isDouble ? OpndSize_64 : OpndSize_32,
+                  PhysicalReg_ST0+reg, PhysicalReg_ST0, LowOpndRegType_fs);
+}
+/*!
+\brief generate a single return instruction
+
+*/
+LowOp* lower_return() {
+    stream = encoder_return(stream);
+    return NULL;
+}
+
+void x86_return() {
+    lower_return();
+}
+
+//!test imm reg
+
+//!
+void test_imm_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    dump_imm_reg(Mnemonic_TEST, ATOM_NORMAL, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//!test imm mem
+
+//!
+void test_imm_mem(OpndSize size, int imm, int disp, int reg, bool isPhysical) {
+    dump_imm_mem(Mnemonic_TEST, ATOM_NORMAL, size, imm, disp, reg, isPhysical, MemoryAccess_Unknown, -1, false);
+}
+//!alu unary op with one reg operand
+
+//!
+void alu_unary_reg(OpndSize size, ALU_Opcode opc, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg(m, ATOM_NORMAL_ALU, size, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!alu unary op with one mem operand
+
+//!
+void alu_unary_mem(LowOp* op, OpndSize size, ALU_Opcode opc, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_mem(m, ATOM_NORMAL_ALU, size, disp, base_reg, isBasePhysical);
+}
+//!alu binary op with immediate and one mem operand
+
+//!
+void alu_binary_imm_mem(OpndSize size, ALU_Opcode opc, int imm, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_imm_mem(m, ATOM_NORMAL_ALU, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//!alu binary op with immediate and one reg operand
+
+//!
+void alu_binary_imm_reg(OpndSize size, ALU_Opcode opc, int imm, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_imm_reg(m, ATOM_NORMAL_ALU, size, imm, reg, isPhysical, getTypeFromIntSize(size), false);
+}
+//!alu binary op with one mem operand and one reg operand
+
+//!
+void alu_binary_mem_reg(OpndSize size, ALU_Opcode opc,
+             int disp, int base_reg, bool isBasePhysical,
+             int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_mem_reg(m, ATOM_NORMAL_ALU, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+
+void alu_sd_binary_VR_reg(ALU_Opcode opc, int vA, int reg, bool isPhysical, bool isSD) {
+    Mnemonic m;
+    if(isSD) m = map_of_sse_opcode_2_mnemonic[opc];
+    else m = (Mnemonic)(map_of_sse_opcode_2_mnemonic[opc]+1); //from SD to SS
+    OpndSize size = isSD ? OpndSize_64 : OpndSize_32;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        LowOpndRegType type = isSD ? LowOpndRegType_xmm : LowOpndRegType_ss; //type of the mem operand
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, type, tmpValue,
+                          true/*updateRefCount*/);
+        if(isConst == 3 && !isSD) {
+            //isConst can be 0 or 3, mem32, use xmm
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, OpndSize_32, 4*vA, PhysicalReg_FP, true,
+                       MemoryAccess_VR, vA, reg, isPhysical,
+                       LowOpndRegType_xmm);
+            return;
+        }
+        if(isConst == 3 && isSD) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, OpndSize_64, 4*vA, PhysicalReg_FP, true,
+                       MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+            return;
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+
+        int regAll = checkVirtualReg(vA, type, 0/*do not update refCount*/);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(-1, -1); //should we use vA, type
+            //CHECK: callupdateVRAtUse
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL_ALU, size, regAll, true, reg,
+                         isPhysical, LowOpndRegType_xmm);
+            endNativeCode();
+        }
+        else {
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                         MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+        }
+        updateRefCount(vA, type);
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vA, reg, isPhysical, LowOpndRegType_xmm);
+    }
+}
+
+//!alu binary op with a VR and one reg operand
+
+//!
+void alu_binary_VR_reg(OpndSize size, ALU_Opcode opc, int vA, int reg, bool isPhysical) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst = isVirtualRegConstant(vA, getTypeFromIntSize(size), tmpValue,
+                          true/*updateRefCount*/);
+        if(isConst == 3 && size != OpndSize_64) {
+            //allocate a register for dst
+            dump_imm_reg(m, ATOM_NORMAL_ALU, size, tmpValue[0], reg, isPhysical,
+                       getTypeFromIntSize(size), false);
+            return;
+        }
+        if(isConst == 3 && size == OpndSize_64) {
+            dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+            dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+            dump_mem_reg(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+            return;
+        }
+        if(isConst == 1) dumpImmToMem(vA, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vA+1, OpndSize_32, tmpValue[1]);
+
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m, ATOM_NORMAL_ALU, size, regAll, true, reg,
+                         isPhysical, getTypeFromIntSize(size));
+            endNativeCode();
+        }
+        else {
+            dump_mem_reg_noalloc_mem(m, ATOM_NORMAL_ALU, size, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+        }
+        updateRefCount(vA, getTypeFromIntSize(size));
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, reg, isPhysical, getTypeFromIntSize(size));
+    }
+}
+//!alu binary op with two reg operands
+
+//!
+void alu_binary_reg_reg(OpndSize size, ALU_Opcode opc,
+                         int reg1, bool isPhysical1,
+                         int reg2, bool isPhysical2) {
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg_reg(m, ATOM_NORMAL_ALU, size, reg1, isPhysical1, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!alu binary op with one reg operand and one mem operand
+
+//!
+void alu_binary_reg_mem(OpndSize size, ALU_Opcode opc,
+             int reg, bool isPhysical,
+             int disp, int base_reg, bool isBasePhysical) { //destination is mem!!
+    Mnemonic m;
+    if(size == OpndSize_64)
+        m = map_of_64_opcode_2_mnemonic[opc];
+    else
+        m = map_of_alu_opcode_2_mnemonic[opc];
+    dump_reg_mem(m, ATOM_NORMAL_ALU, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!FPU ops with one mem operand
+
+//!
+void fpu_mem(LowOp* op, ALU_Opcode opc, OpndSize size, int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = map_of_fpu_opcode_2_mnemonic[opc];
+    dump_mem_fp(m, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, 0);
+}
+//!SSE 32-bit ALU
+
+//!
+void alu_ss_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                int reg2, bool isPhysical2) {
+    Mnemonic m = (Mnemonic)(map_of_sse_opcode_2_mnemonic[opc]+1); //from SD to SS
+    dump_reg_reg(m, ATOM_NORMAL_ALU, OpndSize_32, reg, isPhysical, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//!SSE 64-bit ALU
+
+//!
+void alu_sd_binary_reg_reg(ALU_Opcode opc, int reg, bool isPhysical,
+                int reg2, bool isPhysical2) {
+    Mnemonic m = map_of_sse_opcode_2_mnemonic[opc];
+    dump_reg_reg(m, ATOM_NORMAL_ALU, OpndSize_64, reg, isPhysical, reg2, isPhysical2, LowOpndRegType_xmm);
+}
+//!push reg to native stack
+
+//!
+void push_reg_to_stack(OpndSize size, int reg, bool isPhysical) {
+    dump_reg(Mnemonic_PUSH, ATOM_NORMAL, size, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!push mem to native stack
+
+//!
+void push_mem_to_stack(OpndSize size, int disp, int base_reg, bool isBasePhysical) {
+    dump_mem(Mnemonic_PUSH, ATOM_NORMAL, size, disp, base_reg, isBasePhysical);
+}
+//!move from reg to memory
+
+//!
+void move_reg_to_mem(OpndSize size,
+                      int reg, bool isPhysical,
+                      int disp, int base_reg, bool isBasePhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, getTypeFromIntSize(size));
+}
+//!move from reg to memory
+
+//!Operands are already allocated
+void move_reg_to_mem_noalloc(OpndSize size,
+                  int reg, bool isPhysical,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_noalloc(m, size, reg, isPhysical, disp, base_reg, isBasePhysical, mType, mIndex, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!
+LowOpRegMem* move_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return dump_mem_reg(m, ATOM_NORMAL, size, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!Operands are already allocated
+LowOpRegMem* move_mem_to_reg_noalloc(OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  MemoryAccessType mType, int mIndex,
+                  int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return dump_mem_reg_noalloc(m, size, disp, base_reg, isBasePhysical, mType, mIndex, reg, isPhysical, getTypeFromIntSize(size));
+}
+//!movss from memory to reg
+
+//!Operands are already allocated
+LowOpRegMem* move_ss_mem_to_reg_noalloc(int disp, int base_reg, bool isBasePhysical,
+                 MemoryAccessType mType, int mIndex,
+                 int reg, bool isPhysical) {
+    return dump_mem_reg_noalloc(Mnemonic_MOVSS, OpndSize_32, disp, base_reg, isBasePhysical, mType, mIndex, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movss from reg to memory
+
+//!Operands are already allocated
+LowOpMemReg* move_ss_reg_to_mem_noalloc(int reg, bool isPhysical,
+                 int disp, int base_reg, bool isBasePhysical,
+                 MemoryAccessType mType, int mIndex) {
+    return dump_reg_mem_noalloc(Mnemonic_MOVSS, OpndSize_32, reg, isPhysical, disp, base_reg, isBasePhysical, mType, mIndex, LowOpndRegType_xmm);
+}
+//!movzx from memory to reg
+
+//!
+void movez_mem_to_reg(OpndSize size,
+               int disp, int base_reg, bool isBasePhysical,
+               int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_MOVZX;
+    dump_movez_mem_reg(m, size, disp, base_reg, isBasePhysical, reg, isPhysical);
+}
+
+//!movzx from one reg to another reg
+
+//!
+void movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2) {
+    Mnemonic m = Mnemonic_MOVZX;
+    dump_movez_reg_reg(m, size, reg, isPhysical, reg2, isPhysical2);
+}
+
+void movez_mem_disp_scale_to_reg(OpndSize size,
+                 int base_reg, bool isBasePhysical,
+                 int disp, int index_reg, bool isIndexPhysical, int scale,
+                 int reg, bool isPhysical) {
+    dump_mem_scale_reg(Mnemonic_MOVZX, size, base_reg, isBasePhysical,
+                 disp, index_reg, isIndexPhysical, scale,
+                 reg, isPhysical, LowOpndRegType_gp);
+}
+void moves_mem_disp_scale_to_reg(OpndSize size,
+                  int base_reg, bool isBasePhysical,
+                  int disp, int index_reg, bool isIndexPhysical, int scale,
+                  int reg, bool isPhysical) {
+    dump_mem_scale_reg(Mnemonic_MOVSX, size, base_reg, isBasePhysical,
+                  disp, index_reg, isIndexPhysical, scale,
+                  reg, isPhysical, LowOpndRegType_gp);
+}
+
+//!movsx from memory to reg
+
+//!
+void moves_mem_to_reg(LowOp* op, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical,
+               int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_MOVSX;
+    dump_moves_mem_reg(m, size, disp, base_reg, isBasePhysical, reg, isPhysical);
+}
+//!mov from one reg to another reg
+
+//!
+void move_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,
+                      int reg2, bool isPhysical2) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_reg(m, ATOM_NORMAL, size, reg, isPhysical, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!mov from one reg to another reg
+
+//!Operands are already allocated
+void move_reg_to_reg_noalloc(OpndSize size,
+                  int reg, bool isPhysical,
+                  int reg2, bool isPhysical2) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_reg_noalloc(m, size, reg, isPhysical, reg2, isPhysical2, getTypeFromIntSize(size));
+}
+//!move from memory to reg
+
+//!
+void move_mem_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_scale_reg(m, size, base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, getTypeFromIntSize(size));
+}
+void move_mem_disp_scale_to_reg(OpndSize size,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_scale_reg(m, size, base_reg, isBasePhysical, disp, index_reg, isIndexPhysical, scale,
+                              reg, isPhysical, getTypeFromIntSize(size));
+}
+//!move from reg to memory
+
+//!
+void move_reg_to_mem_scale(OpndSize size,
+                int reg, bool isPhysical,
+                int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_scale(m, size, reg, isPhysical,
+                              base_reg, isBasePhysical, 0/*disp*/, index_reg, isIndexPhysical, scale,
+                              getTypeFromIntSize(size));
+}
+void move_reg_to_mem_disp_scale(OpndSize size,
+                int reg, bool isPhysical,
+                int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_scale(m, size, reg, isPhysical,
+                              base_reg, isBasePhysical, disp, index_reg, isIndexPhysical, scale,
+                              getTypeFromIntSize(size));
+}
+
+void move_chain_to_mem(OpndSize size, int imm,
+                        int disp, int base_reg, bool isBasePhysical) {
+    dump_imm_mem(Mnemonic_MOV, ATOM_NORMAL, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, true);
+}
+
+//!move an immediate to memory
+
+//!
+void move_imm_to_mem(OpndSize size, int imm,
+                      int disp, int base_reg, bool isBasePhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    dump_imm_mem(Mnemonic_MOV, ATOM_NORMAL, size, imm, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, false);
+}
+//! set a VR to an immediate
+
+//!
+void set_VR_to_imm(u2 vA, OpndSize size, int imm) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int regAll = checkVirtualReg(vA, getTypeFromIntSize(size), 0);
+        if(regAll != PhysicalReg_Null) {
+            dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+            updateRefCount(vA, getTypeFromIntSize(size));
+            updateVirtualReg(vA, getTypeFromIntSize(size));
+            return;
+        }
+        //will call freeReg
+        freeReg(true);
+        regAll = registerAlloc(LowOpndRegType_virtual | getTypeFromIntSize(size), vA, false/*dummy*/, true);
+        if(regAll == PhysicalReg_Null) {
+            dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+            return;
+        }
+        dump_imm_reg_noalloc(m, size, imm, regAll, true, LowOpndRegType_gp);
+        updateVirtualReg(vA, getTypeFromIntSize(size));
+    }
+    else {
+        dump_imm_mem(m, ATOM_NORMAL, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA, false);
+    }
+}
+void set_VR_to_imm_noupdateref(LowOp* op, u2 vA, OpndSize size, int imm) {
+    return;
+}
+//! set a VR to an immediate
+
+//! Do not allocate a physical register for the VR
+void set_VR_to_imm_noalloc(u2 vA, OpndSize size, int imm) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_mem with 64 bits");
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_imm_mem_noalloc(m, size, imm, 4*vA, PhysicalReg_FP, true, MemoryAccess_VR, vA);
+}
+
+void move_chain_to_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    dump_imm_reg(Mnemonic_MOV, ATOM_NORMAL, size, imm, reg, isPhysical, LowOpndRegType_gp, true);
+}
+
+//! move an immediate to reg
+
+//!
+void move_imm_to_reg(OpndSize size, int imm, int reg, bool isPhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_reg with 64 bits");
+    Mnemonic m = Mnemonic_MOV;
+    dump_imm_reg(m, ATOM_NORMAL, size, imm, reg, isPhysical, LowOpndRegType_gp, false);
+}
+//! move an immediate to reg
+
+//! The operand is already allocated
+void move_imm_to_reg_noalloc(OpndSize size, int imm, int reg, bool isPhysical) {
+    assert(size != OpndSize_64);
+    if(size == OpndSize_64) ALOGE("move_imm_to_reg with 64 bits");
+    Mnemonic m = Mnemonic_MOV;
+    dump_imm_reg_noalloc(m, size, imm, reg, isPhysical, LowOpndRegType_gp);
+}
+//!cmov from reg to reg
+
+//!
+void conditional_move_reg_to_reg(OpndSize size, ConditionCode cc, int reg1, bool isPhysical1, int reg, bool isPhysical) {
+    Mnemonic m = (Mnemonic)(Mnemonic_CMOVcc+cc);
+    dump_reg_reg(m, ATOM_NORMAL, size, reg1, isPhysical1, reg, isPhysical, LowOpndRegType_gp);
+}
+//!movss from memory to reg
+
+//!
+void move_ss_mem_to_reg(LowOp* op, int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical) {
+    dump_mem_reg(Mnemonic_MOVSS, ATOM_NORMAL, OpndSize_32, disp, base_reg, isBasePhysical,
+        MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movss from reg to memory
+
+//!
+void move_ss_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical) {
+    dump_reg_mem(Mnemonic_MOVSS, ATOM_NORMAL, OpndSize_32, reg, isPhysical, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, LowOpndRegType_xmm);
+}
+//!movsd from memory to reg
+
+//!
+void move_sd_mem_to_reg(int disp, int base_reg, bool isBasePhysical,
+                         int reg, bool isPhysical) {
+    dump_mem_reg(Mnemonic_MOVSD, ATOM_NORMAL, OpndSize_64, disp, base_reg, isBasePhysical, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_xmm);
+}
+//!movsd from reg to memory
+
+//!
+void move_sd_reg_to_mem(LowOp* op, int reg, bool isPhysical,
+                         int disp, int base_reg, bool isBasePhysical) {
+    dump_reg_mem(Mnemonic_MOVSD, ATOM_NORMAL, OpndSize_64, reg, isPhysical,
+                        disp, base_reg, isBasePhysical,
+                        MemoryAccess_Unknown, -1, LowOpndRegType_xmm);
+}
+//!load from VR to a temporary
+
+//!
+void get_virtual_reg_all(u2 vB, OpndSize size, int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;//gp or xmm
+    OpndSize size2 = size;
+    Mnemonic m2 = m;
+    if(m == Mnemonic_MOVSS) {
+        size = OpndSize_32;
+        size2 = OpndSize_64;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+        m2 = Mnemonic_MOVQ; //to move from one xmm register to another
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        int tmpValue[2];
+        int isConst;
+        isConst = isVirtualRegConstant(vB, type, tmpValue, true/*updateRefCount*/);
+        if(isConst == 3) {
+            if(m == Mnemonic_MOVSS) { //load 32 bits from VR
+                //VR is not mapped to a register but in memory
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                //temporary reg has "pType" (which is xmm)
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, reg, isPhysical, pType);
+                return;
+            }
+            else if(m == Mnemonic_MOVSD || size == OpndSize_64) {
+                //VR is not mapped to a register but in memory
+                dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+                dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+                dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+                    MemoryAccess_VR, vB, reg, isPhysical, pType);
+                return;
+            }
+            else if(size != OpndSize_64) {
+                //VR is not mapped to a register
+                dump_imm_reg(m, ATOM_NORMAL, size, tmpValue[0], reg, isPhysical, pType, false);
+                return;
+            }
+        }
+        if(isConst == 1) dumpImmToMem(vB, OpndSize_32, tmpValue[0]);
+        if(isConst == 2) dumpImmToMem(vB+1, OpndSize_32, tmpValue[1]);
+        freeReg(true);
+        int regAll = checkVirtualReg(vB, type, 0);
+        if(regAll != PhysicalReg_Null) {
+            startNativeCode(vB, type);
+            donotSpillReg(regAll);
+            //check XFER_MEM_TO_XMM
+            updateVRAtUse(vB, type, regAll);
+            //temporary reg has "pType"
+            dump_reg_reg_noalloc_src(m2, ATOM_NORMAL, size2, regAll, true, reg, isPhysical, pType); //register allocator handles assembly move
+            endNativeCode();
+            updateRefCount(vB, type);
+            return;
+        }
+        //not allocated to a register yet, no need to check XFER_MEM_TO_XMM
+        regAll = registerAlloc(LowOpndRegType_virtual | type, vB, false/*dummy*/, false);
+        if(regAll == PhysicalReg_Null) {
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, reg, isPhysical, pType);
+            return;
+        }
+
+        //temporary reg has pType
+        if(checkTempReg2(reg, pType, isPhysical, regAll)) {
+            registerAllocMove(reg, pType, isPhysical, regAll);
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, regAll, true, pType);
+            updateRefCount(vB, type);
+            return;
+        }
+        else {
+            dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+                MemoryAccess_VR, vB, regAll, true, pType);
+            //xmm with 32 bits
+            startNativeCode(vB, type);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_src(m2, ATOM_NORMAL, size2, regAll, true, reg, isPhysical, pType);
+            endNativeCode();
+            updateRefCount(vB, type);
+            return;
+        }
+    }
+    else {
+        dump_mem_reg(m, ATOM_NORMAL, size, 4*vB, PhysicalReg_FP, true,
+            MemoryAccess_VR, vB, reg, isPhysical, pType);
+    }
+}
+void get_virtual_reg(u2 vB, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return get_virtual_reg_all(vB, size, reg, isPhysical, m);
+}
+void get_virtual_reg_noalloc(u2 vB, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_mem_reg_noalloc(m, size, 4*vB, PhysicalReg_FP, true,
+        MemoryAccess_VR, vB, reg, isPhysical, getTypeFromIntSize(size));
+}
+//3 cases: gp, xmm, ss
+//ss: the temporary register is xmm
+//!load from a temporary to a VR
+
+//!
+void set_virtual_reg_all(u2 vA, OpndSize size, int reg, bool isPhysical, Mnemonic m) {
+    LowOpndRegType type = getTypeFromIntSize(size);
+    LowOpndRegType pType = type;//gp or xmm
+    OpndSize size2 = size;
+    Mnemonic m2 = m;
+    if(m == Mnemonic_MOVSS) {
+        size = OpndSize_32;
+        size2 = OpndSize_64;
+        type = LowOpndRegType_ss;
+        pType = LowOpndRegType_xmm;
+        m2 = Mnemonic_MOVQ;
+    }
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //3 cases
+        //1: virtual register is already allocated to a physical register
+        //   call dump_reg_reg_noalloc_dst
+        //2: src reg is already allocated, VR is not yet allocated
+        //   allocate VR to the same physical register used by src reg
+        //   [call registerAllocMove]
+        //3: both not yet allocated
+        //   allocate a physical register for the VR
+        //   then call dump_reg_reg_noalloc_dst
+        //may need to convert from gp to xmm or the other way
+        freeReg(true);
+        int regAll = checkVirtualReg(vA, type, 0);
+        if(regAll != PhysicalReg_Null)  { //case 1
+            startNativeCode(-1, -1);
+            donotSpillReg(regAll);
+            dump_reg_reg_noalloc_dst(m2, size2, reg, isPhysical, regAll, true, pType); //temporary reg is "pType"
+            endNativeCode();
+            updateRefCount(vA, type);
+            updateVirtualReg(vA, type); //will dump VR to memory, should happen afterwards
+            return;
+        }
+        regAll = checkTempReg(reg, pType, isPhysical, vA); //vA is not used inside
+        if(regAll != PhysicalReg_Null) { //case 2
+            registerAllocMove(vA, LowOpndRegType_virtual | type, false, regAll);
+            updateVirtualReg(vA, type); //will dump VR to memory, should happen afterwards
+            return; //next native instruction starts at op
+        }
+        //case 3
+        regAll = registerAlloc(LowOpndRegType_virtual | type, vA, false/*dummy*/, false);
+        if(regAll == PhysicalReg_Null) {
+            dump_reg_mem_noalloc(m, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+                MemoryAccess_VR, vA, pType);
+            return;
+        }
+        startNativeCode(-1, -1);
+        donotSpillReg(regAll);
+        dump_reg_reg_noalloc_dst(m2, size2, reg, isPhysical, regAll, true, pType);
+        endNativeCode();
+        updateRefCount(vA, type);
+        updateVirtualReg(vA, type);
+    }
+    else {
+        dump_reg_mem(m, ATOM_NORMAL, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+            MemoryAccess_VR, vA, pType);
+    }
+}
+void set_virtual_reg(u2 vA, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    return set_virtual_reg_all(vA, size, reg, isPhysical, m);
+}
+void set_virtual_reg_noalloc(u2 vA, OpndSize size, int reg, bool isPhysical) {
+    Mnemonic m = (size == OpndSize_64) ? Mnemonic_MOVQ : Mnemonic_MOV;
+    dump_reg_mem_noalloc(m, size, reg, isPhysical, 4*vA, PhysicalReg_FP, true,
+        MemoryAccess_VR, vA, getTypeFromIntSize(size));
+}
+void get_VR_ss(int vB, int reg, bool isPhysical) {
+    return get_virtual_reg_all(vB, OpndSize_64, reg, isPhysical, Mnemonic_MOVSS);
+}
+void set_VR_ss(int vA, int reg, bool isPhysical) {
+    return set_virtual_reg_all(vA, OpndSize_64, reg, isPhysical, Mnemonic_MOVSS);
+}
+void get_VR_sd(int vB, int reg, bool isPhysical) {
+    return get_virtual_reg_all(vB, OpndSize_64, reg, isPhysical, Mnemonic_MOVSD);
+}
+void set_VR_sd(int vA, int reg, bool isPhysical) {
+    return set_virtual_reg_all(vA, OpndSize_64, reg, isPhysical, Mnemonic_MOVSD);
+}
+////////////////////////////////// END: IA32 native instructions //////////////
+//! generate native instructions to get current PC in the stack frame
+
+//!
+int get_currentpc(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true, reg, isPhysical);
+    return 1;
+}
+//!generate native code to perform null check
+
+//!This function does not export PC
+int simpleNullCheck(int reg, bool isPhysical, int vr) {
+    if(isVRNullCheck(vr, OpndSize_32)) {
+        updateRefCount2(reg, LowOpndRegType_gp, isPhysical);
+        num_removed_nullCheck++;
+        return 0;
+    }
+    compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+    setVRNullCheck(vr, OpndSize_32);
+    return 0;
+}
+
+/* only for O1 code generator */
+int boundCheck(int vr_array, int reg_array, bool isPhysical_array,
+               int vr_index, int reg_index, bool isPhysical_index,
+               int exceptionNum) {
+#ifdef BOUNDCHECK_OPT
+    if(isVRBoundCheck(vr_array, vr_index)) {
+        updateRefCount2(reg_array, LowOpndRegType_gp, isPhysical_array);
+        updateRefCount2(reg_index, LowOpndRegType_gp, isPhysical_index);
+        return 0;
+    }
+#endif
+    compare_mem_reg(OpndSize_32, offArrayObject_length,
+                    reg_array, isPhysical_array,
+                    reg_index, isPhysical_index);
+
+    char errName[256];
+    sprintf(errName, "common_errArrayIndex");
+    handlePotentialException(
+                                       Condition_NC, Condition_C,
+                                       exceptionNum, errName);
+#ifdef BOUNDCHECK_OPT
+    setVRBoundCheck(vr_array, vr_index);
+#endif
+    return 0;
+}
+
+//!generate native code to perform null check
+
+//!
+int nullCheck(int reg, bool isPhysical, int exceptionNum, int vr) {
+    char label[LABEL_SIZE];
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //nullCheck optimization is available in O1 mode only
+        if(isVRNullCheck(vr, OpndSize_32)) {
+            updateRefCount2(reg, LowOpndRegType_gp, isPhysical);
+            if(exceptionNum <= 1) {
+                updateRefCount2(PhysicalReg_EDX, LowOpndRegType_gp, true);
+                updateRefCount2(PhysicalReg_EDX, LowOpndRegType_gp, true);
+            }
+            num_removed_nullCheck++;
+            return 0;
+        }
+        compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+        rememberState(exceptionNum);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(Condition_NE, label, true);
+        if(exceptionNum > 1)
+            nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc(); //use %edx
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        unconditional_jump_global_API("common_errNullObject", false);
+        insertLabel(label, true);
+        goToState(exceptionNum);
+        setVRNullCheck(vr, OpndSize_32);
+    } else {
+        compare_imm_reg(OpndSize_32, 0, reg, isPhysical);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(Condition_NE, label, true);
+        export_pc(); //use %edx
+        unconditional_jump_global_API("common_errNullObject", false);
+        insertLabel(label, true);
+    }
+    return 0;
+}
+//!generate native code to handle potential exception
+
+//!
+int handlePotentialException(
+                             ConditionCode code_excep, ConditionCode code_okay,
+                             int exceptionNum, const char* errName) {
+    char label[LABEL_SIZE];
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        rememberState(exceptionNum);
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(code_okay, label, true);
+        if(exceptionNum > 1)
+            nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc(); //use %edx
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+        if(!strcmp(errName, "common_throw_message")) {
+            move_imm_to_reg(OpndSize_32, LstrInstantiationErrorPtr, PhysicalReg_ECX, true);
+        }
+        unconditional_jump_global_API(errName, false);
+        insertLabel(label, true);
+        goToState(exceptionNum);
+    } else {
+        snprintf(label, LABEL_SIZE, "after_exception_%d", exceptionNum);
+        conditional_jump(code_okay, label, true);
+        export_pc(); //use %edx
+        if(!strcmp(errName, "common_throw_message")) {
+            move_imm_to_reg(OpndSize_32, LstrInstantiationErrorPtr, PhysicalReg_ECX, true);
+        }
+        unconditional_jump_global_API(errName, false);
+        insertLabel(label, true);
+    }
+    return 0;
+}
+//!generate native code to get the self pointer from glue
+
+//!It uses one scratch register
+int get_self_pointer(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, offEBP_self, PhysicalReg_EBP, true, reg, isPhysical);
+    return 0;
+}
+//!generate native code to get ResStrings from glue
+
+//!It uses two scratch registers
+int get_res_strings(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, update spill_loc_index & physicalReg
+#if 0
+        updateRefCount2(C_SCRATCH_1, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_1, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_2, LowOpndRegType_gp, isScratchPhysical);
+        updateRefCount2(C_SCRATCH_2, LowOpndRegType_gp, isScratchPhysical);
+#endif
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResStrings, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResStrings, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+int get_res_classes(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResClasses, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResClasses, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get ResFields from glue
+
+//!It uses two scratch registers
+int get_res_fields(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResFields, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResFields, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get ResMethods from glue
+
+//!It uses two scratch registers
+int get_res_methods(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_mem_reg_noalloc_mem(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, offDvmDex_pResMethods, regAll, true, MemoryAccess_Unknown, -1, reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(C_SCRATCH_2, isScratchPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+            move_mem_to_reg(OpndSize_32, offDvmDex_pResMethods, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+        }
+    return 0;
+}
+//!generate native code to get the current class object from glue
+
+//!It uses two scratch registers
+int get_glue_method_class(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to get the current method from glue
+
+//!It uses one scratch register
+int get_glue_method(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set the current method in glue
+
+//!It uses one scratch register
+int set_glue_method(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, interpSave.method), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+
+//!generate native code to get DvmDex from glue
+
+//!It uses one scratch register
+int get_glue_dvmdex(int reg, bool isPhysical) {
+    //if spill_loc_index > 0 || reg != NULL, use registerAlloc
+    if(isGlueHandled(PhysicalReg_GLUE_DVMDEX)) {
+        //if spill_loc_index > 0
+        //  load from spilled location, updte spill_loc_index & physicalReg
+        startNativeCode(-1, -1);
+        freeReg(true);
+        int regAll = registerAlloc(LowOpndRegType_gp, PhysicalReg_GLUE_DVMDEX, false, false/*updateRefCount*/);
+        donotSpillReg(regAll);
+        dump_reg_reg_noalloc_src(Mnemonic_MOV, ATOM_NORMAL, OpndSize_32, regAll, true,
+                                          reg, isPhysical, LowOpndRegType_gp);
+        endNativeCode();
+    }
+    else
+        {
+            get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+            move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+            //glue is not in a physical reg nor in a spilled location
+            updateGlue(reg, isPhysical, PhysicalReg_GLUE_DVMDEX); //spill_loc_index is -1, set physicalReg
+        }
+    return 0;
+}
+//!generate native code to set DvmDex in glue
+
+//!It uses one scratch register
+int set_glue_dvmdex(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, interpSave.methodClassDex), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//!generate native code to get SuspendCount from glue
+
+//!It uses one scratch register
+int get_suspendCount(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, suspendCount), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+
+//!generate native code to get retval from glue
+
+//!It uses one scratch register
+int get_return_value(OpndSize size, int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(size, offsetof(Thread, interpSave.retval), C_SCRATCH_1, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set retval in glue
+
+//!It uses one scratch register
+int set_return_value(OpndSize size, int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(size, reg, isPhysical, offsetof(Thread, interpSave.retval), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//!generate native code to clear exception object in glue
+
+//!It uses two scratch registers
+int clear_exception() {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical);
+    return 0;
+}
+//!generate native code to get exception object in glue
+
+//!It uses two scratch registers
+int get_exception(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical, reg, isPhysical);
+    return 0;
+}
+//!generate native code to set exception object in glue
+
+//!It uses two scratch registers
+int set_exception(int reg, bool isPhysical) {
+    get_self_pointer(C_SCRATCH_2, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, offsetof(Thread, exception), C_SCRATCH_2, isScratchPhysical);
+    return 0;
+}
+//!generate native code to save frame pointer and current PC in stack frame to glue
+
+//!It uses two scratch registers
+int save_pc_fp_to_glue() {
+    get_self_pointer(C_SCRATCH_1, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), C_SCRATCH_1, isScratchPhysical);
+
+    //from stack-save currentPc
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true, C_SCRATCH_2, isScratchPhysical);
+    move_reg_to_mem(OpndSize_32, C_SCRATCH_2, isScratchPhysical, offsetof(Thread, interpSave.pc), C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+//! get SaveArea pointer
+
+//!
+int savearea_from_fp(int reg, bool isPhysical) {
+    load_effective_addr(-sizeofStackSaveArea, PhysicalReg_FP, true, reg, isPhysical);
+    return 0;
+}
+
+#ifdef DEBUG_CALL_STACK3
+int call_debug_dumpSwitch() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = debug_dumpSwitch;
+    callFuncPtr((int)funcPtr, "debug_dumpSwitch");
+    return 0;
+}
+#endif
+
+int call_dvmQuasiAtomicSwap64() {
+    typedef int64_t (*vmHelper)(int64_t, volatile int64_t*);
+    vmHelper funcPtr = dvmQuasiAtomicSwap64;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmQuasiAtomicSwap64");
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicSwap64");
+        afterCall("dvmQuasiAtomicSwap64");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicSwap64");
+    }
+    return 0;
+}
+
+int call_dvmQuasiAtomicRead64() {
+    typedef int64_t (*vmHelper)(volatile const int64_t*);
+    vmHelper funcPtr = dvmQuasiAtomicRead64;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmQuasiAtomiRead64");
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicRead64");
+        afterCall("dvmQuasiAtomicRead64");
+        touchEax(); //for return value
+        touchEdx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmQuasiAtomicRead64");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpPunt() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpPunt;
+    callFuncPtr((int)funcPtr, "dvmJitToInterpPunt");
+    return 0;
+}
+
+int call_dvmJitToInterpNormal() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNormal;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpNormal");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpNormal");
+        afterCall("dvmJitToInterpNormal");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpNormal");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpTraceSelectNoChain() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpTraceSelectNoChain;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpTraceSelectNoChain");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelectNoChain");
+        afterCall("dvmJitToInterpTraceSelectNoChain");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelectNoChain");
+    }
+    return 0;
+}
+
+int call_dvmJitToInterpTraceSelect() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpTraceSelect;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToInterpTraceSelect");
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelect");
+        afterCall("dvmJitToInterpTraceSelect");
+        touchEbx();
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToInterpTraceSelect");
+    }
+    return 0;
+}
+
+int call_dvmJitToPatchPredictedChain() {
+    typedef const Method * (*vmHelper)(const Method *method,
+                                       Thread *self,
+                                       PredictedChainingCell *cell,
+                                       const ClassObject *clazz);
+    vmHelper funcPtr = dvmJitToPatchPredictedChain;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitToPatchPredictedChain");
+        callFuncPtr((int)funcPtr, "dvmJitToPatchPredictedChain");
+        afterCall("dvmJitToPatchPredictedChain");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitToPatchPredictedChain");
+    }
+    return 0;
+}
+
+//!generate native code to call __moddi3
+
+//!
+int call_moddi3() {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("moddi3");
+        callFuncPtr((intptr_t)__moddi3, "__moddi3");
+        afterCall("moddi3");
+    } else {
+        callFuncPtr((intptr_t)__moddi3, "__moddi3");
+    }
+    return 0;
+}
+//!generate native code to call __divdi3
+
+//!
+int call_divdi3() {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("divdi3");
+        callFuncPtr((intptr_t)__divdi3, "__divdi3");
+        afterCall("divdi3");
+    } else {
+        callFuncPtr((intptr_t)__divdi3, "__divdi3");
+    }
+    return 0;
+}
+
+//!generate native code to call fmod
+
+//!
+int call_fmod() {
+    typedef double (*libHelper)(double, double);
+    libHelper funcPtr = fmod;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("fmod");
+        callFuncPtr((int)funcPtr, "fmod");
+        afterCall("fmod");
+    } else {
+        callFuncPtr((int)funcPtr, "fmod");
+    }
+    return 0;
+}
+//!generate native code to call fmodf
+
+//!
+int call_fmodf() {
+    typedef float (*libHelper)(float, float);
+    libHelper funcPtr = fmodf;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("fmodf");
+        callFuncPtr((int)funcPtr, "fmodf");
+        afterCall("fmodf");
+    } else {
+        callFuncPtr((int)funcPtr, "fmodf");
+    }
+    return 0;
+}
+//!generate native code to call dvmFindCatchBlock
+
+//!
+int call_dvmFindCatchBlock() {
+    //int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    //bool doUnroll, void** newFrame)
+    typedef int (*vmHelper)(Thread*, int, Object*, bool, void**);
+    vmHelper funcPtr = dvmFindCatchBlock;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmFindCatchBlock");
+        callFuncPtr((int)funcPtr, "dvmFindCatchBlock");
+        afterCall("dvmFindCatchBlock");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmFindCatchBlock");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowVerificationError
+
+//!
+int call_dvmThrowVerificationError() {
+    typedef void (*vmHelper)(const Method*, int, int);
+    vmHelper funcPtr = dvmThrowVerificationError;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowVerificationError");
+        callFuncPtr((int)funcPtr, "dvmThrowVerificationError");
+        afterCall("dvmThrowVerificationError");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowVerificationError");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmResolveMethod
+
+//!
+int call_dvmResolveMethod() {
+    //Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx, MethodType methodType);
+    typedef Method* (*vmHelper)(const ClassObject*, u4, MethodType);
+    vmHelper funcPtr = dvmResolveMethod;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveMethod");
+        callFuncPtr((int)funcPtr, "dvmResolveMethod");
+        afterCall("dvmResolveMethod");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveMethod");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveClass
+
+//!
+int call_dvmResolveClass() {
+    //ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx, bool fromUnverifiedConstant)
+    typedef ClassObject* (*vmHelper)(const ClassObject*, u4, bool);
+    vmHelper funcPtr = dvmResolveClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveClass");
+        callFuncPtr((int)funcPtr, "dvmResolveClass");
+        afterCall("dvmResolveClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveClass");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmInstanceofNonTrivial
+
+//!
+int call_dvmInstanceofNonTrivial() {
+    typedef int (*vmHelper)(const ClassObject*, const ClassObject*);
+    vmHelper funcPtr = dvmInstanceofNonTrivial;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInstanceofNonTrivial");
+        callFuncPtr((int)funcPtr, "dvmInstanceofNonTrivial");
+        afterCall("dvmInstanceofNonTrivial");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInstanceofNonTrivial");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowException
+
+//!
+int call_dvmThrow() {
+    typedef void (*vmHelper)(ClassObject* exceptionClass, const char*);
+    vmHelper funcPtr = dvmThrowException;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowException");
+        callFuncPtr((int)funcPtr, "dvmThrowException");
+        afterCall("dvmThrowException");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowException");
+    }
+    return 0;
+}
+//!generate native code to call dvmThrowExceptionWithClassMessage
+
+//!
+int call_dvmThrowWithMessage() {
+    typedef void (*vmHelper)(ClassObject* exceptionClass, const char*);
+    vmHelper funcPtr = dvmThrowExceptionWithClassMessage;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmThrowExceptionWithClassMessage");
+        callFuncPtr((int)funcPtr, "dvmThrowExceptionWithClassMessage");
+        afterCall("dvmThrowExceptionWithClassMessage");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmThrowExceptionWithClassMessage");
+    }
+    return 0;
+}
+//!generate native code to call dvmCheckSuspendPending
+
+//!
+int call_dvmCheckSuspendPending() {
+    typedef bool (*vmHelper)(Thread*);
+    vmHelper funcPtr = dvmCheckSuspendPending;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmCheckSuspendPending");
+        callFuncPtr((int)funcPtr, "dvmCheckSuspendPending");
+        afterCall("dvmCheckSuspendPending");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmCheckSuspendPending");
+    }
+    return 0;
+}
+//!generate native code to call dvmLockObject
+
+//!
+int call_dvmLockObject() {
+    typedef void (*vmHelper)(struct Thread*, struct Object*);
+    vmHelper funcPtr = dvmLockObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmLockObject");
+        callFuncPtr((int)funcPtr, "dvmLockObject");
+        afterCall("dvmLockObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmLockObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmUnlockObject
+
+//!
+int call_dvmUnlockObject() {
+    typedef bool (*vmHelper)(Thread*, Object*);
+    vmHelper funcPtr = dvmUnlockObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmUnlockObject");
+        callFuncPtr((int)funcPtr, "dvmUnlockObject");
+        afterCall("dvmUnlockObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmUnlockObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmInitClass
+
+//!
+int call_dvmInitClass() {
+    typedef bool (*vmHelper)(ClassObject*);
+    vmHelper funcPtr = dvmInitClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInitClass");
+        callFuncPtr((int)funcPtr, "dvmInitClass");
+        afterCall("dvmInitClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInitClass");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocObject
+
+//!
+int call_dvmAllocObject() {
+    typedef Object* (*vmHelper)(ClassObject*, int);
+    vmHelper funcPtr = dvmAllocObject;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocObject");
+        callFuncPtr((int)funcPtr, "dvmAllocObject");
+        afterCall("dvmAllocObject");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocObject");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocArrayByClass
+
+//!
+int call_dvmAllocArrayByClass() {
+    typedef ArrayObject* (*vmHelper)(ClassObject*, size_t, int);
+    vmHelper funcPtr = dvmAllocArrayByClass;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocArrayByClass");
+        callFuncPtr((int)funcPtr, "dvmAllocArrayByClass");
+        afterCall("dvmAllocArrayByClass");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocArrayByClass");
+    }
+    return 0;
+}
+//!generate native code to call dvmAllocPrimitiveArray
+
+//!
+int call_dvmAllocPrimitiveArray() {
+    typedef ArrayObject* (*vmHelper)(char, size_t, int);
+    vmHelper funcPtr = dvmAllocPrimitiveArray;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmAllocPrimitiveArray");
+        callFuncPtr((int)funcPtr, "dvmAllocPrimitiveArray");
+        afterCall("dvmAllocPrimitiveArray");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmAllocPrimitiveArray");
+    }
+    return 0;
+}
+//!generate native code to call dvmInterpHandleFillArrayData
+
+//!
+int call_dvmInterpHandleFillArrayData() {
+    typedef bool (*vmHelper)(ArrayObject*, const u2*);
+    vmHelper funcPtr = dvmInterpHandleFillArrayData;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmInterpHandleFillArrayData"); //before move_imm_to_reg to avoid spilling C_SCRATCH_1
+        callFuncPtr((int)funcPtr, "dvmInterpHandleFillArrayData");
+        afterCall("dvmInterpHandleFillArrayData");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmInterpHandleFillArrayData");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmNcgHandlePackedSwitch
+
+//!
+int call_dvmNcgHandlePackedSwitch() {
+    typedef s4 (*vmHelper)(const s4*, s4, u2, s4);
+    vmHelper funcPtr = dvmNcgHandlePackedSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmNcgHandlePackedSwitch");
+        callFuncPtr((int)funcPtr, "dvmNcgHandlePackedSwitch");
+        afterCall("dvmNcgHandlePackedSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmNcgHandlePackedSwitch");
+    }
+    return 0;
+}
+
+int call_dvmJitHandlePackedSwitch() {
+    typedef s4 (*vmHelper)(const s4*, s4, u2, s4);
+    vmHelper funcPtr = dvmJitHandlePackedSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitHandlePackedSwitch");
+        callFuncPtr((int)funcPtr, "dvmJitHandlePackedSwitch");
+        afterCall("dvmJitHandlePackedSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitHandlePackedSwitch");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmNcgHandleSparseSwitch
+
+//!
+int call_dvmNcgHandleSparseSwitch() {
+    typedef s4 (*vmHelper)(const s4*, u2, s4);
+    vmHelper funcPtr = dvmNcgHandleSparseSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmNcgHandleSparseSwitch");
+        callFuncPtr((int)funcPtr, "dvmNcgHandleSparseSwitch");
+        afterCall("dvmNcgHandleSparseSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmNcgHandleSparseSwitch");
+    }
+    return 0;
+}
+
+int call_dvmJitHandleSparseSwitch() {
+    typedef s4 (*vmHelper)(const s4*, u2, s4);
+    vmHelper funcPtr = dvmJitHandleSparseSwitch;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmJitHandleSparseSwitch");
+        callFuncPtr((int)funcPtr, "dvmJitHandleSparseSwitch");
+        afterCall("dvmJitHandleSparseSwitch");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmJitHandleSparseSwitch");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmCanPutArrayElement
+
+//!
+int call_dvmCanPutArrayElement() {
+    typedef bool (*vmHelper)(const ClassObject*, const ClassObject*);
+    vmHelper funcPtr = dvmCanPutArrayElement;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmCanPutArrayElement");
+        callFuncPtr((int)funcPtr, "dvmCanPutArrayElement");
+        afterCall("dvmCanPutArrayElement");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmCanPutArrayElement");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmFindInterfaceMethodInCache
+
+//!
+int call_dvmFindInterfaceMethodInCache() {
+    typedef Method* (*vmHelper)(ClassObject*, u4, const Method*, DvmDex*);
+    vmHelper funcPtr = dvmFindInterfaceMethodInCache;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmFindInterfaceMethodInCache");
+        callFuncPtr((int)funcPtr, "dvmFindInterfaceMethodInCache");
+        afterCall("dvmFindInterfaceMethodInCache");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmFindInterfaceMethodInCache");
+    }
+    return 0;
+}
+
+//!generate native code to call dvmHandleStackOverflow
+
+//!
+int call_dvmHandleStackOverflow() {
+    typedef void (*vmHelper)(Thread*, const Method*);
+    vmHelper funcPtr = dvmHandleStackOverflow;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmHandleStackOverflow");
+        callFuncPtr((int)funcPtr, "dvmHandleStackOverflow");
+        afterCall("dvmHandleStackOverflow");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmHandleStackOverflow");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveString
+
+//!
+int call_dvmResolveString() {
+    //StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
+    typedef StringObject* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveString;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveString");
+        callFuncPtr((int)funcPtr, "dvmResolveString");
+        afterCall("dvmResolveString");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveString");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveInstField
+
+//!
+int call_dvmResolveInstField() {
+    //InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
+    typedef InstField* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveInstField;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveInstField");
+        callFuncPtr((int)funcPtr, "dvmResolveInstField");
+        afterCall("dvmResolveInstField");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveInstField");
+    }
+    return 0;
+}
+//!generate native code to call dvmResolveStaticField
+
+//!
+int call_dvmResolveStaticField() {
+    //StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
+    typedef StaticField* (*vmHelper)(const ClassObject*, u4);
+    vmHelper funcPtr = dvmResolveStaticField;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall("dvmResolveStaticField");
+        callFuncPtr((int)funcPtr, "dvmResolveStaticField");
+        afterCall("dvmResolveStaticField");
+    } else {
+        callFuncPtr((int)funcPtr, "dvmResolveStaticField");
+    }
+    return 0;
+}
+
+#define P_GPR_2 PhysicalReg_ECX
+/*!
+\brief This function is used to resolve a string reference
+
+INPUT: const pool index in %eax
+
+OUTPUT: resolved string in %eax
+
+The registers are hard-coded, 2 physical registers %esi and %edx are used as scratch registers;
+It calls a C function dvmResolveString;
+The only register that is still live after this function is ebx
+*/
+int const_string_resolve() {
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    insertLabel(".const_string_resolve", false);
+    //method stored in glue structure as well as on the interpreted stack
+    get_glue_method_class(P_GPR_2, true);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_2, true, 0, PhysicalReg_ESP, true);
+    call_dvmResolveString();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg( OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+    x86_return();
+    return 0;
+}
+#undef P_GPR_2
+/*!
+\brief This function is used to resolve a class
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved class in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveClass;
+The only register that is still live after this function is ebx
+*/
+int resolve_class2(
+           int startLR/*scratch register*/, bool isPhysical, int indexReg/*const pool index*/,
+           bool indexPhysical, int thirdArg) {
+    insertLabel(".class_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    //push index to stack first, to free indexReg
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    get_glue_method_class(startLR, isPhysical);
+    move_imm_to_mem(OpndSize_32, thirdArg, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve a method, and it is called once with %eax for both indexReg and startLR
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved method in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveMethod;
+The only register that is still live after this function is ebx
+*/
+int resolve_method2(
+            int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+            bool indexPhysical,
+            int thirdArg/*VIRTUAL*/) {
+    if(thirdArg == METHOD_VIRTUAL)
+        insertLabel(".virtual_method_resolve", false);
+    else if(thirdArg == METHOD_DIRECT)
+        insertLabel(".direct_method_resolve", false);
+    else if(thirdArg == METHOD_STATIC)
+        insertLabel(".static_method_resolve", false);
+
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_glue_method_class(startLR, isPhysical);
+
+    move_imm_to_mem(OpndSize_32, thirdArg, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveMethod();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve an instance field
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved field in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveInstField;
+The only register that is still live after this function is ebx
+*/
+int resolve_inst_field2(
+            int startLR/*logical register index*/, bool isPhysical,
+            int indexReg/*const pool index*/, bool indexPhysical) {
+    insertLabel(".inst_field_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    //method stored in glue structure as well as interpreted stack
+    get_glue_method_class(startLR, isPhysical);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveInstField();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+/*!
+\brief This function is used to resolve a static field
+
+INPUT: const pool index in argument "indexReg" (%eax)
+
+OUTPUT: resolved field in %eax
+
+The registers are hard-coded, 3 physical registers (%esi, %edx, startLR:%eax) are used as scratch registers.
+It calls a C function dvmResolveStaticField;
+The only register that is still live after this function is ebx
+*/
+int resolve_static_field2(
+              int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/,
+              bool indexPhysical) {
+    insertLabel(".static_field_resolve", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, indexReg, indexPhysical, 4, PhysicalReg_ESP, true);
+    get_glue_method_class(startLR, isPhysical);
+    move_reg_to_mem(OpndSize_32, startLR, isPhysical, 0, PhysicalReg_ESP, true);
+    call_dvmResolveStaticField();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+
+    x86_return();
+    return 0;
+}
+
+int pushAllRegs() {
+    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EAX, true, 24, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBX, true, 20, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_ECX, true, 16, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EDX, true, 12, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_ESI, true, 8, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EDI, true, 4, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    move_reg_to_mem_noalloc(OpndSize_32, PhysicalReg_EBP, true, 0, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1);
+    return 0;
+}
+int popAllRegs() {
+    move_mem_to_reg_noalloc(OpndSize_32, 24, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EAX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 20, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EBX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 16, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_ECX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 12, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EDX, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 8, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_ESI, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 4, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EDI, true);
+    move_mem_to_reg_noalloc(OpndSize_32, 0, PhysicalReg_ESP, true, MemoryAccess_Unknown, -1, PhysicalReg_EBP, true);
+    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    return 0;
+}
+
+void dump_nop(int size) {
+    switch(size) {
+        case 1:
+          *stream = 0x90;
+          break;
+        case 2:
+          *stream = 0x66;
+          *(stream +1) = 0x90;
+          break;
+        case 3:
+          *stream = 0x0f;
+          *(stream + 1) = 0x1f;
+          *(stream + 2) = 0x00;
+          break;
+        default:
+          //TODO: add more cases.
+          break;
+    }
+    stream += size;
+}
diff --git a/vm/compiler/codegen/x86/LowerInvoke.cpp b/vm/compiler/codegen/x86/LowerInvoke.cpp
new file mode 100644
index 0000000..10bc197
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerInvoke.cpp
@@ -0,0 +1,1672 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerInvoke.cpp
+    \brief This file lowers the following bytecodes: INVOKE_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "mterp/Mterp.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+char* streamMisPred = NULL;
+
+/* according to callee, decide the ArgsDoneType*/
+ArgsDoneType convertCalleeToType(const Method* calleeMethod) {
+    if(calleeMethod == NULL)
+        return ArgsDone_Full;
+    if(dvmIsNativeMethod(calleeMethod))
+        return ArgsDone_Native;
+    return ArgsDone_Normal;
+}
+int common_invokeMethodRange(ArgsDoneType);
+int common_invokeMethodNoRange(ArgsDoneType);
+void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg);
+
+//inputs to common_invokeMethodRange: %ecx
+//          common_errNoSuchMethod: %edx
+#define P_GPR_1 PhysicalReg_ESI
+#define P_GPR_2 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+
+#ifdef WITH_JIT_INLINING
+/*
+ * The function here takes care the
+ * branch over if prediction is correct and the misprediction target for misPredBranchOver.
+ */
+static void genLandingPadForMispredictedCallee(MIR* mir) {
+    BasicBlock *fallThrough = traceCurrentBB->fallThrough;
+    /* Bypass the move-result block if there is one */
+    if (fallThrough->firstMIRInsn) {
+        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+        fallThrough = fallThrough->fallThrough;
+    }
+    /* Generate a branch over if the predicted inlining is correct */
+    jumpToBasicBlock(stream, fallThrough->id);
+    /* Hook up the target to the verification branch */
+    int relativeNCG = stream - streamMisPred;
+    unsigned instSize = encoder_get_inst_size(streamMisPred);
+    relativeNCG -= instSize; //size of the instruction
+    updateJumpInst(streamMisPred, OpndSize_8, relativeNCG);
+}
+#endif
+
+//! LOWER bytecode INVOKE_VIRTUAL without usage of helper function
+
+//!
+int common_invoke_virtual_nohelper(bool isRange, u2 tmp, u2 vD) {
+#ifdef WITH_JIT_INLINING
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+
+    get_virtual_reg(vD, OpndSize_32, 5, false);
+    simpleNullCheck(5, false, vD);
+#ifndef PREDICTED_CHAINING
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false); //clazz of "this"
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 6, false, 7, false); //vtable
+    /* method is already resolved in trace-based JIT */
+    int methodIndex =
+                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    move_mem_to_reg(OpndSize_32, methodIndex*4, 7, false, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    int methodIndex =
+                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    gen_predicted_chain(isRange, tmp, methodIndex*4, false, 5/*tmp5*/);
+#endif
+    ///////////////////////////////////
+    return 0;
+}
+//! wrapper to call either common_invoke_virtual_helper or common_invoke_virtual_nohelper
+
+//!
+int common_invoke_virtual(bool isRange, u2 tmp, u2 vD) {
+    return common_invoke_virtual_nohelper(isRange, tmp, vD);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1 PhysicalReg_ESI
+#define P_GPR_2 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_SUPER
+
+//! It will use helper function if the switch is on
+int common_invoke_super(bool isRange, u2 tmp) {
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ///////////////////////
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    /* method is already resolved in trace-based JIT */
+    int mIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
+    const Method *calleeMethod =
+        currentMethod->clazz->super->vtable[mIndex];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ///////////////////////////////
+    return 0;
+}
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+//! helper function to handle no such method error
+
+//!
+int invoke_super_nsm() {
+    insertLabel(".invoke_super_nsm", false);
+    //NOTE: it seems that the name in %edx is not used in common_errNoSuchMethod
+    move_mem_to_reg(OpndSize_32, offMethod_name, PhysicalReg_EAX, true, PhysicalReg_EDX, true); //method name
+    unconditional_jump("common_errNoSuchMethod", false);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_GPR_3 PhysicalReg_ECX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_DIRECT
+
+//! It will use helper function if the switch is on
+int common_invoke_direct(bool isRange, u2 tmp, u2 vD) {
+    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ////////////////////////////////////
+    get_virtual_reg(vD, OpndSize_32, 5, false);
+    simpleNullCheck(5, false, vD);
+    /* method is already resolved in trace-based JIT */
+    const Method *calleeMethod =
+        currentMethod->clazz->pDvmDex->pResMethods[tmp];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    //%ecx passed to common_invokeMethod...
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ////////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1  PhysicalReg_EBX
+#define P_GPR_3  PhysicalReg_ECX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_STATIC
+
+//! It will use helper function if the switch is on
+int common_invoke_static(bool isRange, u2 tmp) {
+    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ////////////////////////////
+    /* method is already resolved in trace-based JIT */
+    const Method *calleeMethod =
+        currentMethod->clazz->pDvmDex->pResMethods[tmp];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    //%ecx passed to common_invokeMethod...
+    if(isRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    ////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_EAX //scratch
+#define P_GPR_3 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_ESI //clazz of object
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common section to lower INVOKE_INTERFACE
+
+//! It will use helper function if the switch is on
+int common_invoke_interface(bool isRange, u2 tmp, u2 vD) {
+#ifdef WITH_JIT_INLINING
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    export_pc(); //use %edx
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    ///////////////////////
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_virtual_reg(vD, OpndSize_32, 1, false);
+    simpleNullCheck(1, false, vD);
+
+#ifndef PREDICTED_CHAINING
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    /* for trace-based JIT, pDvmDex is a constant at JIT time
+       4th argument to dvmFindInterfaceMethodInCache at -4(%esp) */
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 5, false);
+    /* for trace-based JIT, method is a constant at JIT time
+       3rd argument to dvmFindInterfaceMethodInCache at 8(%esp) */
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+
+    conditional_jump_global_API(Condition_E, "common_exceptionThrown", false);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    gen_predicted_chain(isRange, tmp, -1, true /*interface*/, 1/*tmp1*/);
+#endif
+    ///////////////////////
+    return 0;
+}
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+//! lower bytecode INVOKE_VIRTUAL by calling common_invoke_virtual
+
+//!
+int op_invoke_virtual() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_virtual(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER by calling common_invoke_super
+
+//!
+int op_invoke_super() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_super(false/*not range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_DIRECT by calling common_invoke_direct
+
+//!
+int op_invoke_direct() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_direct(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_STATIC by calling common_invoke_static
+
+//!
+int op_invoke_static() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_static(false/*not range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_INTERFACE by calling common_invoke_interface
+
+//!
+int op_invoke_interface() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //B|A|op CCCC G|F|E|D
+    //D: the first argument, which is the "this" pointer
+    //B: argument count
+    //D,E,F,G,A: arguments
+    u2 vD = FETCH(2) & 0xf;
+    u2 tmp = FETCH(1); //method index
+    int retval = common_invoke_interface(false/*not range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_VIRTUAL_RANGE by calling common_invoke_virtual
+
+//!
+int op_invoke_virtual_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    //AA|op BBBB CCCC
+    //CCCC: the first argument, which is the "this" pointer
+    //AA: argument count
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_virtual(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER_RANGE by calling common_invoke_super
+
+//!
+int op_invoke_super_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    int retval = common_invoke_super(true/*range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_DIRECT_RANGE by calling common_invoke_direct
+
+//!
+int op_invoke_direct_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_direct(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_STATIC_RANGE by calling common_invoke_static
+
+//!
+int op_invoke_static_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    int retval = common_invoke_static(true/*range*/, tmp);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_INTERFACE_RANGE by calling common_invoke_interface
+
+//!
+int op_invoke_interface_range() {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+#endif
+    u2 tmp = FETCH(1); //BBBB, method index
+    u2 vD = FETCH(2); //the first argument
+    int retval = common_invoke_interface(true/*range*/, tmp, vD);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+
+//used %ecx, %edi, %esp %ebp
+#define P_GPR_1 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EAX
+#define P_SCRATCH_3 PhysicalReg_EDX
+#define P_SCRATCH_4 PhysicalReg_ESI
+#define P_SCRATCH_5 PhysicalReg_EAX
+//! pass the arguments for invoking method without range
+
+//!
+int common_invokeMethodNoRange_noJmp() {
+    u2 count = INST_B(inst);
+    u2 vD = FETCH(2) & 0xf;
+    u2 vE = (FETCH(2) >> 4) & 0xf;
+    u2 vF = (FETCH(2) >> 8) & 0xf;
+    u2 vG = (FETCH(2) >> 12) & 0xf;
+    u2 vA = INST_A(inst); //5th argument
+    int offsetFromSaveArea = -4;
+    if(count == 5) {
+        get_virtual_reg(vA, OpndSize_32, 22, false);
+        move_reg_to_mem(OpndSize_32, 22, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 4) {
+        get_virtual_reg(vG, OpndSize_32, 23, false);
+        move_reg_to_mem(OpndSize_32, 23, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 3) {
+        get_virtual_reg(vF, OpndSize_32, 24, false);
+        move_reg_to_mem(OpndSize_32, 24, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 2) {
+        get_virtual_reg(vE, OpndSize_32, 25, false);
+        move_reg_to_mem(OpndSize_32, 25, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+        offsetFromSaveArea -= 4;
+    }
+    if(count >= 1) {
+        get_virtual_reg(vD, OpndSize_32, 26, false);
+        move_reg_to_mem(OpndSize_32, 26, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
+    }
+    return 0;
+}
+
+int common_invokeMethod_Jmp(ArgsDoneType form) {
+    nextVersionOfHardReg(PhysicalReg_EDX, 1);
+    move_imm_to_reg(OpndSize_32, (int)rPC, PhysicalReg_EDX, true);
+    //arguments needed in ArgsDone:
+    //    start of HotChainingCell for next bytecode: -4(%esp)
+    //    start of InvokeSingletonChainingCell for callee: -8(%esp)
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->fallThrough->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->fallThrough->id, 4, PhysicalReg_ESP, true);
+    // for honeycomb: JNI call doesn't need a chaining cell, so the taken branch is null
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int takenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_mem(OpndSize_32, takenId, 0, PhysicalReg_ESP, true);
+    if(form == ArgsDone_Full)
+        unconditional_jump_global_API(".invokeArgsDone_jit", false);
+    else if(form == ArgsDone_Native)
+        unconditional_jump_global_API(".invokeArgsDone_native", false);
+    else
+        unconditional_jump_global_API(".invokeArgsDone_normal", false);
+    return 0;
+}
+
+int common_invokeMethodNoRange(ArgsDoneType form) {
+    common_invokeMethodNoRange_noJmp();
+    common_invokeMethod_Jmp(form);
+    return 0;
+}
+
+#undef P_GPR_1
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+
+//input: %ecx (method to call)
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_GPR_3 PhysicalReg_EDX //not used with P_SCRATCH_2
+#define P_SCRATCH_1 PhysicalReg_EAX
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_EAX
+#define P_SCRATCH_4 PhysicalReg_EDX
+#define P_SCRATCH_5 PhysicalReg_EAX
+#define P_SCRATCH_6 PhysicalReg_EDX
+#define P_SCRATCH_7 PhysicalReg_EAX
+#define P_SCRATCH_8 PhysicalReg_EDX
+#define P_SCRATCH_9 PhysicalReg_EAX
+#define P_SCRATCH_10 PhysicalReg_EDX
+//! pass the arguments for invoking method with range
+
+//! loop is unrolled when count <= 10
+int common_invokeMethodRange_noJmp() {
+    u2 count = INST_AA(inst);
+    u2 vD = FETCH(2); //the first argument
+    savearea_from_fp(21, false);
+    //vD to rFP-4*count-20
+    //vD+1 to rFP-4*count-20+4 = rFP-20-4*(count-1)
+    if(count >= 1 && count <= 10) {
+        get_virtual_reg(vD, OpndSize_32, 22, false);
+        move_reg_to_mem(OpndSize_32, 22, false, -4*count, 21, false);
+    }
+    if(count >= 2 && count <= 10) {
+        get_virtual_reg(vD+1, OpndSize_32, 23, false);
+        move_reg_to_mem(OpndSize_32, 23, false, -4*(count-1), 21, false);
+    }
+    if(count >= 3 && count <= 10) {
+        get_virtual_reg(vD+2, OpndSize_32, 24, false);
+        move_reg_to_mem(OpndSize_32, 24, false, -4*(count-2), 21, false);
+    }
+    if(count >= 4 && count <= 10) {
+        get_virtual_reg(vD+3, OpndSize_32, 25, false);
+        move_reg_to_mem(OpndSize_32, 25, false, -4*(count-3), 21, false);
+    }
+    if(count >= 5 && count <= 10) {
+        get_virtual_reg(vD+4, OpndSize_32, 26, false);
+        move_reg_to_mem(OpndSize_32, 26, false, -4*(count-4), 21, false);
+    }
+    if(count >= 6 && count <= 10) {
+        get_virtual_reg(vD+5, OpndSize_32, 27, false);
+        move_reg_to_mem(OpndSize_32, 27, false, -4*(count-5), 21, false);
+    }
+    if(count >= 7 && count <= 10) {
+        get_virtual_reg(vD+6, OpndSize_32, 28, false);
+        move_reg_to_mem(OpndSize_32, 28, false, -4*(count-6), 21, false);
+    }
+    if(count >= 8 && count <= 10) {
+        get_virtual_reg(vD+7, OpndSize_32, 29, false);
+        move_reg_to_mem(OpndSize_32, 29, false, -4*(count-7), 21, false);
+    }
+    if(count >= 9 && count <= 10) {
+        get_virtual_reg(vD+8, OpndSize_32, 30, false);
+        move_reg_to_mem(OpndSize_32, 30, false, -4*(count-8), 21, false);
+    }
+    if(count == 10) {
+        get_virtual_reg(vD+9, OpndSize_32, 31, false);
+        move_reg_to_mem(OpndSize_32, 31, false, -4*(count-9), 21, false);
+    }
+    if(count > 10) {
+        //dump to memory first, should we set physicalReg to Null?
+        //this bytecodes uses a set of virtual registers (update getVirtualInfo)
+        //this is necessary to correctly insert transfer points
+        int k;
+        for(k = 0; k < count; k++) {
+            spillVirtualReg(vD+k, LowOpndRegType_gp, true); //will update refCount
+        }
+        load_effective_addr(4*vD, PhysicalReg_FP, true, 12, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 4*count, 21, false);
+        move_imm_to_reg(OpndSize_32, count, 13, false);
+        insertLabel(".invokeMethod_1", true); //if checkDup: will perform work from ShortWorklist
+        rememberState(1);
+        move_mem_to_reg(OpndSize_32, 0, 12, false, 14, false);
+        move_reg_to_mem(OpndSize_32, 14, false, 0, 21, false);
+        load_effective_addr(4, 12, false, 12, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 13, false);
+        load_effective_addr(4, 21, false, 21, false);
+        transferToState(1);
+        conditional_jump(Condition_NE, ".invokeMethod_1", true); //backward branch
+    }
+    return 0;
+}
+
+int common_invokeMethodRange(ArgsDoneType form) {
+    common_invokeMethodRange_noJmp();
+    common_invokeMethod_Jmp(form);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+#undef P_SCRATCH_6
+#undef P_SCRATCH_7
+#undef P_SCRATCH_8
+#undef P_SCRATCH_9
+#undef P_SCRATCH_10
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EAX
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_EAX
+#define P_SCRATCH_4 PhysicalReg_EDX
+#define P_SCRATCH_5 PhysicalReg_EAX
+#define P_SCRATCH_6 PhysicalReg_EDX
+
+//! spill a register to native stack
+
+//! decrease %esp by 4, then store a register at 0(%esp)
+int spill_reg(int reg, bool isPhysical) {
+    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, reg, isPhysical, 0, PhysicalReg_ESP, true);
+    return 0;
+}
+//! get a register from native stack
+
+//! load a register from 0(%esp), then increase %esp by 4
+int unspill_reg(int reg, bool isPhysical) {
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, reg, isPhysical);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    return 0;
+}
+
+void generate_invokeNative(bool generateForNcg); //forward declaration
+void generate_stackOverflow(); //forward declaration
+
+//! common code to invoke a method after all arguments are handled
+
+//!
+//takes one argument to generate code
+//  for invokeNativeSingle (form == ArgsDone_Native)
+//   or invokeNonNativeSingle (form == ArgsDone_Normal) when WITH_JIT is true
+//   to dynamically determine which one to choose (form == ArgsDone_Full)
+/* common_invokeArgsDone is called at NCG time and
+     at execution time during relocation
+   generate invokeArgsDone for NCG if isJitFull is false && form == Full */
+int common_invokeArgsDone(ArgsDoneType form, bool isJitFull) {
+    bool generateForNcg = false;
+    if(form == ArgsDone_Full) {
+        if(isJitFull)
+            insertLabel(".invokeArgsDone_jit", false);
+        else {
+            insertLabel(".invokeArgsDone", false);
+            generateForNcg = true;
+        }
+    }
+    else if(form == ArgsDone_Normal)
+        insertLabel(".invokeArgsDone_normal", false);
+    else if(form == ArgsDone_Native)
+        insertLabel(".invokeArgsDone_native", false);
+    //%ecx: methodToCall
+    movez_mem_to_reg(OpndSize_16, offMethod_registersSize, PhysicalReg_ECX, true, P_SCRATCH_1, true); //regSize
+    scratchRegs[0] = PhysicalReg_EBX; scratchRegs[1] = PhysicalReg_ESI;
+    scratchRegs[2] = PhysicalReg_EDX; scratchRegs[3] = PhysicalReg_Null;
+    savearea_from_fp(P_GPR_3, true);
+    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_1, true);
+    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_1, true, P_GPR_3, true);
+    //update newSaveArea->savedPc, here P_GPR_3 is new FP
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true);
+    movez_mem_to_reg(OpndSize_16, offMethod_outsSize, PhysicalReg_ECX, true, P_SCRATCH_2, true); //outsSize
+    move_reg_to_reg(OpndSize_32, P_GPR_3, true, P_GPR_1, true); //new FP
+    alu_binary_imm_reg(OpndSize_32, sub_opc, sizeofStackSaveArea, P_GPR_3, true);
+
+    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_2, true);
+    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_2, true, P_GPR_3, true);
+    get_self_pointer(P_SCRATCH_3, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offStackSaveArea_prevFrame-sizeofStackSaveArea, P_GPR_1, true); //set stack->prevFrame
+    compare_mem_reg(OpndSize_32, offsetof(Thread, interpStackEnd), P_SCRATCH_3, true, P_GPR_3, true);
+    conditional_jump(Condition_L, ".stackOverflow", true);
+
+    if(form == ArgsDone_Full) {
+        test_imm_mem(OpndSize_32, ACC_NATIVE, offMethod_accessFlags, PhysicalReg_ECX, true);
+    }
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offStackSaveArea_method-sizeofStackSaveArea, P_GPR_1, true); //set stack->method
+
+    if(form == ArgsDone_Native || form == ArgsDone_Full) {
+        /* to correctly handle code cache reset:
+           update returnAddr and check returnAddr after done with the native method
+           if returnAddr is set to NULL during code cache reset,
+           the execution will correctly continue with interpreter */
+        //get returnAddr from 4(%esp) and update stack
+        move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
+                        PhysicalReg_EDX, true);
+        move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
+                        offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_1, true);
+    }
+    if(form == ArgsDone_Native) {
+        generate_invokeNative(generateForNcg);
+        return 0;
+    }
+    if(form == ArgsDone_Full) {
+        conditional_jump(Condition_NE, ".invokeNative", true);
+    }
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, PhysicalReg_ECX, true, P_SCRATCH_4, true); //get method->claz
+    move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, P_SCRATCH_4, true, P_SCRATCH_4, true); //get method->clazz->pDvmDex
+    move_reg_to_reg(OpndSize_32, P_GPR_1, true, PhysicalReg_FP, true); //update rFP
+    get_self_pointer(P_GPR_1, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offsetof(Thread, interpSave.method), P_GPR_1, true); //glue->method
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_4, true, offsetof(Thread, interpSave.methodClassDex), P_GPR_1, true); //set_glue_dvmdex
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set glue->self->frame
+    if(!generateForNcg) {
+        /* returnAddr updated already for Full */
+        //get returnAddr from 4(%esp) and update stack
+        if(form == ArgsDone_Normal)
+            move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
+                            PhysicalReg_EDX, true);
+        //for JIT: starting bytecode in %ebx to invoke JitToInterp
+        move_mem_to_reg(OpndSize_32, offMethod_insns, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+        if(form == ArgsDone_Normal)
+            move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
+                            offStackSaveArea_returnAddr-sizeofStackSaveArea, PhysicalReg_FP, true);
+    }
+
+    insertLabel(".invokeInterp", true);
+    if(!generateForNcg) {
+        bool callNoChain = false;
+#ifdef PREDICTED_CHAINING
+        if(form == ArgsDone_Full) callNoChain = true;
+#endif
+        if(callNoChain) {
+            scratchRegs[0] = PhysicalReg_EAX;
+            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+#if defined(WITH_JIT_TUNING)
+            /* Predicted chaining failed. Fall back to interpreter and indicate
+             * inline cache miss.
+             */
+            move_imm_to_reg(OpndSize_32, kInlineCacheMiss, PhysicalReg_EDX, true);
+#endif
+            call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx
+        } else {
+            //jump to the stub at (%esp)
+            move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true,
+                            PhysicalReg_EDX, true);
+            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+            unconditional_jump_reg(PhysicalReg_EDX, true);
+        }
+    }
+
+    if(form == ArgsDone_Full) generate_invokeNative(generateForNcg);
+    generate_stackOverflow();
+    return 0;
+}
+
+/* when WITH_JIT is true,
+     JIT'ed code invokes native method, after invoke, execution will continue
+     with the interpreter or with JIT'ed code if chained
+*/
+void generate_invokeNative(bool generateForNcg) {
+    insertLabel(".invokeNative", true);
+    //if(!generateForNcg)
+    //    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EDX;
+    get_self_pointer(P_SCRATCH_1, true); //glue->self
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next
+    scratchRegs[1] = PhysicalReg_EAX;
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame
+    move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache
+    load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true);
+    //NOTE: native method checks the interpreted stack for arguments
+    //      The immediate arguments on native stack: address of return value, new FP, self
+    call_mem(40, PhysicalReg_ECX, true);//*40(%ecx)
+    //we can't assume the argument stack is unmodified after the function call
+    //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4)
+    move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP
+    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self
+    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal
+    compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception
+    if(!generateForNcg)
+        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //NOTE: PhysicalReg_FP should be callee-saved register
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal
+    conditional_jump(Condition_NE, "common_exceptionThrown", false);
+    if(!generateForNcg) {
+        //get returnAddr, if it is not NULL,
+        //    return to JIT'ed returnAddr after executing the native method
+        /* to correctly handle code cache reset:
+           update returnAddr and check returnAddr after done with the native method
+           if returnAddr is set to NULL during code cache reset,
+           the execution will correctly continue with interpreter */
+        move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true);
+        //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx)
+        move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true);
+        move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc
+        compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
+        conditional_jump(Condition_E, ".nativeToInterp", true);
+        unconditional_jump_reg(P_SCRATCH_2, true);
+        //if returnAddr is NULL, return to interpreter after executing the native method
+        insertLabel(".nativeToInterp", true);
+        //move rPC by 6 (3 bytecode units for INVOKE)
+        alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true);
+        scratchRegs[0] = PhysicalReg_EAX;
+#if defined(WITH_JIT_TUNING)
+        /* Return address not in code cache. Indicate that continuing with interpreter
+         */
+        move_imm_to_reg(OpndSize_32, kCallsiteInterpreted, PhysicalReg_EDX, true);
+#endif
+        call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx
+    }
+    return;
+}
+void generate_stackOverflow() {
+    insertLabel(".stackOverflow", true);
+    //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true);
+    get_self_pointer(P_GPR_1, true); //glue->self
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    call_dvmHandleStackOverflow();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+#undef P_SCRATCH_5
+#undef P_SCRATCH_6
+
+/////////////////////////////////////////////
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define P_SCRATCH_3 PhysicalReg_ESI
+#define P_SCRATCH_4 PhysicalReg_EDX
+//! lower bytecode EXECUTE_INLINE
+
+//!
+int op_execute_inline(bool isRange) {
+    //tmp, vC, vD, vE, vF
+    int num;
+    if(!isRange) num = INST_B(inst);
+    else num = INST_AA(inst);
+    u2 tmp = FETCH(1);
+    u2 vC, vD, vE, vF;
+    if(!isRange) {
+        vC = FETCH(2) & 0xf;
+        vD = (FETCH(2) >> 4) & 0xf;
+        vE = (FETCH(2) >> 8) & 0xf;
+        vF = FETCH(2) >> 12;
+    } else {
+        vC = FETCH(2);
+        vD = vC + 1;
+        vE = vC + 2;
+        vF = vC + 3;
+    }
+    export_pc();
+    switch (tmp) {
+        case INLINE_EMPTYINLINEMETHOD:
+            return 0;  /* Nop */
+        case INLINE_STRING_LENGTH:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_length", true);
+            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_STRING_IS_EMPTY:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_length", true);
+            compare_imm_mem(OpndSize_32, 0, 0x14, 1, false);
+            conditional_jump(Condition_E, ".inlined_string_length_return_true",
+                             true);
+            get_self_pointer(2, false);
+            move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false);
+            unconditional_jump(".inlined_string_length_done", true);
+            insertLabel(".inlined_string_length_return_true", true);
+            get_self_pointer(2, false);
+            move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false);
+            insertLabel(".inlined_string_length_done", true);
+            return 0;
+        case INLINE_MATH_ABS_INT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false);
+            alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_MATH_ABS_LONG:
+            get_virtual_reg(vD, OpndSize_32, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
+            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 3, false);
+            move_reg_to_reg(OpndSize_32, 1, false, 4, false);
+            get_virtual_reg(vC, OpndSize_32, 5, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false);
+            get_self_pointer(6, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false);
+            move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false);
+            alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
+            return 0;
+        case INLINE_MATH_MAX_INT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            compare_reg_reg(1, false, 2, false);
+            conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2,
+                                        false/*src*/, 1, false/*dst*/);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_MATH_ABS_FLOAT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_MATH_ABS_DOUBLE:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_STRING_FASTINDEXOF_II:
+#if defined(USE_GLOBAL_STRING_DEFS)
+            break;
+#else
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            get_virtual_reg(vE, OpndSize_32, 3, false);
+            conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof",
+                             true);
+            scratchRegs[0] = PhysicalReg_SCRATCH_1;
+            jumpToExceptionThrown(1/*exception number*/);
+            insertLabel(".do_inlined_string_fastIndexof", true);
+            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false);
+            move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false);
+            move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false);
+            alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false);
+            compare_imm_reg(OpndSize_32, 0, 3, false);
+            conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1,
+                                        false);
+            compare_reg_reg(4, false, 1, false);
+            conditional_jump(Condition_GE,
+                             ".do_inlined_string_fastIndexof_exitfalse", true);
+            dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/,
+                               6, false, 2, 5, false, LowOpndRegType_gp);
+            movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2,
+                                        3, false);
+            compare_reg_reg(3, false, 2, false);
+            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
+                             true);
+            load_effective_addr(0x1, 1, false, 3, false);
+            load_effective_addr_scale(5, false, 3, false, 2, 5, false);
+            unconditional_jump(".do_inlined_string_fastIndexof_iter", true);
+            insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true);
+            if(gDvm.executionMode == kExecutionModeNcgO1) {
+                rememberState(1);
+            }
+            movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false);
+            load_effective_addr(0x2, 5, false, 5, false);
+            compare_reg_reg(6, false, 2, false);
+            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
+                             true);
+            load_effective_addr(0x1, 3, false, 3, false);
+            insertLabel(".do_inlined_string_fastIndexof_iter", true);
+            compare_reg_reg(4, false, 3, false);
+            move_reg_to_reg(OpndSize_32, 3, false, 1, false);
+            if(gDvm.executionMode == kExecutionModeNcgO1) {
+                transferToState(1);
+            }
+            conditional_jump(Condition_NE,
+                             ".do_inlined_string_fastIndexof_ch_cmp", true);
+            insertLabel(".do_inlined_string_fastIndexof_exitfalse", true);
+            move_imm_to_reg(OpndSize_32, 0xffffffff, 1,  false);
+            insertLabel(".do_inlined_string_fastIndexof_exit", true);
+            get_self_pointer(7, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false);
+            return 0;
+        case INLINE_FLOAT_TO_RAW_INT_BITS:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_INT_BITS_TO_FLOAT:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(2, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
+            return 0;
+        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        case INLINE_LONG_BITS_TO_DOUBLE:
+            get_virtual_reg(vC, OpndSize_32, 1, false);
+            get_virtual_reg(vD, OpndSize_32, 2, false);
+            get_self_pointer(3, false);
+            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
+            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
+            return 0;
+        default:
+                break;
+    }
+#endif
+    get_self_pointer(PhysicalReg_SCRATCH_1, false);
+    load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false);
+    load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true);
+    if(num >= 1) {
+        get_virtual_reg(vC, OpndSize_32, 2, false);
+        move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true);
+    }
+    if(num >= 2) {
+        get_virtual_reg(vD, OpndSize_32, 3, false);
+        move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true);
+    }
+    if(num >= 3) {
+        get_virtual_reg(vE, OpndSize_32, 4, false);
+        move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true);
+    }
+    if(num >= 4) {
+        get_virtual_reg(vF, OpndSize_32, 5, false);
+        move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true);
+    }
+    beforeCall("execute_inline");
+    load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false);
+    call_mem(16*tmp, 6, false);//
+    afterCall("execute_inline");
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+
+    load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    conditional_jump(Condition_NE, ".execute_inline_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    jumpToExceptionThrown(1/*exception number*/);
+    insertLabel(".execute_inline_done", true);
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef P_SCRATCH_3
+#undef P_SCRATCH_4
+
+//! lower bytecode INVOKE_OBJECT_INIT_RANGE
+
+//!
+int op_invoke_object_init_range() {
+    return -1;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+#define PP_GPR_1 PhysicalReg_EBX
+#define PP_GPR_2 PhysicalReg_ESI
+#define PP_GPR_3 PhysicalReg_EAX
+#define PP_GPR_4 PhysicalReg_EDX
+//! common code for INVOKE_VIRTUAL_QUICK
+
+//! It uses helper function if the switch is on
+int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) {
+#ifdef WITH_JIT_INLINING
+    /* An invoke with the MIR_INLINED is effectively a no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return false;
+    /*
+     * If the invoke has non-null misPredBranchOver, we need to generate
+     * the non-inlined version of the invoke here to handle the
+     * mispredicted case.
+     */
+    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
+        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
+    }
+#endif
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    /////////////////////////////////////////////////
+    get_virtual_reg(vD, OpndSize_32, 1, false);
+    simpleNullCheck(1, false, vD);
+#ifndef PREDICTED_CHAINING
+    move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false);
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false);
+    move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true);
+
+    if(hasRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+#else
+    gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/);
+#endif
+    ////////////////////////
+    return 0;
+}
+#undef P_GPR_1
+#undef P_SCRATCH_1
+#undef P_SCRATCH_2
+#undef PP_GPR_1
+#undef PP_GPR_2
+#undef PP_GPR_3
+#undef PP_GPR_4
+//! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick
+
+//!
+int op_invoke_virtual_quick() {
+    u2 vD = FETCH(2) & 0xf;
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_virtual_quick(false, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick
+
+//!
+int op_invoke_virtual_quick_range() {
+    u2 vD = FETCH(2);
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_virtual_quick(true, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ESI
+#define P_SCRATCH_1 PhysicalReg_EDX
+//! common code to lower INVOKE_SUPER_QUICK
+
+//!
+int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) {
+    export_pc();
+    constVREndOfBB();
+    beforeCall("exception"); //dump GG, GL VRs
+    compare_imm_VR(OpndSize_32, 0, vD);
+
+    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
+    /* for trace-based JIT, callee is already resolved */
+    int mIndex = IMMC/4;
+    const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex];
+    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
+    if(hasRange) {
+        common_invokeMethodRange(convertCalleeToType(calleeMethod));
+    }
+    else {
+        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
+    }
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+//! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick
+
+//!
+int op_invoke_super_quick() {
+    u2 vD = FETCH(2) & 0xf;
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_super_quick(false, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+//! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick
+
+//!
+int op_invoke_super_quick_range() {
+    u2 vD = FETCH(2);
+    u2 IMMC = 4*FETCH(1);
+    int retval = common_invoke_super_quick(true, vD, IMMC);
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
+#endif
+    rPC += 3;
+    return retval;
+}
+/////// code to predict the callee method for invoke_virtual & invoke_interface
+#define offChainingCell_clazz 8
+#define offChainingCell_method 12
+#define offChainingCell_counter 16
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_EAX
+#define P_GPR_3 PhysicalReg_ESI
+#define P_SCRATCH_2 PhysicalReg_EDX
+/* TODO gingerbread: implemented for O1, but not for O0:
+   valid input to JitToPatch & use icRechainCount */
+/* update predicted method for invoke interface */
+// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
+void predicted_chain_interface_O0(u2 tmp) {
+    ALOGI("TODO chain_interface_O0");
+
+    /* set up arguments to dvmFindInterfaceMethodInCache */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EDX;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
+       otherwise, jump to .find_interface_done */
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".find_interface_done", true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    jumpToExceptionThrown(1/*exception number*/);
+
+    /* the interface method is found */
+    insertLabel(".find_interface_done", true);
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    insertLabel(".skipPrediction", true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+}
+
+// 2 inputs: ChainingCell in temp 41, current class object in temp 40
+void predicted_chain_interface_O1(u2 tmp) {
+
+    /* set up arguments to dvmFindInterfaceMethodInCache */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10;
+    call_dvmFindInterfaceMethodInCache();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
+       otherwise, jump to .find_interface_done */
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".find_interface_done", true);
+    rememberState(3);
+    scratchRegs[0] = PhysicalReg_SCRATCH_9;
+    jumpToExceptionThrown(1/*exception number*/);
+
+    goToState(3);
+    /* the interface method is found */
+    insertLabel(".find_interface_done", true);
+#if 1 //
+    /* for gingerbread, counter is stored in glue structure
+       if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */
+    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false);
+    move_imm_to_reg(OpndSize_32, 0, 43, false);
+    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
+    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    /* sub_opc will update control flags, so compare_imm_reg must happen after */
+    compare_imm_reg(OpndSize_32, 0, 45, false);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
+    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
+#else
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false);
+#endif
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, 43, false);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    rememberState(4);
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_8;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    transferToState(4);
+
+    insertLabel(".skipPrediction", true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+}
+
+/* update predicted method for invoke virtual */
+// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
+void predicted_chain_virtual_O0(u2 IMMC) {
+    ALOGI("TODO chain_virtual_O0");
+
+    /* reduce counter in chaining cell by 1 */
+    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true);
+    move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0,  PhysicalReg_ESP, true);
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //callee method in %ecx for invoke virtual
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    insertLabel(".skipPrediction", true);
+}
+
+// 2 inputs: ChainingCell in temp 41, current class object in temp 40
+// extra input: predicted clazz in temp 32
+void predicted_chain_virtual_O1(u2 IMMC) {
+
+    /* reduce counter in chaining cell by 1 */
+    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
+    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
+    move_imm_to_reg(OpndSize_32, 0, 43, false);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
+    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false);
+    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
+    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
+    compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc
+    move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
+    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
+
+    /* if counter is still greater than zero, skip prediction
+       if it is zero, update predicted method */
+    compare_imm_reg(OpndSize_32, 0, 43, false);
+    conditional_jump(Condition_G, ".skipPrediction", true);
+
+    rememberState(2);
+    /* call dvmJitToPatchPredictedChain to update predicted method */
+    //%ecx has callee method for virtual, %eax has callee for interface
+    /* set up arguments for dvmJitToPatchPredictedChain */
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell
+    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10;
+    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //callee method in %ecx for invoke virtual
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
+    transferToState(2);
+
+    insertLabel(".skipPrediction", true);
+}
+
+static int invokeChain_inst = 0;
+/* object "this" is in %ebx */
+void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+    ALOGI("TODO predicted_chain_O0");
+
+    /* get current class object */
+    move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true,
+             P_GPR_3, true);
+#ifdef DEBUG_CALL_STACK3
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    /* get predicted clazz
+       get predicted method
+    */
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz
+    move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method
+
+#ifdef DEBUG_CALL_STACK3
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
+
+    move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+
+    move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true);
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+#endif
+
+    /* compare current class object against predicted clazz
+       if equal, prediction is still valid, jump to .invokeChain */
+    //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2
+    compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true);
+    conditional_jump(Condition_E, ".invokeChain", true);
+    invokeChain_inst++;
+
+    //get callee method and update predicted method if necessary
+    if(isInterface) {
+        predicted_chain_interface_O0(tmp);
+    } else {
+        predicted_chain_virtual_O0(IMMC);
+    }
+
+#ifdef DEBUG_CALL_STACK3
+    move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Full);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Full);
+    }
+
+    insertLabel(".invokeChain", true);
+#ifdef DEBUG_CALL_STACK3
+    move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+#endif
+
+    if(isRange) {
+        common_invokeMethodRange(ArgsDone_Normal);
+    }
+    else {
+        common_invokeMethodNoRange(ArgsDone_Normal);
+    }
+}
+
+/* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */
+void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+
+    /* get current class object */
+    move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false,
+             40, false);
+
+    /* get predicted clazz
+       get predicted method
+    */
+    if(traceCurrentBB->taken)
+        insertChainingWorklist(traceCurrentBB->taken->id, stream);
+    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+    move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell
+    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz
+    move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method
+
+    /* update stack with parameters first, then decide the callee */
+    if(isRange) common_invokeMethodRange_noJmp();
+    else common_invokeMethodNoRange_noJmp();
+
+    /* compare current class object against predicted clazz
+       if equal, prediction is still valid, jump to .invokeChain */
+    compare_reg_reg(40, false, 32, false);
+    conditional_jump(Condition_E, ".invokeChain", true);
+    rememberState(1);
+    invokeChain_inst++;
+
+    //get callee method and update predicted method if necessary
+    if(isInterface) {
+        predicted_chain_interface_O1(tmp);
+    } else {
+        predicted_chain_virtual_O1(IMMC);
+    }
+
+    common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx
+
+    insertLabel(".invokeChain", true);
+    goToState(1);
+    common_invokeMethod_Jmp(ArgsDone_Normal);
+}
+
+void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
+    return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_2
diff --git a/vm/compiler/codegen/x86/LowerJump.cpp b/vm/compiler/codegen/x86/LowerJump.cpp
new file mode 100644
index 0000000..d4b0df3
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerJump.cpp
@@ -0,0 +1,1582 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerJump.cpp
+    \brief This file lowers the following bytecodes: IF_XXX, GOTO
+*/
+#include <math.h>
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+#include "interp/InterpDefs.h"
+#include "NcgHelper.h"
+
+LabelMap* globalMap;
+LabelMap* globalShortMap;//make sure for each bytecode, there is no duplicated label
+LabelMap* globalWorklist = NULL;
+LabelMap* globalShortWorklist;
+
+int globalMapNum;
+int globalWorklistNum;
+int globalDataWorklistNum;
+int VMAPIWorklistNum;
+int globalPCWorklistNum;
+int chainingWorklistNum;
+
+LabelMap* globalDataWorklist = NULL;
+LabelMap* globalPCWorklist = NULL;
+LabelMap* chainingWorklist = NULL;
+LabelMap* VMAPIWorklist = NULL;
+
+char* ncgClassData;
+char* ncgClassDataPtr;
+char* ncgMethodData;
+char* ncgMethodDataPtr;
+int   ncgClassNum;
+int   ncgMethodNum;
+
+NCGWorklist* globalNCGWorklist;
+DataWorklist* methodDataWorklist;
+#ifdef ENABLE_TRACING
+MapWorklist* methodMapWorklist;
+#endif
+/*!
+\brief search globalShortMap to find the entry for the given label
+
+*/
+LabelMap* findItemForShortLabel(const char* label) {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+//assume size of "jump reg" is 2
+#define JUMP_REG_SIZE 2
+#define ADD_REG_REG_SIZE 3
+/*!
+\brief update value of the immediate in the given jump instruction
+
+check whether the immediate is out of range for the pre-set size
+*/
+int updateJumpInst(char* jumpInst, OpndSize immSize, int relativeNCG) {
+#ifdef DEBUG_NCG_JUMP
+    ALOGI("update jump inst @ %p with %d", jumpInst, relativeNCG);
+#endif
+    if(immSize == OpndSize_8) { //-128 to 127
+        if(relativeNCG >= 128 || relativeNCG < -128) {
+            ALOGE("pre-allocated space for a forward jump is not big enough");
+            dvmAbort();
+        }
+    }
+    if(immSize == OpndSize_16) { //-2^16 to 2^16-1
+        if(relativeNCG >= 32768 || relativeNCG < -32768) {
+            ALOGE("pre-allocated space for a forward jump is not big enough");
+            dvmAbort();
+        }
+    }
+    encoder_update_imm(relativeNCG, jumpInst);
+    return 0;
+}
+
+/*!
+\brief insert a label
+
+It takes argument checkDup, if checkDup is true, an entry is created in globalShortMap, entries in globalShortWorklist are checked, if there exists a match, the immediate in the jump instruction is updated and the entry is removed from globalShortWorklist;
+otherwise, an entry is created in globalMap.
+*/
+int insertLabel(const char* label, bool checkDup) {
+    LabelMap* item = NULL;
+    if(!checkDup) {
+        item = (LabelMap*)malloc(sizeof(LabelMap));
+        if(item == NULL) {
+            ALOGE("Memory allocation failed");
+            return -1;
+        }
+        snprintf(item->label, LABEL_SIZE, "%s", label);
+        item->codePtr = stream;
+        item->nextItem = globalMap;
+        globalMap = item;
+#ifdef DEBUG_NCG_CODE_SIZE
+        ALOGI("insert global label %s %p", label, stream);
+#endif
+        globalMapNum++;
+        return 0;
+    }
+
+    item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->nextItem = globalShortMap;
+    globalShortMap = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert short-term label %s %p", label, stream);
+#endif
+    LabelMap* ptr = globalShortWorklist;
+    LabelMap* ptr_prevItem = NULL;
+    while(ptr != NULL) {
+        if(!strcmp(ptr->label, label)) {
+            //perform work
+            int relativeNCG = stream - ptr->codePtr;
+            unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+            relativeNCG -= instSize; //size of the instruction
+#ifdef DEBUG_NCG
+            ALOGI("perform work short-term %p for label %s relative %d", ptr->codePtr, label, relativeNCG);
+#endif
+            updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+            //remove work
+            if(ptr_prevItem == NULL) {
+                globalShortWorklist = ptr->nextItem;
+                free(ptr);
+                ptr = globalShortWorklist; //ptr_prevItem is still NULL
+            }
+            else {
+                ptr_prevItem->nextItem = ptr->nextItem;
+                free(ptr);
+                ptr = ptr_prevItem->nextItem;
+            }
+        }
+        else {
+            ptr_prevItem = ptr;
+            ptr = ptr->nextItem;
+        }
+    } //while
+    return 0;
+}
+/*!
+\brief search globalMap to find the entry for the given label
+
+*/
+char* findCodeForLabel(const char* label) {
+    LabelMap* ptr = globalMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr->codePtr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+/*!
+\brief search globalShortMap to find the entry for the given label
+
+*/
+char* findCodeForShortLabel(const char* label) {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        if(!strcmp(label, ptr->label)) {
+            return ptr->codePtr;
+        }
+        ptr = ptr->nextItem;
+    }
+    return NULL;
+}
+int insertLabelWorklist(const char* label, OpndSize immSize) {
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalWorklist;
+    globalWorklist = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert globalWorklist: %s %p", label, stream);
+#endif
+    return 0;
+}
+
+int insertShortWorklist(const char* label, OpndSize immSize) {
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalShortWorklist;
+    globalShortWorklist = item;
+#ifdef DEBUG_NCG
+    ALOGI("insert globalShortWorklist: %s %p", label, stream);
+#endif
+    return 0;
+}
+/*!
+\brief free memory allocated for globalMap
+
+*/
+void freeLabelMap() {
+    LabelMap* ptr = globalMap;
+    while(ptr != NULL) {
+        globalMap = ptr->nextItem;
+        free(ptr);
+        ptr = globalMap;
+    }
+}
+/*!
+\brief free memory allocated for globalShortMap
+
+*/
+void freeShortMap() {
+    LabelMap* ptr = globalShortMap;
+    while(ptr != NULL) {
+        globalShortMap = ptr->nextItem;
+        free(ptr);
+        ptr = globalShortMap;
+    }
+    globalShortMap = NULL;
+}
+
+int insertGlobalPCWorklist(char * offset, char * codeStart)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", "export_pc");
+    item->size = OpndSize_32;
+    item->codePtr = offset; //points to the immediate operand
+    item->addend = codeStart - streamMethodStart; //relative code pointer
+    item->nextItem = globalPCWorklist;
+    globalPCWorklist = item;
+    globalPCWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert globalPCWorklist: %p %p %p %x %p", globalDvmNcg->streamCode,  codeStart, streamCode, item->addend, item->codePtr);
+#endif
+    return 0;
+}
+
+int insertChainingWorklist(int bbId, char * codeStart)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->size = OpndSize_32;
+    item->codePtr = codeStart; //points to the move instruction
+    item->addend = bbId; //relative code pointer
+    item->nextItem = chainingWorklist;
+    chainingWorklist = item;
+
+#ifdef DEBUG_NCG
+    ALOGI("insertChainingWorklist: %p basic block %d", codeStart, bbId);
+#endif
+    return 0;
+}
+
+int insertGlobalDataWorklist(char * offset, const char* label)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = offset;
+    item->size = OpndSize_32;
+    item->nextItem = globalDataWorklist;
+    globalDataWorklist = item;
+    globalDataWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert globalDataWorklist: %s %p", label, offset);
+#endif
+
+    return 0;
+}
+
+int insertVMAPIWorklist(char * offset, const char* label)
+{
+    LabelMap* item = (LabelMap*)malloc(sizeof(LabelMap));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    snprintf(item->label, LABEL_SIZE, "%s", label);
+    item->codePtr = offset;
+    item->size = OpndSize_32;
+
+    item->nextItem = VMAPIWorklist;
+    VMAPIWorklist = item;
+
+    VMAPIWorklistNum ++;
+
+#ifdef DEBUG_NCG
+    ALOGI("insert VMAPIWorklist: %s %p", label, offset);
+#endif
+    return 0;
+}
+////////////////////////////////////////////////
+
+
+int updateImmRMInst(char* moveInst, const char* label, int relativeNCG); //forward declaration
+//////////////////// performLabelWorklist is defined differently for code cache
+void performChainingWorklist() {
+    LabelMap* ptr = chainingWorklist;
+    while(ptr != NULL) {
+        int tmpNCG = traceLabelList[ptr->addend].lop.generic.offset;
+        char* NCGaddr = streamMethodStart + tmpNCG;
+        updateImmRMInst(ptr->codePtr, "", (int)NCGaddr);
+        chainingWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = chainingWorklist;
+    }
+}
+void freeChainingWorklist() {
+    LabelMap* ptr = chainingWorklist;
+    while(ptr != NULL) {
+        chainingWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = chainingWorklist;
+    }
+}
+
+//Work only for initNCG
+void performLabelWorklist() {
+    LabelMap* ptr = globalWorklist;
+    while(ptr != NULL) {
+#ifdef DEBUG_NCG
+        ALOGI("perform work global %p for label %s", ptr->codePtr, ptr->label);
+#endif
+        char* targetCode = findCodeForLabel(ptr->label);
+        assert(targetCode != NULL);
+        int relativeNCG = targetCode - ptr->codePtr;
+        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+        relativeNCG -= instSize; //size of the instruction
+        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+        globalWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalWorklist;
+    }
+}
+void freeLabelWorklist() {
+    LabelMap* ptr = globalWorklist;
+    while(ptr != NULL) {
+        globalWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalWorklist;
+    }
+}
+
+///////////////////////////////////////////////////
+/*!
+\brief update value of the immediate in the given move instruction
+
+*/
+int updateImmRMInst(char* moveInst, const char* label, int relativeNCG) {
+#ifdef DEBUG_NCG
+    ALOGI("perform work ImmRM inst @ %p for label %s with %d", moveInst, label, relativeNCG);
+#endif
+    encoder_update_imm_rm(relativeNCG, moveInst);
+    return 0;
+}
+//! maximum instruction size for jump,jcc,call: 6 for jcc rel32
+#define MAX_JCC_SIZE 6
+//! minimum instruction size for jump,jcc,call: 2
+#define MIN_JCC_SIZE 2
+/*!
+\brief estimate size of the immediate
+
+Somehow, 16 bit jump does not work. This function will return either 8 bit or 32 bit
+EXAMPLE:
+  native code at A: ...
+  native code at B: jump relOffset (target is A)
+  native code at B':
+  --> relOffset = A - B' = A - B - size of the jump instruction
+  Argument "target" is equal to A - B. To determine size of the immediate, we check tha value of "target - size of the jump instructoin"
+*/
+OpndSize estOpndSizeFromImm(int target) {
+    if(target-MIN_JCC_SIZE < 128 && target-MAX_JCC_SIZE >= -128) return OpndSize_8;
+#ifdef SUPPORT_IMM_16
+    if(target-MIN_JCC_SIZE < 32768 && target-MAX_JCC_SIZE >= -32768) return OpndSize_16;
+#endif
+    return OpndSize_32;
+}
+/*!
+\brief return size of a jump or call instruction
+
+*/
+unsigned getJmpCallInstSize(OpndSize size, JmpCall_type type) {
+    if(type == JmpCall_uncond) {
+        if(size == OpndSize_8) return 2;
+        if(size == OpndSize_16) return 4;
+        return 5;
+    }
+    if(type == JmpCall_cond) {
+        if(size == OpndSize_8) return 2;
+        if(size == OpndSize_16) return 5;
+        return 6;
+    }
+    if(type == JmpCall_reg) {
+        assert(size == OpndSize_32);
+        return JUMP_REG_SIZE;
+    }
+    if(type == JmpCall_call) {
+        assert(size != OpndSize_8);
+        if(size == OpndSize_16) return 4;
+        return 5;
+    }
+    return 0;
+}
+/*!
+\brief check whether a branch target is already handled, if yes, return the size of the immediate; otherwise, call insertShortWorklist or insertLabelWorklist.
+
+If the branch target is not handled, call insertShortWorklist or insertLabelWorklist depending on isShortTerm, unknown is set to true, immSize is set to 32 if isShortTerm is false, set to 32 if isShortTerm is true and target is check_cast_null, set to 8 otherwise.
+
+If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
+*/
+int getRelativeOffset(const char* target, bool isShortTerm, JmpCall_type type, bool* unknown, OpndSize* immSize) {
+    char* targetPtrInStream = NULL;
+    if(isShortTerm) targetPtrInStream = findCodeForShortLabel(target);
+    else targetPtrInStream = findCodeForLabel(target);
+
+    int relOffset;
+    *unknown = false;
+    if(targetPtrInStream == NULL) {
+        //branch target is not handled yet
+        relOffset = 0;
+        *unknown = true;
+        if(isShortTerm) {
+            /* for backward jump, at this point, we don't know how far the target is from this jump
+               since the lable is only used within a single bytecode, we assume OpndSize_8 is big enough
+               but there are special cases where we should use 32 bit offset
+            */
+            if(!strcmp(target, ".check_cast_null") || !strcmp(target, ".stackOverflow") ||
+               !strcmp(target, ".invokeChain") ||
+               !strcmp(target, ".new_instance_done") ||
+               !strcmp(target, ".new_array_done") ||
+               !strcmp(target, ".fill_array_data_done") ||
+               !strcmp(target, ".inlined_string_compare_done") ||
+               !strncmp(target, "after_exception", 15)) {
+#ifdef SUPPORT_IMM_16
+                *immSize = OpndSize_16;
+#else
+                *immSize = OpndSize_32;
+#endif
+            } else {
+                *immSize = OpndSize_8;
+            }
+#ifdef DEBUG_NCG_JUMP
+            ALOGI("insert to short worklist %s %d", target, *immSize);
+#endif
+            insertShortWorklist(target, *immSize);
+        }
+        else {
+#ifdef SUPPORT_IMM_16
+            *immSize = OpndSize_16;
+#else
+            *immSize = OpndSize_32;
+#endif
+            insertLabelWorklist(target, *immSize);
+        }
+        if(type == JmpCall_call) { //call sz16 does not work in gdb
+            *immSize = OpndSize_32;
+        }
+        return 0;
+    }
+    else if (!isShortTerm) {
+#ifdef SUPPORT_IMM_16
+        *immSize = OpndSize_16;
+#else
+        *immSize = OpndSize_32;
+#endif
+        insertLabelWorklist(target, *immSize);
+    }
+
+#ifdef DEBUG_NCG
+    ALOGI("backward branch @ %p for label %s", stream, target);
+#endif
+    relOffset = targetPtrInStream - stream;
+    if(type == JmpCall_call) *immSize = OpndSize_32;
+    else
+        *immSize = estOpndSizeFromImm(relOffset);
+
+    relOffset -= getJmpCallInstSize(*immSize, type);
+    return relOffset;
+}
+
+/*!
+\brief generate a single native instruction "jcc imm" to jump to a label
+
+*/
+void conditional_jump(ConditionCode cc, const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
+        return;
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
+    bool unknown;
+    OpndSize size;
+    int imm = 0;
+    imm = getRelativeOffset(target, isShortTerm, JmpCall_cond, &unknown, &size);
+    dump_label(m, size, imm, target, isShortTerm);
+}
+/*!
+\brief generate a single native instruction "jmp imm" to jump to ".invokeArgsDone"
+
+*/
+void goto_invokeArgsDone() {
+    unconditional_jump_global_API(".invokeArgsDone", false);
+}
+/*!
+\brief generate a single native instruction "jmp imm" to jump to a label
+
+If the target is ".invokeArgsDone" and mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
+*/
+void unconditional_jump(const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        jumpToBasicBlock(stream, currentExceptionBlockIdx);
+        return;
+    }
+    Mnemonic m = Mnemonic_JMP;
+    bool unknown;
+    OpndSize size;
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        //for other three labels used by JIT: invokeArgsDone_formal, _native, _jit
+        if(!strncmp(target, ".invokeArgsDone", 15)) {
+            touchEcx(); //keep ecx live, if ecx was spilled, it is loaded here
+            beforeCall(target); //
+        }
+        if(!strcmp(target, ".invokeArgsDone")) {
+            nextVersionOfHardReg(PhysicalReg_EDX, 1); //edx will be used in a function
+            call("ncgGetEIP"); //must be immediately before JMP
+        }
+    }
+    int imm = 0;
+    imm = getRelativeOffset(target, isShortTerm, JmpCall_uncond, &unknown, &size);
+    dump_label(m, size, imm, target, isShortTerm);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        if(!strncmp(target, ".invokeArgsDone", 15)) {
+            afterCall(target); //un-spill before executing the next bytecode
+        }
+    }
+}
+/*!
+\brief generate a single native instruction "jcc imm"
+
+*/
+void conditional_jump_int(ConditionCode cc, int target, OpndSize size) {
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cc);
+    dump_ncg(m, size, target);
+}
+/*!
+\brief generate a single native instruction "jmp imm"
+
+*/
+void unconditional_jump_int(int target, OpndSize size) {
+    Mnemonic m = Mnemonic_JMP;
+    dump_ncg(m, size, target);
+}
+/*!
+\brief generate a single native instruction "jmp reg"
+
+*/
+void unconditional_jump_reg(int reg, bool isPhysical) {
+    dump_reg(Mnemonic_JMP, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+
+/*!
+\brief generate a single native instruction to call a function
+
+If mode is NCG O1, extra work is performed to dump content of virtual registers to memory.
+*/
+void call(const char* target) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        beforeCall(target);
+    }
+    Mnemonic m = Mnemonic_CALL;
+    bool dummy;
+    OpndSize size;
+    int relOffset = 0;
+    relOffset = getRelativeOffset(target, false, JmpCall_call, &dummy, &size);
+    dump_label(m, size, relOffset, target, false);
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        afterCall(target);
+    }
+}
+/*!
+\brief generate a single native instruction to call a function
+
+*/
+void call_reg(int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_reg(m, ATOM_NORMAL, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+void call_reg_noalloc(int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_reg_noalloc(m, OpndSize_32, reg, isPhysical, LowOpndRegType_gp);
+}
+
+/*!
+\brief generate a single native instruction to call a function
+
+*/
+void call_mem(int disp, int reg, bool isPhysical) {
+    Mnemonic m = Mnemonic_CALL;
+    dump_mem(m, ATOM_NORMAL, OpndSize_32, disp, reg, isPhysical);
+}
+
+/*!
+\brief insert an entry to globalNCGWorklist
+
+*/
+int insertNCGWorklist(s4 relativePC, OpndSize immSize) {
+    int offsetNCG2 = stream - streamMethodStart;
+#ifdef DEBUG_NCG
+    ALOGI("insert NCGWorklist (goto forward) @ %p offsetPC %x relativePC %x offsetNCG %x", stream, offsetPC, relativePC, offsetNCG2);
+#endif
+    NCGWorklist* item = (NCGWorklist*)malloc(sizeof(NCGWorklist));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->relativePC = relativePC;
+    item->offsetPC = offsetPC;
+    item->offsetNCG = offsetNCG2;
+    item->codePtr = stream;
+    item->size = immSize;
+    item->nextItem = globalNCGWorklist;
+    globalNCGWorklist = item;
+    return 0;
+}
+#ifdef ENABLE_TRACING
+int insertMapWorklist(s4 BCOffset, s4 NCGOffset, int isStartOfPC) {
+    return 0;
+}
+#endif
+/*!
+\brief insert an entry to methodDataWorklist
+
+This function is used by bytecode FILL_ARRAY_DATA, PACKED_SWITCH, SPARSE_SWITCH
+*/
+int insertDataWorklist(s4 relativePC, char* codePtr1) {
+    //insert according to offsetPC+relativePC, smallest at the head
+    DataWorklist* item = (DataWorklist*)malloc(sizeof(DataWorklist));
+    if(item == NULL) {
+        ALOGE("Memory allocation failed");
+        return -1;
+    }
+    item->relativePC = relativePC;
+    item->offsetPC = offsetPC;
+    item->codePtr = codePtr1;
+    item->codePtr2 = stream; //jump_reg for switch
+    DataWorklist* ptr = methodDataWorklist;
+    DataWorklist* prev_ptr = NULL;
+    while(ptr != NULL) {
+        int tmpPC = ptr->offsetPC + ptr->relativePC;
+        int tmpPC2 = relativePC + offsetPC;
+        if(tmpPC2 < tmpPC) {
+            break;
+        }
+        prev_ptr = ptr;
+        ptr = ptr->nextItem;
+    }
+    //insert item before ptr
+    if(prev_ptr != NULL) {
+        prev_ptr->nextItem = item;
+    }
+    else methodDataWorklist = item;
+    item->nextItem = ptr;
+    return 0;
+}
+
+/*!
+\brief work on globalNCGWorklist
+
+*/
+int performNCGWorklist() {
+    NCGWorklist* ptr = globalNCGWorklist;
+    while(ptr != NULL) {
+        ALOGV("perform NCG worklist: @ %p target block %d target NCG %x",
+             ptr->codePtr, ptr->relativePC, traceLabelList[ptr->relativePC].lop.generic.offset);
+        int tmpNCG = traceLabelList[ptr->relativePC].lop.generic.offset;
+        assert(tmpNCG >= 0);
+        int relativeNCG = tmpNCG - ptr->offsetNCG;
+        unsigned instSize = encoder_get_inst_size(ptr->codePtr);
+        relativeNCG -= instSize;
+        updateJumpInst(ptr->codePtr, ptr->size, relativeNCG);
+        globalNCGWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalNCGWorklist;
+    }
+    return 0;
+}
+void freeNCGWorklist() {
+    NCGWorklist* ptr = globalNCGWorklist;
+    while(ptr != NULL) {
+        globalNCGWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = globalNCGWorklist;
+    }
+}
+
+/*!
+\brief used by bytecode SWITCH
+
+targetPC points to start of the data section
+Code sequence for SWITCH
+  call ncgGetEIP
+  @codeInst: add_reg_reg %eax, %edx
+  jump_reg %edx
+This function returns the offset in native code between add_reg_reg and the data section
+*/
+int getRelativeNCGForSwitch(int targetPC, char* codeInst) {
+    int tmpNCG = mapFromBCtoNCG[targetPC];
+    int offsetNCG2 = codeInst - streamMethodStart;
+    int relativeOff = tmpNCG - offsetNCG2;
+    return relativeOff;
+}
+/*!
+\brief work on methodDataWorklist
+
+*/
+int performDataWorklist() {
+    DataWorklist* ptr = methodDataWorklist;
+    if(ptr == NULL) return 0;
+
+    char* codeCacheEnd = ((char *) gDvmJit.codeCache) + gDvmJit.codeCacheSize - CODE_CACHE_PADDING;
+    u2 insnsSize = dvmGetMethodInsnsSize(currentMethod); //bytecode
+    //align stream to multiple of 4
+    int alignBytes = (int)stream & 3;
+    if(alignBytes != 0) alignBytes = 4-alignBytes;
+    stream += alignBytes;
+
+    while(ptr != NULL) {
+        int tmpPC = ptr->offsetPC + ptr->relativePC;
+        int endPC = insnsSize;
+        if(ptr->nextItem != NULL) endPC = ptr->nextItem->offsetPC + ptr->nextItem->relativePC;
+        mapFromBCtoNCG[tmpPC] = stream - streamMethodStart; //offsetNCG in byte
+
+        //handle fill_array_data, packed switch & sparse switch
+        u2 tmpInst = *(currentMethod->insns + ptr->offsetPC);
+        u2* sizePtr;
+        s4* entryPtr_bytecode;
+        u2 tSize, iVer;
+        u4 sz;
+
+        if (gDvmJit.codeCacheFull == true) {
+            // We are out of code cache space. Skip writing data/code to
+            //   code cache. Simply free the item.
+            methodDataWorklist = ptr->nextItem;
+            free(ptr);
+            ptr = methodDataWorklist;
+        }
+
+        switch (INST_INST(tmpInst)) {
+        case OP_FILL_ARRAY_DATA:
+            sz = (endPC-tmpPC)*sizeof(u2);
+            if ((stream + sz) < codeCacheEnd) {
+                memcpy(stream, (u2*)currentMethod->insns+tmpPC, sz);
+#ifdef DEBUG_NCG_CODE_SIZE
+                ALOGI("copy data section to stream %p: start at %d, %d bytes", stream, tmpPC, sz);
+#endif
+#ifdef DEBUG_NCG
+                ALOGI("update data section at %p with %d", ptr->codePtr, stream-ptr->codePtr);
+#endif
+                updateImmRMInst(ptr->codePtr, "", stream - ptr->codePtr);
+                stream += sz;
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        case OP_PACKED_SWITCH:
+            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
+            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
+            entryPtr_bytecode = (s4*)(sizePtr + 1 /*size*/ + 2 /*firstKey*/);
+            tSize = *(sizePtr);
+            sz = tSize * 4;     /* expected size needed in stream */
+            if ((stream + sz) < codeCacheEnd) {
+                for(iVer = 0; iVer < tSize; iVer++) {
+                    //update entries
+                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
+                    //need stream, offsetPC,
+                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
+#ifdef DEBUG_NCG_CODE_SIZE
+                    ALOGI("convert target from %d to %d", relativePC+ptr->offsetPC, relativeNCG);
+#endif
+                    *((s4*)stream) = relativeNCG;
+                    stream += 4;
+                    entryPtr_bytecode++;
+                }
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        case OP_SPARSE_SWITCH:
+            updateImmRMInst(ptr->codePtr, "", stream-ptr->codePtr);
+            sizePtr = (u2*)currentMethod->insns+tmpPC + 1 /*signature*/;
+            s4* keyPtr_bytecode = (s4*)(sizePtr + 1 /*size*/);
+            tSize = *(sizePtr);
+            entryPtr_bytecode = (s4*)(keyPtr_bytecode + tSize);
+            sz = tSize * (sizeof(s4) + 4); /* expected size needed in stream */
+            if ((stream + sz) < codeCacheEnd) {
+                memcpy(stream, keyPtr_bytecode, tSize*sizeof(s4));
+                stream += tSize*sizeof(s4);
+                for(iVer = 0; iVer < tSize; iVer++) {
+                    //update entries
+                    s4 relativePC = *entryPtr_bytecode; //relative to ptr->offsetPC
+                    //need stream, offsetPC,
+                    int relativeNCG = getRelativeNCGForSwitch(relativePC+ptr->offsetPC, ptr->codePtr2);
+                    *((s4*)stream) = relativeNCG;
+                    stream += 4;
+                    entryPtr_bytecode++;
+                }
+            } else {
+                gDvmJit.codeCacheFull = true;
+            }
+            break;
+        }
+
+        //remove the item
+        methodDataWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = methodDataWorklist;
+    }
+    return 0;
+}
+void freeDataWorklist() {
+    DataWorklist* ptr = methodDataWorklist;
+    while(ptr != NULL) {
+        methodDataWorklist = ptr->nextItem;
+        free(ptr);
+        ptr = methodDataWorklist;
+    }
+}
+
+//////////////////////////
+/*!
+\brief check whether a branch target (specified by relative offset in bytecode) is already handled, if yes, return the size of the immediate; otherwise, call insertNCGWorklist.
+
+If the branch target is not handled, call insertNCGWorklist, unknown is set to true, immSize is set to 32.
+
+If the branch target is handled, call estOpndSizeFromImm to set immSize for jump instruction, returns the value of the immediate
+*/
+int getRelativeNCG(s4 tmp, JmpCall_type type, bool* unknown, OpndSize* size) {//tmp: relativePC
+    int tmpNCG = traceLabelList[tmp].lop.generic.offset;
+
+    *unknown = false;
+    if(tmpNCG <0) {
+        *unknown = true;
+#ifdef SUPPORT_IMM_16
+        *size = OpndSize_16;
+#else
+        *size = OpndSize_32;
+#endif
+        insertNCGWorklist(tmp, *size);
+        return 0;
+    }
+    int offsetNCG2 = stream - streamMethodStart;
+#ifdef DEBUG_NCG
+    ALOGI("goto backward @ %p offsetPC %d relativePC %d offsetNCG %d relativeNCG %d", stream, offsetPC, tmp, offsetNCG2, tmpNCG-offsetNCG2);
+#endif
+    int relativeOff = tmpNCG - offsetNCG2;
+    *size = estOpndSizeFromImm(relativeOff);
+    return relativeOff - getJmpCallInstSize(*size, type);
+}
+/*!
+\brief a helper function to handle backward branch
+
+input: jump target in %eax; at end of the function, jump to %eax
+*/
+int common_backwardBranch() {
+    insertLabel("common_backwardBranch", false);
+    spill_reg(PhysicalReg_EAX, true);
+    call("common_periodicChecks_entry");
+    unspill_reg(PhysicalReg_EAX, true);
+    unconditional_jump_reg(PhysicalReg_EAX, true);
+    return 0;
+}
+//when this is called from JIT, there is no need to check GC
+int common_goto(s4 tmp) { //tmp: target basic block id
+    bool unknown;
+    OpndSize size;
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+
+    int relativeNCG = tmp;
+    relativeNCG = getRelativeNCG(tmp, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return 1;
+}
+int common_if(s4 tmp, ConditionCode cc_next, ConditionCode cc) {
+    bool unknown;
+    OpndSize size;
+    int relativeNCG = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
+
+    if(traceCurrentBB->taken)
+        relativeNCG = getRelativeNCG(traceCurrentBB->taken->id, JmpCall_cond, &unknown, &size);
+    conditional_jump_int(cc, relativeNCG, size);
+    relativeNCG = traceCurrentBB->fallThrough ? traceCurrentBB->fallThrough->id : 0;
+    if(traceCurrentBB->fallThrough)
+        relativeNCG = getRelativeNCG(traceCurrentBB->fallThrough->id, JmpCall_uncond, &unknown, &size);
+    unconditional_jump_int(relativeNCG, size);
+    return 2;
+}
+
+/*!
+\brief helper function to handle null object error
+
+*/
+int common_errNullObject() {
+    insertLabel("common_errNullObject", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNullPointerException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle string index error
+
+*/
+int common_StringIndexOutOfBounds() {
+    insertLabel("common_StringIndexOutOfBounds", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrStringIndexOutOfBoundsException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+
+/*!
+\brief helper function to handle array index error
+
+*/
+int common_errArrayIndex() {
+    insertLabel("common_errArrayIndex", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArrayIndexException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle array store error
+
+*/
+int common_errArrayStore() {
+    insertLabel("common_errArrayStore", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArrayStoreException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle negative array size error
+
+*/
+int common_errNegArraySize() {
+    insertLabel("common_errNegArraySize", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNegativeArraySizeException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle divide-by-zero error
+
+*/
+int common_errDivideByZero() {
+    insertLabel("common_errDivideByZero", false);
+    move_imm_to_reg(OpndSize_32, LstrDivideByZero, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrArithmeticException, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+/*!
+\brief helper function to handle no such method error
+
+*/
+int common_errNoSuchMethod() {
+    insertLabel("common_errNoSuchMethod", false);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, LstrNoSuchMethodError, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+int call_dvmFindCatchBlock();
+
+#define P_GPR_1 PhysicalReg_ESI //self callee-saved
+#define P_GPR_2 PhysicalReg_EBX //exception callee-saved
+#define P_GPR_3 PhysicalReg_EAX //method that caused exception
+/*!
+\brief helper function common_exceptionThrown
+
+*/
+int common_exceptionThrown() {
+    insertLabel("common_exceptionThrown", false);
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToExceptionThrown;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+/*!
+\brief helper function to throw an exception with message
+
+INPUT: obj_reg(%eax), exceptionPtrReg(%ecx)
+SCRATCH: C_SCRATCH_1(%esi) & C_SCRATCH_2(%edx)
+OUTPUT: no
+*/
+int throw_exception_message(int exceptionPtrReg, int obj_reg, bool isPhysical,
+                            int startLR/*logical register index*/, bool startPhysical) {
+    insertLabel("common_throw_message", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EDX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    move_mem_to_reg(OpndSize_32, offObject_clazz, obj_reg, isPhysical, C_SCRATCH_1, isScratchPhysical);
+    move_mem_to_reg(OpndSize_32, offClassObject_descriptor, C_SCRATCH_1, isScratchPhysical, C_SCRATCH_2, isScratchPhysical);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, C_SCRATCH_2, isScratchPhysical, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
+    call_dvmThrowWithMessage();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+    return 0;
+}
+/*!
+\brief helper function to throw an exception
+
+scratch: C_SCRATCH_1(%edx)
+*/
+int throw_exception(int exceptionPtrReg, int immReg,
+                    int startLR/*logical register index*/, bool startPhysical) {
+    insertLabel("common_throw", false);
+    scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, immReg, true, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, exceptionPtrReg, true, 0, PhysicalReg_ESP, true);
+    call_dvmThrow();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    unconditional_jump("common_exceptionThrown", false);
+    return 0;
+}
+
+//! lower bytecode GOTO
+
+//!
+int op_goto() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto(tmp);
+    rPC += 1;
+    return retval;
+}
+//! lower bytecode GOTO_16
+
+//!
+int op_goto_16() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto(tmp);
+    rPC += 2;
+    return retval;
+}
+//! lower bytecode GOTO_32
+
+//!
+int op_goto_32() {
+    s2 tmp = traceCurrentBB->taken->id;
+    int retval = common_goto((s4)tmp);
+    rPC += 3;
+    return retval;
+}
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode PACKED_SWITCH
+
+//!
+int op_packed_switch() {
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    u2 vA = INST_AA(inst);
+
+#ifdef DEBUG_EACH_BYTECODE
+    u2 tSize = 0;
+    s4 firstKey = 0;
+    s4* entries = NULL;
+#else
+    u2* switchData = rPC + (s4)tmp;
+    if (*switchData++ != kPackedSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError(
+                          "bad packed switch magic");
+        return 0; //no_op
+    }
+    u2 tSize = *switchData++;
+    assert(tSize > 0);
+    s4 firstKey = *switchData++;
+    firstKey |= (*switchData++) << 16;
+    s4* entries = (s4*) switchData;
+    assert(((u4)entries & 0x3) == 0);
+#endif
+
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //dvmNcgHandlePackedSwitch: testVal, size, first_key, targets
+    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tSize, 8, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, firstKey, 4, PhysicalReg_ESP, true);
+
+    /* "entries" is constant for JIT
+       it is the 1st argument to dvmJitHandlePackedSwitch */
+    move_imm_to_mem(OpndSize_32, (int)entries, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 12, PhysicalReg_ESP, true);
+
+    //if value out of range, fall through (no_op)
+    //return targets[testVal - first_key]
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    call_dvmJitHandlePackedSwitch();
+    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //TODO: eax should be absolute address, call globalVREndOfBB, constVREndOfBB
+    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod); //update GG VRs
+    //get rPC, %eax has the relative PC offset
+    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+#if defined(WITH_JIT_TUNING)
+    /* Fall back to interpreter after resolving address of switch target.
+     * Indicate a kSwitchOverflow. Note: This is not an "overflow". But it helps
+     * count the times we return from a Switch
+     */
+    move_imm_to_mem(OpndSize_32, kSwitchOverflow, 0, PhysicalReg_ESP, true);
+#endif
+    jumpToInterpNoChain();
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode SPARSE_SWITCH
+
+//!
+int op_sparse_switch() {
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    u2 vA = INST_AA(inst);
+#ifdef DEBUG_EACH_BYTECODE
+    u2 tSize = 0;
+    const s4* keys = NULL;
+    s4* entries = NULL;
+#else
+    u2* switchData = rPC + (s4)tmp;
+
+    if (*switchData++ != kSparseSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError(
+                          "bad sparse switch magic");
+        return 0; //no_op
+    }
+    u2 tSize = *switchData++;
+    assert(tSize > 0);
+    const s4* keys = (const s4*) switchData;
+    assert(((u4)keys & 0x3) == 0);
+    s4* entries = (s4*)switchData + tSize;
+    assert(((u4)entries & 0x3) == 0);
+#endif
+
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //dvmNcgHandleSparseSwitch: keys, size, testVal
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, tSize, 4, PhysicalReg_ESP, true);
+
+    /* "keys" is constant for JIT
+       it is the 1st argument to dvmJitHandleSparseSwitch */
+    move_imm_to_mem(OpndSize_32, (int)keys, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 8, PhysicalReg_ESP, true);
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    //if testVal is in keys, return the corresponding target
+    //otherwise, fall through (no_op)
+    call_dvmJitHandleSparseSwitch();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //TODO: eax should be absolute address, call globalVREndOfBB constVREndOfBB
+    //conditional_jump_global_API(Condition_LE, "common_backwardBranch", false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    //get rPC, %eax has the relative PC offset
+    alu_binary_imm_reg(OpndSize_32, add_opc, (int)rPC, PhysicalReg_EAX, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+#if defined(WITH_JIT_TUNING)
+    /* Fall back to interpreter after resolving address of switch target.
+     * Indicate a kSwitchOverflow. Note: This is not an "overflow". But it helps
+     * count the times we return from a Switch
+     */
+    move_imm_to_mem(OpndSize_32, kSwitchOverflow, 0, PhysicalReg_ESP, true);
+#endif
+    jumpToInterpNoChain();
+    rPC += 3;
+    return 0;
+}
+
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode IF_EQ
+
+//!
+int op_if_eq() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_NE, Condition_E);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_NE
+
+//!
+int op_if_ne() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_E, Condition_NE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LT
+
+//!
+int op_if_lt() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_GE, Condition_L);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GE
+
+//!
+int op_if_ge() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_L, Condition_GE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GT
+
+//!
+int op_if_gt() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_LE, Condition_G);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LE
+
+//!
+int op_if_le() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    s2 tmp = (s2)FETCH(1);
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    compare_VR_reg(OpndSize_32, vB, 1, false);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_G, Condition_LE);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+//! lower bytecode IF_EQZ
+
+//!
+int op_if_eqz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_NE, Condition_E);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_NEZ
+
+//!
+int op_if_nez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_E, Condition_NE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LTZ
+
+//!
+int op_if_ltz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_GE, Condition_L);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GEZ
+
+//!
+int op_if_gez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_L, Condition_GE);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_GTZ
+
+//!
+int op_if_gtz() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_LE, Condition_G);
+    rPC += 2;
+    return 0;
+}
+//! lower bytecode IF_LEZ
+
+//!
+int op_if_lez() {
+    u2 vA = INST_AA(inst);
+    s2 tmp = (s2)FETCH(1);
+    compare_imm_VR(OpndSize_32,
+                                  0, vA);
+    constVREndOfBB();
+    globalVREndOfBB(currentMethod);
+    common_if(tmp, Condition_G, Condition_LE);
+    rPC += 2;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_ECX
+#define P_GPR_2 PhysicalReg_EBX
+/*!
+\brief helper function common_periodicChecks4 to check GC request
+BCOffset in %edx
+*/
+int common_periodicChecks4() {
+    insertLabel("common_periodicChecks4", false);
+#if (!defined(ENABLE_TRACING))
+    get_self_pointer(PhysicalReg_ECX, true);
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, suspendCount), PhysicalReg_ECX, true, PhysicalReg_EAX, true);
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend4", true); //called once
+    x86_return();
+
+    insertLabel("common_handleSuspend4", true);
+    push_reg_to_stack(OpndSize_32, PhysicalReg_ECX, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+
+#else
+    ///////////////////
+    //get debuggerActive: 3 memory accesses, and $7
+    move_mem_to_reg(OpndSize_32, offGlue_pSelfSuspendCount, PhysicalReg_Glue, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, offGlue_pIntoDebugger, PhysicalReg_Glue, true, P_GPR_2, true);
+
+    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_1, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend4_1", true); //called once
+
+    compare_imm_mem(OpndSize_32, 0, 0, P_GPR_2, true); //debugger active
+
+    conditional_jump(Condition_NE, "common_debuggerActive4", true);
+
+    //recover registers and return
+    x86_return();
+
+    insertLabel("common_handleSuspend4_1", true);
+    push_mem_to_stack(OpndSize_32, offGlue_self, PhysicalReg_Glue, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+
+    insertLabel("common_debuggerActive4", true);
+    //%edx: offsetBC (at run time, get method->insns_bytecode, then calculate BCPointer)
+    move_mem_to_reg(OpndSize_32, offGlue_method, PhysicalReg_Glue, true, P_GPR_1, true);
+    move_mem_to_reg(OpndSize_32, offMethod_insns_bytecode, P_GPR_1, true, P_GPR_2, true);
+    alu_binary_reg_reg(OpndSize_32, add_opc, P_GPR_2, true, PhysicalReg_EDX, true);
+    move_imm_to_mem(OpndSize_32, 0, offGlue_entryPoint, PhysicalReg_Glue, true);
+    unconditional_jump("common_gotoBail", false); //update glue->rPC with edx
+#endif
+    return 0;
+}
+//input: %edx PC adjustment
+//CHECK: should %edx be saved before calling dvmCheckSuspendPending?
+/*!
+\brief helper function common_periodicChecks_entry to check GC request
+
+*/
+int common_periodicChecks_entry() {
+    insertLabel("common_periodicChecks_entry", false);
+    scratchRegs[0] = PhysicalReg_ESI; scratchRegs[1] = PhysicalReg_EAX;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_suspendCount(P_GPR_1, true);
+
+    //get debuggerActive: 3 memory accesses, and $7
+#if 0 //defined(WITH_DEBUGGER)
+    get_debuggerActive(P_GPR_2, true);
+#endif
+
+    compare_imm_reg(OpndSize_32, 0, P_GPR_1, true); //suspendCount
+    conditional_jump(Condition_NE, "common_handleSuspend", true); //called once
+
+#if 0 //defined(WITH_DEBUGGER)
+#ifdef NCG_DEBUG
+    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true); //debugger active
+    conditional_jump(Condition_NE, "common_debuggerActive", true);
+#endif
+#endif
+
+    //recover registers and return
+    x86_return();
+    insertLabel("common_handleSuspend", true);
+    get_self_pointer(P_GPR_1, true);
+    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
+    call_dvmCheckSuspendPending();
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+#ifdef NCG_DEBUG
+    insertLabel("common_debuggerActive", true);
+    //adjust PC!!! use 0(%esp) TODO
+    set_glue_entryPoint_imm(0); //kInterpEntryInstr);
+    unconditional_jump("common_gotoBail", false);
+#endif
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+/*!
+\brief helper function common_gotoBail
+  input: %edx: BCPointer %esi: Glue
+  set %eax to 1 (switch interpreter = true), recover the callee-saved registers and return
+*/
+int common_gotoBail() {
+    insertLabel("common_gotoBail", false);
+    //scratchRegs[0] = PhysicalReg_EDX; scratchRegs[1] = PhysicalReg_ESI;
+    //scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    //save_pc_fp_to_glue();
+    get_self_pointer(PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
+
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
+    move_imm_to_reg(OpndSize_32, 1, PhysicalReg_EAX, true); //return value
+    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
+    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
+    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+    return 0;
+}
+/*!
+\brief helper function common_gotoBail_0
+
+  set %eax to 0, recover the callee-saved registers and return
+*/
+int common_gotoBail_0() {
+    insertLabel("common_gotoBail_0", false);
+
+    get_self_pointer(PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offsetof(Thread, interpSave.curFrame), PhysicalReg_EAX, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offsetof(Thread, interpSave.pc), PhysicalReg_EAX, true);
+
+    /*
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+    */
+    move_mem_to_reg(OpndSize_32, offsetof(Thread, interpSave.bailPtr), PhysicalReg_EAX, true, PhysicalReg_ESP, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(FRAME_SIZE-4, PhysicalReg_EBP, true, PhysicalReg_EBP, true);
+    move_imm_to_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //return value
+    move_mem_to_reg(OpndSize_32, -4, PhysicalReg_EBP, true, PhysicalReg_EDI, true);
+    move_mem_to_reg(OpndSize_32, -8, PhysicalReg_EBP, true, PhysicalReg_ESI, true);
+    move_mem_to_reg(OpndSize_32, -12, PhysicalReg_EBP, true, PhysicalReg_EBX, true);
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBP, true, PhysicalReg_ESP, true);
+    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EBP, true);
+    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    x86_return();
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/LowerMove.cpp b/vm/compiler/codegen/x86/LowerMove.cpp
new file mode 100644
index 0000000..2f0b5bc
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerMove.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerMove.cpp
+    \brief This file lowers the following bytecodes: MOVE_XXX
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+
+#define P_GPR_1 PhysicalReg_EBX
+//! lower bytecode MOVE
+
+//!
+int op_move() {
+    u2 vA, vB;
+    vA = INST_A(inst);
+    vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_32, 1, false/*isPhysical*/);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 2;
+}
+//! lower bytecode MOVE_FROM16
+
+//!
+int op_move_from16() {
+    u2 vA, vB;
+    vA = INST_AA(inst);
+    vB = FETCH(1);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode MOVE_16
+
+//!
+int op_move_16() {
+    u2 vA, vB;
+    vA = FETCH(1);
+    vB = FETCH(2);
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 3;
+    return 2;
+}
+#undef P_GPR_1
+//! lower bytecode MOVE_WIDE
+
+//!
+int op_move_wide() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 2;
+}
+//! lower bytecode MOVE_WIDE_FROM16
+
+//!
+int op_move_wide_from16() {
+    u2 vA = INST_AA(inst);
+    u2 vB = FETCH(1);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 2;
+    return 2;
+}
+//! lower bytecode MOVE_WIDE_16
+
+//!
+int op_move_wide_16() {
+    u2 vA = FETCH(1);
+    u2 vB = FETCH(2);
+    get_virtual_reg(vB, OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 3;
+    return 2;
+}
+//! lower bytecode MOVE_RESULT.
+
+//! the return value from bytecode INVOKE is stored in the glue structure
+int op_move_result() {
+#ifdef WITH_JIT_INLINING
+    /* An inlined move result is effectively no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return 0;
+#endif
+    u2 vA = INST_AA(inst);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_return_value(OpndSize_32, 1, false);
+    set_virtual_reg(vA, OpndSize_32, 1, false);
+    rPC += 1;
+    return 0;
+}
+//! lower bytecode MOVE_RESULT_WIDE.
+
+//! the return value from bytecode INVOKE is stored in the glue structure
+int op_move_result_wide() {
+#ifdef WITH_JIT_INLINING
+    /* An inlined move result is effectively no-op */
+    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
+        return 0;
+#endif
+    u2 vA = INST_AA(inst);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_return_value(OpndSize_64, 1, false);
+    set_virtual_reg(vA, OpndSize_64, 1, false);
+    rPC += 1;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//!lower bytecode MOVE_RESULT_EXCEPTION
+
+//!update a virtual register with exception from glue structure;
+//!clear the exception from glue structure
+int op_move_exception() {
+    u2 vA = INST_AA(inst);
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_Null;
+    get_self_pointer(2, false);
+    move_mem_to_reg(OpndSize_32, offThread_exception, 2, false, 3, false);
+    move_imm_to_mem(OpndSize_32, 0, offThread_exception, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 3, false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
diff --git a/vm/compiler/codegen/x86/LowerObject.cpp b/vm/compiler/codegen/x86/LowerObject.cpp
new file mode 100644
index 0000000..67c4044
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerObject.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*! \file LowerObject.cpp
+    \brief This file lowers the following bytecodes: CHECK_CAST,
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "Lower.h"
+#include "NcgAot.h"
+#include "enc_wrapper.h"
+
+extern void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical);
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! LOWER bytecode CHECK_CAST and INSTANCE_OF
+//!   CALL class_resolve (%ebx is live across the call)
+//!        dvmInstanceofNonTrivial
+//!   NO register is live through function check_cast_helper
+int check_cast_nohelper(u2 vA, u4 tmp, bool instance, u2 vDest) {
+    get_virtual_reg(vA, OpndSize_32, 1, false); //object
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    /* for trace-based JIT, it is likely that the class is already resolved */
+    bool needToResolve = true;
+    ClassObject *classPtr =
+                (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    ALOGV("in check_cast, class is resolved to %p", classPtr);
+    if(classPtr != NULL) {
+        needToResolve = false;
+        ALOGV("check_cast class %s", classPtr->descriptor);
+    }
+    if(needToResolve) {
+        //get_res_classes is moved here for NCG O1 to improve performance of GLUE optimization
+        scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+        get_res_classes(4, false);
+    }
+    compare_imm_reg(OpndSize_32, 0, 1, false);
+
+    rememberState(1);
+    //for private code cache, previously it jumped to .instance_of_okay_1
+    //if object reference is null, jump to the handler for this special case
+    if(instance) {
+        conditional_jump(Condition_E, ".instance_of_null", true);
+    }
+    else {
+        conditional_jump(Condition_E, ".check_cast_null", true);
+    }
+    //check whether the class is already resolved
+    //if yes, jump to check_cast_resolved
+    //if not, call class_resolve
+    if(needToResolve) {
+        move_mem_to_reg(OpndSize_32, tmp*4, 4, false, PhysicalReg_EAX, true);
+        compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+        if(instance)
+            conditional_jump(Condition_NE, ".instance_of_resolved", true);
+        else
+            conditional_jump(Condition_NE, ".check_cast_resolved", true);
+        //try to resolve the class
+        rememberState(2);
+        move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
+        export_pc(); //trying to resolve the class
+        call_helper_API(".class_resolve");
+        transferToState(2);
+    } //needToResolve
+    else {
+        /* the class is already resolved and is constant */
+        move_imm_to_reg(OpndSize_32, (int)classPtr, PhysicalReg_EAX, true);
+    }
+    //class is resolved, and it is in %eax
+    if(!instance) {
+        insertLabel(".check_cast_resolved", true);
+    }
+    else insertLabel(".instance_of_resolved", true);
+
+    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false); //object->clazz
+
+    //%eax: resolved class
+    //compare resolved class and object->clazz
+    //if the same, jump to the handler for this special case
+    compare_reg_reg(PhysicalReg_EAX, true, 6, false);
+    rememberState(3);
+    if(instance) {
+        conditional_jump(Condition_E, ".instance_of_equal", true);
+    } else {
+        conditional_jump(Condition_E, ".check_cast_equal", true);
+    }
+
+    //prepare to call dvmInstanceofNonTrivial
+    //INPUT: the resolved class & object reference
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 6, false, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true); //resolved class
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 2); //next version has 2 refs
+    call_dvmInstanceofNonTrivial();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //
+    if(instance) {
+        //move return value to P_GPR_2
+        move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 3, false);
+        rememberState(4);
+        unconditional_jump(".instance_of_okay", true);
+    } else {
+        //if return value of dvmInstanceofNonTrivial is zero, throw exception
+        compare_imm_reg(OpndSize_32, 0,  PhysicalReg_EAX, true);
+        rememberState(4);
+        conditional_jump(Condition_NE, ".check_cast_okay", true);
+        //two inputs for common_throw_message: object reference in eax, exception pointer in ecx
+        nextVersionOfHardReg(PhysicalReg_EAX, 1); //next version has 1 ref
+        move_reg_to_reg(OpndSize_32, 1, false, PhysicalReg_EAX, true);
+
+        load_imm_global_data_API("strClassCastExceptionPtr", OpndSize_32, PhysicalReg_ECX, true);
+
+        nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
+        export_pc();
+
+        unconditional_jump_global_API("common_throw_message", false);
+    }
+    //handler for speical case where object reference is null
+    if(instance)
+        insertLabel(".instance_of_null", true);
+    else insertLabel(".check_cast_null", true);
+    goToState(1);
+    if(instance) {
+        move_imm_to_reg(OpndSize_32, 0, 3, false);
+    }
+    transferToState(4);
+    if(instance)
+        unconditional_jump(".instance_of_okay", true);
+    else
+        unconditional_jump(".check_cast_okay", true);
+
+    //handler for special case where class of object is the same as the resolved class
+    if(instance)
+        insertLabel(".instance_of_equal", true);
+    else insertLabel(".check_cast_equal", true);
+    goToState(3);
+    if(instance) {
+        move_imm_to_reg(OpndSize_32, 1, 3, false);
+    }
+    transferToState(4);
+    if(instance)
+        insertLabel(".instance_of_okay", true);
+    else insertLabel(".check_cast_okay", true);
+    //all cases merge here and the value is put to virtual register
+    if(instance) {
+        set_virtual_reg(vDest, OpndSize_32, 3, false);
+    }
+    return 0;
+}
+//! common code to lower CHECK_CAST & INSTANCE_OF
+
+//!
+int common_check_cast_instance_of(u2 vA, u4 tmp, bool instance, u2 vDest) {
+    return check_cast_nohelper(vA, tmp, instance, vDest);
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+//! LOWER bytecode CHECK_CAST
+
+//!
+int op_check_cast() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    common_check_cast_instance_of(vA, tmp, false, 0);
+    rPC += 2;
+    return 0;
+}
+//!LOWER bytecode INSTANCE_OF
+
+//!
+int op_instance_of() {
+    u2 vB = INST_B(inst);
+    u2 vA = INST_A(inst);
+    u4 tmp = (u4)FETCH(1);
+    common_check_cast_instance_of(vB, tmp, true, vA);
+    rPC += 2;
+    return 0;
+}
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! LOWER bytecode MONITOR_ENTER without usage of helper function
+
+//!   CALL dvmLockObject
+int monitor_enter_nohelper(u2 vA) {
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+
+    requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    //get_self_pointer is separated
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //to optimize redundant null check, NCG O1 wraps up null check in a function: nullCheck
+    get_self_pointer(3, false);
+    nullCheck(1, false, 1, vA); //maybe optimized away
+    cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
+
+    /////////////////////////////
+    //prepare to call dvmLockObject, inputs: object reference and self
+    // TODO: Should reset inJitCodeCache before calling dvmLockObject
+    //       so that code cache can be reset if needed when locking object
+    //       taking a long time. Not resetting inJitCodeCache may delay
+    //       code cache reset when code cache is full, preventing traces from
+    //       JIT compilation. This has performance implication.
+    //       However, after resetting inJitCodeCache, the code should be
+    //       wrapped in a helper instead of directly inlined in code cache.
+    //       If the code after dvmLockObject call is in code cache and the code
+    //       cache is reset during dvmLockObject call, execution after
+    //       dvmLockObject will return to a cleared code cache region,
+    //       resulting in seg fault.
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 3, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmLockObject();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /////////////////////////////
+    return 0;
+}
+//! lower bytecode MONITOR_ENTER
+
+//! It will use helper function if switch is on
+int op_monitor_enter() {
+    u2 vA = INST_AA(inst);
+    export_pc();
+    monitor_enter_nohelper(vA);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+//! lower bytecode MONITOR_EXIT
+
+//! It will use helper function if switch is on
+int op_monitor_exit() {
+    u2 vA = INST_AA(inst);
+    ////////////////////
+    //LOWER bytecode MONITOR_EXIT without helper function
+    //   CALL dvmUnlockObject
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vA); //maybe optimized away
+    cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
+
+    /////////////////////////////
+    //prepare to call dvmUnlockObject, inputs: object reference and self
+    push_reg_to_stack(OpndSize_32, 1, false);
+    push_mem_to_stack(OpndSize_32, offEBP_self, PhysicalReg_EBP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmUnlockObject();
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    conditional_jump(Condition_NE, ".unlock_object_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".unlock_object_done", true);
+    ///////////////////////////
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX /*vA*/
+//! LOWER bytecode ARRAY_LENGTH
+
+//! It will use helper function if switch is on
+int op_array_length() {
+    u2 vA = INST_A(inst);
+    u2 vB = INST_B(inst);
+    ////////////////////
+    //no usage of helper function
+    requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
+    get_virtual_reg(vB, OpndSize_32, 1, false);
+    nullCheck(1, false, 1, vB); //maybe optimized away
+    cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
+
+    move_mem_to_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false);
+    set_virtual_reg(vA, OpndSize_32, 2, false);
+    ///////////////////////
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! lower bytecode NEW_INSTANCE
+
+//! It will use helper function if switch is on
+int op_new_instance() {
+    u4 tmp = (u4)FETCH(1);
+    u2 vA = INST_AA(inst);
+    export_pc();
+    /* for trace-based JIT, class is already resolved */
+    ClassObject *classPtr =
+        (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    assert(classPtr->status & CLASS_INITIALIZED);
+    /*
+     * If it is going to throw, it should not make to the trace to begin
+     * with.  However, Alloc might throw, so we need to genExportPC()
+     */
+    assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+    //prepare to call dvmAllocObject, inputs: resolved class & flag ALLOC_DONT_TRACK
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /* 1st argument to dvmAllocObject at -8(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
+    call_dvmAllocObject();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //return value of dvmAllocObject is in %eax
+    //if return value is null, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".new_instance_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    jumpToExceptionThrown(3/*exception number*/);
+    insertLabel(".new_instance_done", true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    rPC += 2;
+    return 0;
+}
+
+//! function to initialize a class
+
+//!INPUT: %eax (class object) %eax is recovered before return
+//!OUTPUT: none
+//!CALL: dvmInitClass
+//!%eax, %esi, %ebx are live through function new_instance_needinit
+int new_instance_needinit() {
+    insertLabel(".new_instance_needinit", false);
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_ECX;
+    call_dvmInitClass();
+    //if return value of dvmInitClass is zero, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    //recover EAX with the class object
+    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, PhysicalReg_EAX, true);
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    conditional_jump(Condition_E, "common_exceptionThrown", false);
+    x86_return();
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX //live through C function, must in callee-saved reg
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_EDX
+//! lower bytecode NEW_ARRAY
+
+//! It will use helper function if switch is on
+int op_new_array() {
+    u4 tmp = (u4)FETCH(1);
+    u2 vA = INST_A(inst); //destination
+    u2 vB = INST_B(inst); //length
+    /////////////////////////
+    //   REGS used: %esi, %eax, P_GPR_1, P_GPR_2
+    //   CALL class_resolve, dvmAllocArrayByClass
+    export_pc(); //use %edx
+    //check size of the array, if negative, throw exception
+    get_virtual_reg(vB, OpndSize_32, 5, false);
+    compare_imm_reg(OpndSize_32, 0, 5, false);
+    handlePotentialException(Condition_S, Condition_NS,
+                             1, "common_errNegArraySize");
+    void *classPtr = (void*)
+        (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    assert(classPtr != NULL);
+    //here, class is already resolved, the class object is in %eax
+    //prepare to call dvmAllocArrayByClass with inputs: resolved class, array length, flag ALLOC_DONT_TRACK
+    insertLabel(".new_array_resolved", true);
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    /* 1st argument to dvmAllocArrayByClass at 0(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 5, false, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3;
+    nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
+    call_dvmAllocArrayByClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //the allocated object is in %eax
+    //check whether it is null, throw exception if null
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".new_array_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".new_array_done", true);
+    set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
+    //////////////////////////////////////
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+
+#define P_GPR_1 PhysicalReg_EBX
+#define P_GPR_2 PhysicalReg_ECX
+#define P_GPR_3 PhysicalReg_ESI
+//! common code to lower FILLED_NEW_ARRAY
+
+//! call: class_resolve call_dvmAllocPrimitiveArray
+//! exception: filled_new_array_notimpl common_exceptionThrown
+int common_filled_new_array(u2 length, u4 tmp, bool hasRange) {
+    ClassObject *classPtr =
+              (currentMethod->clazz->pDvmDex->pResClasses[tmp]);
+    if(classPtr != NULL) ALOGI("FILLED_NEW_ARRAY class %s", classPtr->descriptor);
+    //check whether class is resolved, if yes, jump to resolved
+    //if not, call class_resolve
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_res_classes(3, false);
+    move_mem_to_reg(OpndSize_32, tmp*4, 3, false, PhysicalReg_EAX, true);
+    export_pc();
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //resolved class
+    conditional_jump(Condition_NE, ".filled_new_array_resolved", true);
+    rememberState(1);
+    move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
+    call_helper_API(".class_resolve");
+    transferToState(1);
+    //here, class is already resolved
+    insertLabel(".filled_new_array_resolved", true);
+    //check descriptor of the class object, if not implemented, throws exception
+    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_EAX, true, 5, false);
+    //load a single byte of the descriptor
+    movez_mem_to_reg(OpndSize_8, 1, 5, false, 6, false);
+    compare_imm_reg(OpndSize_32, 'I', 6, false);
+    conditional_jump(Condition_E, ".filled_new_array_impl", true);
+    compare_imm_reg(OpndSize_32, 'L', 6, false);
+    conditional_jump(Condition_E, ".filled_new_array_impl", true);
+    compare_imm_reg(OpndSize_32, '[', 6, false);
+    conditional_jump(Condition_NE, ".filled_new_array_notimpl", false);
+
+    insertLabel(".filled_new_array_impl", true);
+    //prepare to call dvmAllocArrayByClass with inputs: classObject, length, flag ALLOC_DONT_TRACK
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, length, 4, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
+    if(hasRange) {
+        nextVersionOfHardReg(PhysicalReg_EAX, 5+(length >= 1 ? LOOP_COUNT : 0)); //next version
+    }
+    else {
+        nextVersionOfHardReg(PhysicalReg_EAX, 5+length); //next version
+    }
+    call_dvmAllocArrayByClass();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    //return value of dvmAllocPrimitiveArray is in %eax
+    //if the return value is null, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    handlePotentialException(
+                                       Condition_E, Condition_NE,
+                                       3, "common_exceptionThrown");
+
+    /* we need to mark the card of the new array, if it's not an int */
+    compare_imm_reg(OpndSize_32, 'I', 6, false);
+    conditional_jump(Condition_E, ".dont_mark_filled_new_array", true);
+
+    // Need to make copy of EAX, because it's used later in op_filled_new_array()
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 6, false);
+
+    markCard_filled(6, false, PhysicalReg_SCRATCH_4, false);
+
+    insertLabel(".dont_mark_filled_new_array", true);
+
+    //return value of bytecode FILLED_NEW_ARRAY is in GLUE structure
+    scratchRegs[0] = PhysicalReg_SCRATCH_4; scratchRegs[1] = PhysicalReg_Null;
+    set_return_value(OpndSize_32, PhysicalReg_EAX, true);
+    return 0;
+}
+//! LOWER bytecode FILLED_NEW_ARRAY
+
+//!
+int op_filled_new_array() {
+    u2 length = INST_B(inst);
+    u4 tmp = (u4)FETCH(1);
+    u2 v5 = INST_A(inst);
+    u2 vv = FETCH(2);
+    u2 v1 = vv & 0xf;
+    u2 v2 = (vv >> 4) & 0xf;
+    u2 v3 = (vv >> 8) & 0xf;
+    u2 v4 = (vv >> 12) & 0xf;
+    common_filled_new_array(length, tmp, false);
+    if(length >= 1) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v1, OpndSize_32, 7, false);
+        move_reg_to_mem(OpndSize_32, 7, false, offArrayObject_contents, PhysicalReg_EAX, true);
+    }
+    if(length >= 2) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v2, OpndSize_32, 8, false);
+        move_reg_to_mem(OpndSize_32, 8, false, offArrayObject_contents+4, PhysicalReg_EAX, true);
+    }
+    if(length >= 3) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v3, OpndSize_32, 9, false);
+        move_reg_to_mem(OpndSize_32, 9, false, offArrayObject_contents+8, PhysicalReg_EAX, true);
+    }
+    if(length >= 4) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v4, OpndSize_32, 10, false);
+        move_reg_to_mem(OpndSize_32, 10, false, offArrayObject_contents+12, PhysicalReg_EAX, true);
+    }
+    if(length >= 5) {
+        //move from virtual register to contents of array object
+        get_virtual_reg(v5, OpndSize_32, 11, false);
+        move_reg_to_mem(OpndSize_32, 11, false, offArrayObject_contents+16, PhysicalReg_EAX, true);
+    }
+    rPC += 3;
+    return 0;
+}
+//! function to handle the error of array not implemented
+
+//!
+int filled_new_array_notimpl() {
+    //two inputs for common_throw:
+    insertLabel(".filled_new_array_notimpl", false);
+    move_imm_to_reg(OpndSize_32, LstrFilledNewArrayNotImpl, PhysicalReg_EAX, true);
+    move_imm_to_reg(OpndSize_32, (int) gDvm.exInternalError, PhysicalReg_ECX, true);
+    unconditional_jump("common_throw", false);
+    return 0;
+}
+
+#define P_SCRATCH_1 PhysicalReg_EDX
+//! LOWER bytecode FILLED_NEW_ARRAY_RANGE
+
+//!
+int op_filled_new_array_range() {
+    u2 length = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    u4 vC = (u4)FETCH(2);
+    common_filled_new_array(length, tmp, true/*hasRange*/);
+    //here, %eax points to the array object
+    if(length >= 1) {
+        //dump all virtual registers used by this bytecode to stack, for NCG O1
+        int k;
+        for(k = 0; k < length; k++) {
+            spillVirtualReg(vC+k, LowOpndRegType_gp, true); //will update refCount
+        }
+        //address of the first virtual register that will be moved to the array object
+        load_effective_addr(vC*4, PhysicalReg_FP, true, 7, false); //addr
+        //start address for contents of the array object
+        load_effective_addr(offArrayObject_contents, PhysicalReg_EAX, true, 8, false); //addr
+        //loop counter
+        move_imm_to_reg(OpndSize_32, length-1, 9, false); //counter
+        //start of the loop
+        insertLabel(".filled_new_array_range_loop1", true);
+        rememberState(1);
+        move_mem_to_reg(OpndSize_32, 0, 7, false, 10, false);
+        load_effective_addr(4, 7, false, 7, false);
+        move_reg_to_mem(OpndSize_32, 10, false, 0, 8, false);
+        load_effective_addr(4, 8, false, 8, false);
+        alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 9, false);
+        transferToState(1);
+        //jump back to the loop start
+        conditional_jump(Condition_NS, ".filled_new_array_range_loop1", true);
+    }
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_GPR_3
+#undef P_SCRATCH_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode FILL_ARRAY_DATA
+
+//!use 1 GPR and scratch regs (export_pc dvmInterpHandleFillArrayData)
+//!CALL: dvmInterpHandleFillArrayData
+int op_fill_array_data() {
+    u2 vA = INST_AA(inst);
+    u4 tmp = (u4)FETCH(1);
+    tmp |= (u4)FETCH(2) << 16;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //prepare to call dvmInterpHandleFillArrayData, input: array object, address of the data
+    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    /* 2nd argument to dvmInterpHandleFillArrayData at 4(%esp) */
+    move_imm_to_mem(OpndSize_32, (int)(rPC+tmp), 4, PhysicalReg_ESP, true);
+    call_dvmInterpHandleFillArrayData();
+    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    //check return value of dvmInterpHandleFillArrayData, if zero, throw exception
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
+    conditional_jump(Condition_NE, ".fill_array_data_done", true);
+    //jump to dvmJitToExceptionThrown
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    jumpToExceptionThrown(2/*exception number*/);
+    insertLabel(".fill_array_data_done", true);
+    rPC += 3;
+    return 0;
+}
+#undef P_GPR_1
+
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode THROW
+
+//!
+int op_throw() {
+    u2 vA = INST_AA(inst);
+    export_pc();
+    get_virtual_reg(vA, OpndSize_32, 1, false);
+    //null check
+    compare_imm_reg(OpndSize_32, 0, 1, false);
+    conditional_jump(Condition_E, "common_errNullObject", false);
+    //set glue->exception & throw exception
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
+    set_exception(1, false);
+    unconditional_jump("common_exceptionThrown", false);
+    rPC += 1;
+    return 0;
+}
+#undef P_GPR_1
+#define P_GPR_1 PhysicalReg_EBX
+//! LOWER bytecode THROW_VERIFICATION_ERROR
+
+//! op AA, ref@BBBB
+int op_throw_verification_error() {
+    u2 vA, vB;
+    vA = INST_AA(inst);
+    vB = FETCH(1);
+
+    export_pc();
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    get_glue_method(1, false);
+
+    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, vB, 8, PhysicalReg_ESP, true);
+    move_imm_to_mem(OpndSize_32, vA, 4, PhysicalReg_ESP, true);
+    move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_2;
+    call_dvmThrowVerificationError();
+    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
+
+    unconditional_jump("common_exceptionThrown", false);
+    rPC += 2;
+    return 0;
+}
+#undef P_GPR_1
diff --git a/vm/compiler/codegen/x86/LowerReturn.cpp b/vm/compiler/codegen/x86/LowerReturn.cpp
new file mode 100644
index 0000000..294d6b5
--- /dev/null
+++ b/vm/compiler/codegen/x86/LowerReturn.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/*! \file LowerReturn.cpp
+    \brief This file lowers the following bytecodes: RETURN
+
+*/
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexFile.h"
+#include "mterp/Mterp.h"
+#include "Lower.h"
+#include "enc_wrapper.h"
+#include "NcgHelper.h"
+
+//4 GPRs and scratch registers used in get_self_pointer, set_glue_method and set_glue_dvmdex
+//will jump to "gotoBail" if caller method is NULL or if debugger is active
+//what is %edx for each case? for the latter case, it is 1
+#define P_GPR_1 PhysicalReg_ECX //must be ecx
+#define P_GPR_2 PhysicalReg_EBX
+#define P_SCRATCH_1 PhysicalReg_EDX
+#define P_OLD_FP PhysicalReg_EAX
+/*!
+\brief common section to return from a method
+
+If the helper switch is on, this will generate a helper function
+*/
+int common_returnFromMethod() {
+#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
+    insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1); //check when helper switch is on
+#endif
+
+    scratchRegs[0] = PhysicalReg_SCRATCH_7;
+    get_self_pointer(2, false);
+
+    //update rFP to caller stack frame
+    move_reg_to_reg(OpndSize_32, PhysicalReg_FP, true, 10, false);
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_prevFrame, PhysicalReg_FP, true, PhysicalReg_FP, true); //update rFP
+    //get caller method by accessing the stack save area
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_method, PhysicalReg_FP, true, 6, false);
+    compare_imm_reg(OpndSize_32, 0, 6, false);
+    conditional_jump(Condition_E, "common_gotoBail_0", false);
+    get_self_pointer(3, false);
+    //update glue->method
+    move_reg_to_mem(OpndSize_32, 6, false, offsetof(Thread, interpSave.method), 2, false);
+    //get clazz of caller method
+    move_mem_to_reg(OpndSize_32, offMethod_clazz, 6, false, 14, false);
+    //update self->frame
+    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, 3, false);
+    //get method->clazz->pDvmDex
+    move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, 14, false, 7, false);
+    move_reg_to_mem(OpndSize_32, 7, false, offsetof(Thread, interpSave.methodClassDex), 2, false);
+
+    compare_imm_mem(OpndSize_32, 0, offsetof(Thread, suspendCount), 2, false); /* suspendCount */
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_returnAddr, 10, false, PhysicalReg_EBX, true);
+    move_imm_to_reg(OpndSize_32, 0, 17, false);
+    /* if suspendCount is not zero, clear the chaining cell address */
+    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 17, false/*src*/, PhysicalReg_EBX, true/*dst*/);
+    move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_savedPc, 10, false, PhysicalReg_EAX, true);
+    //if returnAddr is not NULL, the thread is still in code cache
+    move_reg_to_mem(OpndSize_32, PhysicalReg_EBX, true, offThread_inJitCodeCache, 3, false);
+
+    insertLabel(".LreturnToInterp", true); //local label
+    //move rPC by 6 (3 bytecode units for INVOKE)
+    alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EAX, true);
+
+    //returnAddr in %ebx, if not zero, jump to returnAddr
+    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EBX, true);
+    conditional_jump(Condition_E, ".LcontinueToInterp", true);
+#ifdef DEBUG_CALL_STACK3
+    move_reg_to_reg(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ESI, true);
+    move_imm_to_reg(OpndSize_32, 0xaabb, PhysicalReg_EBX, true);
+    scratchRegs[0] = PhysicalReg_EAX;
+    call_debug_dumpSwitch(); //%ebx, %eax, %edx
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true);
+    call_debug_dumpSwitch();
+    move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true);
+#endif
+    unconditional_jump_reg(PhysicalReg_EBX, true);
+    insertLabel(".LcontinueToInterp", true);
+    scratchRegs[0] = PhysicalReg_SCRATCH_4;
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNoChainNoProfile; //%eax is the input
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+#if defined(WITH_JIT_TUNING)
+    /* Return address not in code cache. Indicate that continuing with interpreter.
+     */
+    move_imm_to_mem(OpndSize_32, kCallsiteInterpreted, 0, PhysicalReg_ESP, true);
+#endif
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    touchEax();
+    return 0;
+}
+#undef P_GPR_1
+#undef P_GPR_2
+#undef P_SCRATCH_1
+
+//! lower bytecode RETURN_VOID
+
+//! It seems that shared code cache does not support helper switch
+int op_return_void() {
+    int retval;
+    retval = common_returnFromMethod();
+    rPC += 1;
+    return retval;
+}
+
+//! lower bytecode RETURN
+
+//! It seems that shared code cache does not support helper switch
+//! The return value is stored to glue->retval first
+int op_return() {
+    u2 vA = INST_AA(inst);
+
+    get_virtual_reg(vA, OpndSize_32, 22, false);
+    scratchRegs[0] = PhysicalReg_SCRATCH_1;
+    set_return_value(OpndSize_32, 22, false);
+
+    common_returnFromMethod();
+    rPC += 1;
+    return 0;
+}
+
+//! lower bytecode RETURN_WIDE
+
+//! It seems that shared code cache does not support helper switch
+//! The return value is stored to glue->retval first
+int op_return_wide() {
+    u2 vA = INST_AA(inst);
+    get_virtual_reg(vA, OpndSize_64, 1, false);
+    scratchRegs[0] = PhysicalReg_SCRATCH_10; scratchRegs[1] = PhysicalReg_Null;
+    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
+    set_return_value(OpndSize_64, 1, false);
+
+    common_returnFromMethod();
+    rPC += 1;
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/NcgAot.cpp b/vm/compiler/codegen/x86/NcgAot.cpp
new file mode 100644
index 0000000..7c29b6d
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgAot.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#include "Lower.h"
+#include "NcgAot.h"
+#include "NcgHelper.h"
+
+//returns # of ops generated by this function
+//entries relocatable: eip + relativePC
+int get_eip_API() {
+    call("ncgGetEIP");//%edx //will push eip to stack
+    return 1;
+}
+#define NEW_EXPORT_PC
+//!update current PC in the stack frame with %eip
+
+//!
+int export_pc() {
+    /* for trace-based JIT, pc points to bytecode
+       for NCG, pc points to native code */
+    move_imm_to_mem(OpndSize_32, (int)rPC,
+                    -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true);
+    return 1; //return number of ops
+}
+
+/* jump from JIT'ed code to interpreter without chaining */
+int jumpToInterpNoChain() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpNoChain;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+/* jump from JIT'ed code to interpreter becaues of exception */
+int jumpToInterpPunt() {
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToInterpPunt;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    //if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+/* jump to common_exceptionThrown from JIT'ed code */
+int jumpToExceptionThrown(int exceptionNum) {
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        rememberState(exceptionNum);
+        export_pc();
+        constVREndOfBB();
+        beforeCall("exception"); //dump GG, GL VRs
+    }
+
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmJitToExceptionThrown;
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+
+    if(gDvm.executionMode == kExecutionModeNcgO1) {
+        goToState(exceptionNum);
+    }
+    return 0;
+}
+
+//! generate native code to call dvmNcgInvokeInterpreter
+
+//!the interpreter will start execution from %eax
+int invokeInterpreter(bool fromApp)
+{
+    typedef void (*vmHelper)(int);
+    vmHelper funcPtr = dvmNcgInvokeInterpreter;
+
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+
+    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
+    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
+    return 0;
+}
+
+//!work to do before calling a function pointer with code cache enabled
+
+//!
+void callFuncPtr(int funcPtr, const char* funcName) {
+
+    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
+    call_reg(C_SCRATCH_1, isScratchPhysical);
+}
+//.const_string_resolve: input in %eax, output in %eax
+//.const_string_helper:
+//.class_resolve: input in %eax, output in %eax
+int call_helper_API(const char* helperName) {
+    call(helperName);
+    return 1;
+}
+
+/* check whether we are throwing an exception */
+bool jumpToException(const char* target) {
+    bool isException = false;
+    if(!strncmp(target, "common_err", 10)) isException = true;
+    if(!strncmp(target, "common_throw", 12)) isException = true;
+    if(!strncmp(target, "common_exception", 16)) isException = true;
+    return isException;
+}
+
+int conditional_jump_global_API(
+                                ConditionCode cc, const char* target,
+                                bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
+        return 1; //return number of ops
+    }
+    conditional_jump(cc, target, isShortTerm);
+    return 1;
+}
+int unconditional_jump_global_API(
+                                  const char* target, bool isShortTerm) {
+    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
+        jumpToBasicBlock(stream, currentExceptionBlockIdx);
+        return 1; //return number of ops
+    }
+    unconditional_jump(target, isShortTerm);
+    return 1;
+}
+int getGlobalDataAddr(const char* dataName) {
+    int dataAddr = -1;
+    if(!strcmp(dataName, "doubNeg")) dataAddr = LdoubNeg;
+    else if(!strcmp(dataName, "intMax")) dataAddr = LintMax;
+    else if(!strcmp(dataName, "intMin")) dataAddr = LintMin;
+    else if(!strcmp(dataName, "valueNanLong")) dataAddr = LvalueNanLong;
+    else if(!strcmp(dataName, "valuePosInfLong")) dataAddr = LvaluePosInfLong;
+    else if(!strcmp(dataName, "valueNegInfLong")) dataAddr = LvalueNegInfLong;
+    else if(!strcmp(dataName, "shiftMask")) dataAddr = LshiftMask;
+    else if(!strcmp(dataName, "value64")) dataAddr = Lvalue64;
+    else if(!strcmp(dataName, "64bits")) dataAddr = L64bits;
+    else if(!strcmp(dataName, "strClassCastExceptionPtr")) dataAddr = LstrClassCastExceptionPtr;
+    else if(!strcmp(dataName, "strInstantiationError")) dataAddr = LstrInstantiationErrorPtr;
+    else if(!strcmp(dataName, "gDvmInlineOpsTable")) dataAddr = (int)gDvmInlineOpsTable;
+    else ALOGE("global data %s not supported", dataName);
+    return dataAddr;
+}
+
+//for shared code cache, we use scratchRegs[0] & [1]
+int load_imm_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_imm_to_reg(size, dataAddr, reg, isPhysical);
+    return 0;
+}
+//for shared code cache, we use scratchRegs[0] & [1] & [2]
+//FIXME: [2] is assumed to be hard-coded register
+int load_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_mem_to_reg(size, dataAddr, PhysicalReg_Null, true, reg, isPhysical);
+    return 0;
+}
+int load_sd_global_data_API(const char* dataName,
+                            int reg, bool isPhysical) {
+
+    //find the address from name
+    int dataAddr = getGlobalDataAddr(dataName);
+    move_sd_mem_to_reg(dataAddr, PhysicalReg_Null, true, reg, isPhysical);
+    return 0;
+}
+
+int load_fp_stack_global_data_API(const char* dataName,
+                                  OpndSize size) {
+
+    int dataAddr = getGlobalDataAddr(dataName);
+    load_int_fp_stack_imm(size, dataAddr); //fildl
+    return 0;
+}
diff --git a/vm/compiler/codegen/x86/NcgAot.h b/vm/compiler/codegen/x86/NcgAot.h
new file mode 100644
index 0000000..99bcc13
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgAot.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+
+#ifndef _DALVIK_NCG_AOT
+#define _DALVIK_NCG_AOT
+int ncgAppGetEIP();
+int get_eip_API();
+int invokeInterpreter(bool fromApp);
+int invokeNcg(bool fromApp);
+int jumpToInterpNoChain();
+int jumpToInterpPunt();
+int jumpToExceptionThrown(int exceptionNum);
+void callFuncPtr(int funcPtr, const char* funcName);
+int call_helper_API(const char* helperName);
+int conditional_jump_global_API(
+                                ConditionCode cc, const char* target,
+                                bool isShortTerm);
+int unconditional_jump_global_API(
+                                  const char* target, bool isShortTerm);
+int load_imm_global_data_API(const char* dataName,
+                             OpndSize size,
+                             int reg, bool isPhysical);
+int load_global_data_API(const char* dataName,
+                         OpndSize size,
+                         int reg, bool isPhysical);
+int load_sd_global_data_API(const char* dataName,
+                            int reg, bool isPhysical);
+int load_fp_stack_global_data_API(const char* dataName,
+                                  OpndSize size);
+#endif
+
diff --git a/vm/compiler/codegen/x86/NcgHelper.cpp b/vm/compiler/codegen/x86/NcgHelper.cpp
new file mode 100644
index 0000000..c603d09
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgHelper.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#include "Dalvik.h"
+#include "NcgHelper.h"
+#include "interp/InterpDefs.h"
+
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the packed-switch
+ * instruction).
+ */
+s4 dvmNcgHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal)
+{
+    //skip add_reg_reg (ADD_REG_REG_SIZE) and jump_reg (JUMP_REG_SIZE)
+    const int kInstrLen = 4; //default to next bytecode
+    if (testVal < firstKey || testVal >= firstKey + size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return kInstrLen;
+    }
+
+    assert(testVal - firstKey >= 0 && testVal - firstKey < size);
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, testVal - firstKey,
+        s4FromSwitchData(&entries[testVal - firstKey]));
+    return s4FromSwitchData(&entries[testVal - firstKey]);
+
+}
+/* return the number of bytes to increase the bytecode pointer by */
+s4 dvmJitHandlePackedSwitch(const s4* entries, s4 firstKey, u2 size, s4 testVal)
+{
+    if (testVal < firstKey || testVal >= firstKey + size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return 2*3;//bytecode packed_switch is 6(2*3) bytes long
+    }
+
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, testVal - firstKey,
+        s4FromSwitchData(&entries[testVal - firstKey]));
+    return 2*s4FromSwitchData(&entries[testVal - firstKey]); //convert from u2 to byte
+
+}
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the sparse-switch
+ * instruction).
+ */
+s4 dvmNcgHandleSparseSwitch(const s4* keys, u2 size, s4 testVal)
+{
+    const int kInstrLen = 4; //CHECK
+    const s4* entries = keys + size;
+    int i;
+    for (i = 0; i < size; i++) {
+        s4 k = s4FromSwitchData(&keys[i]);
+        if (k == testVal) {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, i, s4FromSwitchData(&entries[i]));
+            return s4FromSwitchData(&entries[i]);
+        } else if (k > testVal) {
+            break;
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return kInstrLen;
+}
+/* return the number of bytes to increase the bytecode pointer by */
+s4 dvmJitHandleSparseSwitch(const s4* keys, u2 size, s4 testVal)
+{
+    const s4* entries = keys + size;
+    int i;
+    for (i = 0; i < size; i++) {
+        s4 k = s4FromSwitchData(&keys[i]);
+        if (k == testVal) {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, i, s4FromSwitchData(&entries[i]));
+            return 2*s4FromSwitchData(&entries[i]); //convert from u2 to byte
+        } else if (k > testVal) {
+            break;
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return 2*3; //bytecode sparse_switch is 6(2*3) bytes long
+}
diff --git a/vm/compiler/codegen/x86/NcgHelper.h b/vm/compiler/codegen/x86/NcgHelper.h
new file mode 100644
index 0000000..713f424
--- /dev/null
+++ b/vm/compiler/codegen/x86/NcgHelper.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef _DALVIK_NCG_HELPER
+#define _DALVIK_NCG_HELPER
+#include "mterp/Mterp.h"
+s4 dvmNcgHandlePackedSwitch(const s4*, s4, u2, s4);
+s4 dvmNcgHandleSparseSwitch(const s4*, u2, s4);
+s4 dvmJitHandlePackedSwitch(const s4*, s4, u2, s4);
+s4 dvmJitHandleSparseSwitch(const s4*, u2, s4);
+extern "C" void dvmNcgInvokeInterpreter(int pc); //interpreter to execute at pc
+extern "C" void dvmNcgInvokeNcg(int pc);
+extern "C" void dvmJitToInterpNormal(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpTraceSelect(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpTraceSelectNoChain(int targetpc); //in %ebx
+extern "C" void dvmJitToInterpNoChain(int targetpc); //in %eax
+extern "C" void dvmJitToInterpNoChainNoProfile(int targetpc); //in %eax
+extern "C" void dvmJitToInterpPunt(int targetpc); //in currentPc
+extern "C" void dvmJitToExceptionThrown(int targetpc); //in currentPc
+#ifdef DEBUG_CALL_STACK3
+void debug_dumpSwitch(int); //in %ebx
+#endif
+
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+                                          Thread *self,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz);
+#endif /*_DALVIK_NCG_HELPER*/
diff --git a/vm/compiler/codegen/x86/Translator.h b/vm/compiler/codegen/x86/Translator.h
new file mode 100644
index 0000000..0d7ef32
--- /dev/null
+++ b/vm/compiler/codegen/x86/Translator.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+
+#ifndef _DALVIK_TRANSLATOR
+#define _DALVIK_TRANSLATOR
+
+#include "Dalvik.h"
+#include "enc_wrapper.h"
+
+/* initialization for trace-based JIT */
+void initJIT(const char* curFileName, DvmDex *pDvmDex);
+
+#endif
diff --git a/vm/compiler/codegen/x86/libenc/Android.mk b/vm/compiler/codegen/x86/libenc/Android.mk
new file mode 100644
index 0000000..85bea56
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/Android.mk
@@ -0,0 +1,66 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# Only include the x86 encoder/decoder for x86 architecture
+ifeq ($(TARGET_ARCH),x86)
+
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(LIBENC_INCLUDED),true)
+
+LIBENC_INCLUDED := true
+
+enc_src_files := \
+        enc_base.cpp \
+        dec_base.cpp \
+        enc_wrapper.cpp \
+        enc_tabl.cpp
+
+enc_include_files :=
+
+##
+##
+## Build the device version of libenc
+##
+##
+ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(enc_src_files)
+LOCAL_C_INCLUDES += $(enc_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libenc
+LOCAL_32_BIT_ONLY := true
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host version of libenc
+##
+##
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(enc_src_files)
+LOCAL_C_INCLUDES += $(enc_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libenc
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif   # ifneq ($(LIBENC_INCLUDED),true)
+
+endif   # ifeq ($(TARGET_ARCH),x86)
diff --git a/vm/compiler/codegen/x86/libenc/README.txt b/vm/compiler/codegen/x86/libenc/README.txt
new file mode 100644
index 0000000..30df760
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/README.txt
@@ -0,0 +1,26 @@
+Original source from Apache Harmony 5.0M15 (r991518 from 2010-09-01) at
+http://harmony.apache.org/.
+
+The following files are from drlvm/vm/port/src/encoder/ia32_em64t.
+
+    dec_base.cpp
+    dec_base.h
+    enc_base.cpp
+    enc_base.h
+    enc_defs.h
+    enc_prvt.h
+    enc_tabl.cpp
+    encoder.cpp
+    encoder.h
+    encoder.inl
+
+The following files are derived partially from the original Apache
+Harmony files.
+
+    enc_defs_ext.h -- derived from enc_defs.h
+    enc_wrapper.h  -- derived from encoder.h
+
+
+
+
+
diff --git a/vm/compiler/codegen/x86/libenc/dec_base.cpp b/vm/compiler/codegen/x86/libenc/dec_base.cpp
new file mode 100644
index 0000000..e0edc10
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/dec_base.cpp
@@ -0,0 +1,540 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main decoding (disassembling) routines implementation.
+ */
+
+#include "dec_base.h"
+#include "enc_prvt.h"
+#include <stdio.h>
+//#include "open/common.h"
+
+bool DecoderBase::is_prefix(const unsigned char * bytes)
+{
+    unsigned char b0 = *bytes;
+    unsigned char b1 = *(bytes+1);
+    if (b0 == 0xF0) { // LOCK
+        return true;
+    }
+    if (b0==0xF2 || b0==0xF3) { // REPNZ/REPZ prefixes
+        if (b1 == 0x0F) {   // .... but may be a part of SIMD opcode
+            return false;
+        }
+        return true;
+    }
+    if (b0 == 0x2E || b0 == 0x36 || b0==0x3E || b0==0x26 || b0==0x64 || b0==0x3E) {
+        // branch hints, segment prefixes
+        return true;
+    }
+    if (b0==0x66) { // operand-size prefix
+        if (b1 == 0x0F) {   // .... but may be a part of SIMD opcode
+            return false;
+        }
+        return false; //XXX - currently considered as part of opcode//true;
+    }
+    if (b0==0x67) { // address size prefix
+        return true;
+    }
+    return false;
+}
+
+// Returns prefix count from 0 to 4, or ((unsigned int)-1) on error
+unsigned int DecoderBase::fill_prefs(const unsigned char * bytes, Inst * pinst)
+{
+    const unsigned char * my_bytes = bytes;
+
+    while( 1 )
+    {
+        unsigned char by1 = *my_bytes;
+        unsigned char by2 = *(my_bytes + 1);
+        Inst::PrefGroups where;
+
+        switch( by1 )
+        {
+        case InstPrefix_REPNE:
+        case InstPrefix_REP:
+        {
+            if( 0x0F == by2)
+            {
+                return pinst->prefc;
+            }
+        }
+        case InstPrefix_LOCK:
+        {
+            where = Inst::Group1;
+            break;
+        }
+        case InstPrefix_CS:
+        case InstPrefix_SS:
+        case InstPrefix_DS:
+        case InstPrefix_ES:
+        case InstPrefix_FS:
+        case InstPrefix_GS:
+//      case InstPrefix_HintTaken: the same as CS override
+//      case InstPrefix_HintNotTaken: the same as DS override
+        {
+            where = Inst::Group2;
+            break;
+        }
+        case InstPrefix_OpndSize:
+        {
+//NOTE:   prefix does not work for JMP Sz16, the opcode is 0x66 0xe9
+//        here 0x66 will be treated as prefix, try_mn will try to match the code starting at 0xe9
+//        it will match JMP Sz32 ...
+//HACK:   assume it is the last prefix, return any way
+            if( 0x0F == by2)
+            {
+                return pinst->prefc;
+            }
+            return pinst->prefc;
+            where = Inst::Group3;
+            break;
+        }
+        case InstPrefix_AddrSize:
+        {
+            where = Inst::Group4;
+            break;
+        }
+        default:
+        {
+            return pinst->prefc;
+        }
+        }
+        // Assertions are not allowed here.
+        // Error situations should result in returning error status
+        if (InstPrefix_Null != pinst->pref[where]) //only one prefix in each group
+            return (unsigned int)-1;
+
+        pinst->pref[where] = (InstPrefix)by1;
+
+        if (pinst->prefc >= 4) //no more than 4 prefixes
+            return (unsigned int)-1;
+
+        pinst->prefc++;
+        ++my_bytes;
+    }
+}
+
+
+
+unsigned DecoderBase::decode(const void * addr, Inst * pinst)
+{
+    Inst tmp;
+
+    //assert( *(unsigned char*)addr != 0x66);
+
+    const unsigned char * bytes = (unsigned char*)addr;
+
+    // Load up to 4 prefixes
+    // for each Mnemonic
+    unsigned int pref_count = fill_prefs(bytes, &tmp);
+
+    if (pref_count == (unsigned int)-1) // Wrong prefix sequence, or >4 prefixes
+        return 0; // Error
+
+    bytes += pref_count;
+
+    //  for each opcodedesc
+    //      if (raw_len == 0) memcmp(, raw_len)
+    //  else check the mixed state which is one of the following:
+    //      /digit /i /rw /rd /rb
+
+    bool found = false;
+    const unsigned char * saveBytes = bytes;
+    for (unsigned mn=1; mn<Mnemonic_Count; mn++) {
+        bytes = saveBytes;
+        found=try_mn((Mnemonic)mn, &bytes, &tmp);
+        if (found) {
+            tmp.mn = (Mnemonic)mn;
+            break;
+        }
+    }
+    if (!found) {
+        // Unknown opcode
+        return 0;
+    }
+    tmp.size = (unsigned)(bytes-(const unsigned char*)addr);
+    if (pinst) {
+        *pinst = tmp;
+    }
+    return tmp.size;
+}
+
+#ifdef _EM64T_
+#define EXTEND_REG(reg, flag)                        \
+    ((NULL == rex || 0 == rex->flag) ? reg : (reg + 8))
+#else
+#define EXTEND_REG(reg, flag) (reg)
+#endif
+
+//don't know the use of rex, seems not used when _EM64T_ is not enabled
+bool DecoderBase::decode_aux(const EncoderBase::OpcodeDesc& odesc, unsigned aux,
+    const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+    , const Rex UNREF *rex
+#endif
+    )
+{
+    OpcodeByteKind kind = (OpcodeByteKind)(aux & OpcodeByteKind_KindMask);
+    unsigned byte = (aux & OpcodeByteKind_OpcodeMask);
+    unsigned data_byte = **pbuf;
+    EncoderBase::Operand& opnd = pinst->operands[pinst->argc];
+    const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc];
+
+    switch (kind) {
+    case OpcodeByteKind_SlashR:
+        {
+            RegName reg;
+            OpndKind okind;
+            const ModRM& modrm = *(ModRM*)*pbuf;
+            if (opndDesc.kind & OpndKind_Mem) { // 1st operand is memory
+#ifdef _EM64T_
+                decodeModRM(odesc, pbuf, pinst, rex);
+#else
+                decodeModRM(odesc, pbuf, pinst);
+#endif
+                ++pinst->argc;
+                const EncoderBase::OpndDesc& opndDesc2 = odesc.opnds[pinst->argc];
+                okind = ((opndDesc2.kind & OpndKind_XMMReg) || opndDesc2.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+                EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc];
+                reg = getRegName(okind, opndDesc2.size, EXTEND_REG(modrm.reg, r));
+                regOpnd = EncoderBase::Operand(reg);
+            } else {                            // 2nd operand is memory
+                okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+                EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc];
+                reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.reg, r));
+                regOpnd = EncoderBase::Operand(reg);
+                ++pinst->argc;
+#ifdef _EM64T_
+                decodeModRM(odesc, pbuf, pinst, rex);
+#else
+                decodeModRM(odesc, pbuf, pinst);
+#endif
+            }
+            ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_rb:
+    case OpcodeByteKind_rw:
+    case OpcodeByteKind_rd:
+        {
+            // Gregory -
+            // Here we don't parse register because for current needs
+            // disassembler doesn't require to parse all operands
+            unsigned regid = data_byte - byte;
+            if (regid>7) {
+                return false;
+            }
+            OpndSize opnd_size;
+            switch(kind)
+            {
+            case OpcodeByteKind_rb:
+            {
+                opnd_size = OpndSize_8;
+                break;
+            }
+            case OpcodeByteKind_rw:
+            {
+                opnd_size = OpndSize_16;
+                break;
+            }
+            case OpcodeByteKind_rd:
+            {
+                opnd_size = OpndSize_32;
+                break;
+            }
+            default:
+                opnd_size = OpndSize_32;  // so there is no compiler warning
+                assert( false );
+            }
+            opnd = EncoderBase::Operand( getRegName(OpndKind_GPReg, opnd_size, regid) );
+
+            ++pinst->argc;
+            ++*pbuf;
+            return true;
+        }
+    case OpcodeByteKind_cb:
+        {
+        char offset = *(char*)*pbuf;
+        *pbuf += 1;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        //pinst->direct_addr = (void*)(pinst->offset + *pbuf);
+        }
+        return true;
+    case OpcodeByteKind_cw:
+        // not an error, but not expected in current env
+        // Android x86
+        {
+        short offset = *(short*)*pbuf;
+        *pbuf += 2;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        }
+        return true;
+        //return false;
+    case OpcodeByteKind_cd:
+        {
+        int offset = *(int*)*pbuf;
+        *pbuf += 4;
+        opnd = EncoderBase::Operand(offset);
+        ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_SlashNum:
+        {
+        const ModRM& modrm = *(ModRM*)*pbuf;
+        if (modrm.reg != byte) {
+            return false;
+        }
+        decodeModRM(odesc, pbuf, pinst
+#ifdef _EM64T_
+                        , rex
+#endif
+                        );
+        ++pinst->argc;
+        }
+        return true;
+    case OpcodeByteKind_ib:
+        {
+        char ival = *(char*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 1;
+        }
+        return true;
+    case OpcodeByteKind_iw:
+        {
+        short ival = *(short*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 2;
+        }
+        return true;
+    case OpcodeByteKind_id:
+        {
+        int ival = *(int*)*pbuf;
+        opnd = EncoderBase::Operand(ival);
+        ++pinst->argc;
+        *pbuf += 4;
+        }
+        return true;
+#ifdef _EM64T_
+    case OpcodeByteKind_io:
+        {
+        long long int ival = *(long long int*)*pbuf;
+        opnd = EncoderBase::Operand(OpndSize_64, ival);
+        ++pinst->argc;
+        *pbuf += 8;
+        }
+        return true;
+#endif
+    case OpcodeByteKind_plus_i:
+        {
+            unsigned regid = data_byte - byte;
+            if (regid>7) {
+                return false;
+            }
+            ++*pbuf;
+            return true;
+        }
+    case OpcodeByteKind_ZeroOpcodeByte: // cant be here
+        return false;
+    default:
+        // unknown kind ? how comes ?
+        break;
+    }
+    return false;
+}
+
+bool DecoderBase::try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst) {
+    const unsigned char * save_pbuf = *pbuf;
+    EncoderBase::OpcodeDesc * opcodes = EncoderBase::opcodes[mn];
+
+    for (unsigned i=0; !opcodes[i].last; i++) {
+        const EncoderBase::OpcodeDesc& odesc = opcodes[i];
+        char *opcode_ptr = const_cast<char *>(odesc.opcode);
+        int opcode_len = odesc.opcode_len;
+#ifdef _EM64T_
+        Rex *prex = NULL;
+        Rex rex;
+#endif
+
+        *pbuf = save_pbuf;
+#ifdef _EM64T_
+        // Match REX prefixes
+        unsigned char rex_byte = (*pbuf)[0];
+        if ((rex_byte & 0xf0) == 0x40)
+        {
+            if ((rex_byte & 0x08) != 0)
+            {
+                // Have REX.W
+                if (opcode_len > 0 && opcode_ptr[0] == 0x48)
+                {
+                    // Have REX.W in opcode. All mnemonics that allow
+                    // REX.W have to have specified it in opcode,
+                    // otherwise it is not allowed
+                    rex = *(Rex *)*pbuf;
+                    prex = &rex;
+                    (*pbuf)++;
+                    opcode_ptr++;
+                    opcode_len--;
+                }
+            }
+            else
+            {
+                // No REX.W, so it doesn't have to be in opcode. We
+                // have REX.B, REX.X, REX.R or their combination, but
+                // not in opcode, they may extend any part of the
+                // instruction
+                rex = *(Rex *)*pbuf;
+                prex = &rex;
+                (*pbuf)++;
+            }
+        }
+#endif
+        if (opcode_len != 0) {
+            if (memcmp(*pbuf, opcode_ptr, opcode_len)) {
+                continue;
+            }
+            *pbuf += opcode_len;
+        }
+        if (odesc.aux0 != 0) {
+
+            if (!decode_aux(odesc, odesc.aux0, pbuf, pinst
+#ifdef _EM64T_
+                            , prex
+#endif
+                            )) {
+                continue;
+            }
+            if (odesc.aux1 != 0) {
+                if (!decode_aux(odesc, odesc.aux1, pbuf, pinst
+#ifdef _EM64T_
+                            , prex
+#endif
+                            )) {
+                    continue;
+                }
+            }
+            pinst->odesc = &opcodes[i];
+            return true;
+        }
+        else {
+            // Can't have empty opcode
+            assert(opcode_len != 0);
+            pinst->odesc = &opcodes[i];
+            return true;
+        }
+    }
+    return false;
+}
+
+bool DecoderBase::decodeModRM(const EncoderBase::OpcodeDesc& odesc,
+    const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+    , const Rex *rex
+#endif
+    )
+{
+    EncoderBase::Operand& opnd = pinst->operands[pinst->argc];
+    const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc];
+
+    //XXX debug ///assert(0x66 != *(*pbuf-2));
+    const ModRM& modrm = *(ModRM*)*pbuf;
+    *pbuf += 1;
+
+    RegName base = RegName_Null;
+    RegName index = RegName_Null;
+    int disp = 0;
+    unsigned scale = 0;
+
+    // On x86_64 all mnemonics that allow REX.W have REX.W in opcode.
+    // Therefore REX.W is simply ignored, and opndDesc.size is used
+
+    if (modrm.mod == 3) {
+        // we have only modrm. no sib, no disp.
+        // Android x86: Use XMMReg for 64b operand.
+        OpndKind okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size == OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg;
+        RegName reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.rm, b));
+        opnd = EncoderBase::Operand(reg);
+        return true;
+    }
+    //Android x86: m16, m32, m64: mean a byte[word|doubleword] operand in memory
+    //base and index should be 32 bits!!!
+    const SIB& sib = *(SIB*)*pbuf;
+    // check whether we have a sib
+    if (modrm.rm == 4) {
+        // yes, we have SIB
+        *pbuf += 1;
+        // scale = sib.scale == 0 ? 0 : (1<<sib.scale);
+        scale = (1<<sib.scale);
+        if (sib.index != 4) {
+            index = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.index, x)); //Android x86: OpndDesc.size
+        } else {
+            // (sib.index == 4) => no index
+            //%esp can't be sib.index
+        }
+
+        if (sib.base != 5 || modrm.mod != 0) {
+            base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.base, b)); //Android x86: OpndDesc.size
+        } else {
+            // (sib.base == 5 && modrm.mod == 0) => no base
+        }
+    }
+    else {
+        if (modrm.mod != 0 || modrm.rm != 5) {
+            base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(modrm.rm, b)); //Android x86: OpndDesc.size
+        }
+        else {
+            // mod=0 && rm == 5 => only disp32
+        }
+    }
+
+    //update disp and pbuf
+    if (modrm.mod == 2) {
+        // have disp32
+        disp = *(int*)*pbuf;
+        *pbuf += 4;
+    }
+    else if (modrm.mod == 1) {
+        // have disp8
+        disp = *(char*)*pbuf;
+        *pbuf += 1;
+    }
+    else {
+        assert(modrm.mod == 0);
+        if (modrm.rm == 5) {
+            // have disp32 w/o sib
+            disp = *(int*)*pbuf;
+            *pbuf += 4;
+        }
+        else if (modrm.rm == 4 && sib.base == 5) {
+            // have disp32 with SI in sib
+            disp = *(int*)*pbuf;
+            *pbuf += 4;
+        }
+    }
+    opnd = EncoderBase::Operand(opndDesc.size, base, index, scale, disp);
+    return true;
+}
+
diff --git a/vm/compiler/codegen/x86/libenc/dec_base.h b/vm/compiler/codegen/x86/libenc/dec_base.h
new file mode 100644
index 0000000..909c743
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/dec_base.h
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main decoding (disassembling) routines and structures.
+ *
+ * @note Quick and rough implementation, subject for a change.
+ */
+
+#ifndef __DEC_BASE_H_INCLUDED__
+#define __DEC_BASE_H_INCLUDED__
+
+
+#include "enc_base.h"
+#include "enc_prvt.h"
+
+#ifdef ENCODER_ISOLATE
+using namespace enc_ia32;
+#endif
+
+#define IF_CONDITIONAL  (0x00000000)
+#define IF_SYMMETRIC    (0x00000000)
+#define IF_BRANCH       (0x00000000)
+
+struct Inst {
+    Inst() {
+        mn = Mnemonic_Null;
+        prefc = 0;
+        size = 0;
+        flags = 0;
+        //offset = 0;
+        //direct_addr = NULL;
+        argc = 0;
+        for(int i = 0; i < 4; ++i)
+        {
+            pref[i] = InstPrefix_Null;
+        }
+    }
+    /**
+     * Mnemonic of the instruction.s
+     */
+    Mnemonic mn;
+    /**
+     * Enumerating of indexes in the pref array.
+     */
+    enum PrefGroups
+    {
+        Group1 = 0,
+        Group2,
+        Group3,
+        Group4
+    };
+    /**
+     * Number of prefixes (1 byte each).
+     */
+    unsigned int prefc;
+    /**
+     * Instruction prefixes. Prefix should be placed here according to its group.
+     */
+    InstPrefix pref[4];
+    /**
+     * Size, in bytes, of the instruction.
+     */
+    unsigned size;
+    /**
+     * Flags of the instruction.
+     * @see MF_
+     */
+    unsigned flags;
+    /**
+     * An offset of target address, in case of 'CALL offset',
+     * 'JMP/Jcc offset'.
+     */
+    //int      offset;
+    /**
+     * Direct address of the target (on Intel64/IA-32 is 'instruction IP' +
+     * 'instruction length' + offset).
+     */
+    //void *   direct_addr;
+    /**
+     * Number of arguments of the instruction.
+     */
+    unsigned argc;
+    //
+    EncoderBase::Operand operands[3];
+    //
+    const EncoderBase::OpcodeDesc * odesc;
+};
+
+inline bool is_jcc(Mnemonic mn)
+{
+    return Mnemonic_JO <= mn && mn<=Mnemonic_JG;
+}
+
+class DecoderBase {
+public:
+    static unsigned decode(const void * addr, Inst * pinst);
+private:
+    static bool decodeModRM(const EncoderBase::OpcodeDesc& odesc,
+        const unsigned char ** pbuf, Inst * pinst
+#ifdef _EM64T_
+        , const Rex *rex
+#endif
+        );
+    static bool decode_aux(const EncoderBase::OpcodeDesc& odesc,
+        unsigned aux, const unsigned char ** pbuf,
+        Inst * pinst
+#ifdef _EM64T_
+        , const Rex *rex
+#endif
+        );
+    static bool try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst);
+    static unsigned int fill_prefs( const unsigned char * bytes, Inst * pinst);
+    static bool is_prefix(const unsigned char * bytes);
+};
+
+#endif  // ~ __DEC_BASE_H_INCLUDED__
+
diff --git a/vm/compiler/codegen/x86/libenc/enc_base.cpp b/vm/compiler/codegen/x86/libenc/enc_base.cpp
new file mode 100644
index 0000000..d141e1f
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_base.cpp
@@ -0,0 +1,1153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include "enc_base.h"
+//#include <climits>
+#include <string.h>
+#define USE_ENCODER_DEFINES
+#include "enc_prvt.h"
+#include <stdio.h>
+
+//#define JET_PROTO
+
+#ifdef JET_PROTO
+#include "dec_base.h"
+#include "jvmti_dasm.h"
+#endif
+
+ENCODER_NAMESPACE_START
+
+/**
+ * @file
+ * @brief Main encoding routines and structures.
+ */
+
+#ifndef _WIN32
+    #define strcmpi strcasecmp
+#endif
+
+int EncoderBase::dummy = EncoderBase::buildTable();
+
+const unsigned char EncoderBase::size_hash[OpndSize_64+1] = {
+    //
+    0xFF,   // OpndSize_Null        = 0,
+    3,              // OpndSize_8           = 0x1,
+    2,              // OpndSize_16          = 0x2,
+    0xFF,   // 0x3
+    1,              // OpndSize_32          = 0x4,
+    0xFF,   // 0x5
+    0xFF,   // 0x6
+    0xFF,   // 0x7
+    0,              // OpndSize_64          = 0x8,
+    //
+};
+
+const unsigned char EncoderBase::kind_hash[OpndKind_Mem+1] = {
+    //
+    //gp reg                -> 000 = 0
+    //memory                -> 001 = 1
+    //immediate             -> 010 = 2
+    //xmm reg               -> 011 = 3
+    //segment regs  -> 100 = 4
+    //fp reg                -> 101 = 5
+    //mmx reg               -> 110 = 6
+    //
+    0xFF,                          // 0    OpndKind_Null=0,
+    0<<2,                          // 1    OpndKind_GPReg =
+                                   //           OpndKind_MinRegKind=0x1,
+    4<<2,                          // 2    OpndKind_SReg=0x2,
+
+#ifdef _HAVE_MMX_
+    6<<2,                          // 3
+#else
+    0xFF,                          // 3
+#endif
+
+    5<<2,                          // 4    OpndKind_FPReg=0x4,
+    0xFF, 0xFF, 0xFF,              // 5, 6, 7
+    3<<2,                                   //      OpndKind_XMMReg=0x8,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 9, 0xA, 0xB, 0xC, 0xD,
+                                              // 0xE, 0xF
+    0xFF,                          // OpndKind_MaxRegKind =
+                                   // OpndKind_StatusReg =
+                                   // OpndKind_OtherReg=0x10,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x11-0x18
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,               // 0x19-0x1F
+    2<<2,                                   // OpndKind_Immediate=0x20,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x21-0x28
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x29-0x30
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x31-0x38
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,               // 0x39-0x3F
+    1<<2,                                   // OpndKind_Memory=0x40
+};
+
+char * EncoderBase::curRelOpnd[3];
+
+char* EncoderBase::encode_aux(char* stream, unsigned aux,
+                              const Operands& opnds, const OpcodeDesc * odesc,
+                              unsigned * pargsCount, Rex * prex)
+{
+    const unsigned byte = aux;
+    OpcodeByteKind kind = (OpcodeByteKind)(byte & OpcodeByteKind_KindMask);
+    // The '>>' here is to force the switch to be table-based) instead of
+    // set of CMP+Jcc.
+    if (*pargsCount >= COUNTOF(opnds)) {
+        assert(false);
+        return stream;
+    }
+    switch(kind>>8) {
+    case OpcodeByteKind_SlashR>>8:
+        // /r - Indicates that the ModR/M byte of the instruction contains
+        // both a register operand and an r/m operand.
+        {
+        assert(opnds.count() > 1);
+    // not true anymore for MOVQ xmm<->r
+        //assert((odesc->opnds[0].kind & OpndKind_Mem) ||
+        //       (odesc->opnds[1].kind & OpndKind_Mem));
+        unsigned memidx = odesc->opnds[0].kind & OpndKind_Mem ? 0 : 1;
+        unsigned regidx = memidx == 0 ? 1 : 0;
+        memidx += *pargsCount;
+        regidx += *pargsCount;
+        ModRM& modrm = *(ModRM*)stream;
+        if (memidx >= COUNTOF(opnds) || regidx >= COUNTOF(opnds)) {
+            assert(false);
+            break;
+        }
+        if (opnds[memidx].is_mem()) {
+            stream = encodeModRM(stream, opnds, memidx, odesc, prex);
+        }
+        else {
+            modrm.mod = 3; // 11
+            modrm.rm = getHWRegIndex(opnds[memidx].reg());
+#ifdef _EM64T_
+            if (opnds[memidx].need_rex() && needs_rex_r(opnds[memidx].reg())) {
+                prex->b = 1;
+            }
+#endif
+            ++stream;
+        }
+        modrm.reg = getHWRegIndex(opnds[regidx].reg());
+#ifdef _EM64T_
+        if (opnds[regidx].need_rex() && needs_rex_r(opnds[regidx].reg())) {
+            prex->r = 1;
+        }
+#endif
+        *pargsCount += 2;
+        }
+        break;
+    case OpcodeByteKind_SlashNum>>8:
+        //  /digit - A digit between 0 and 7 indicates that the
+        //  ModR/M byte of the instruction uses only the r/m
+        //  (register or memory) operand. The reg field contains
+        //  the digit that provides an extension to the instruction's
+        //  opcode.
+        {
+        const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+        assert(lowByte <= 7);
+        ModRM& modrm = *(ModRM*)stream;
+        unsigned idx = *pargsCount;
+        assert(opnds[idx].is_mem() || opnds[idx].is_reg());
+        if (opnds[idx].is_mem()) {
+            stream = encodeModRM(stream, opnds, idx, odesc, prex);
+        }
+        else {
+            modrm.mod = 3; // 11
+            modrm.rm = getHWRegIndex(opnds[idx].reg());
+#ifdef _EM64T_
+            if (opnds[idx].need_rex() && needs_rex_r(opnds[idx].reg())) {
+                prex->b = 1;
+            }
+#endif
+            ++stream;
+        }
+        modrm.reg = (char)lowByte;
+        *pargsCount += 1;
+        }
+        break;
+    case OpcodeByteKind_plus_i>>8:
+        //  +i - A number used in floating-point instructions when one
+        //  of the operands is ST(i) from the FPU register stack. The
+        //  number i (which can range from 0 to 7) is added to the
+        //  hexadecimal byte given at the left of the plus sign to form
+        //  a single opcode byte.
+        {
+            unsigned idx = *pargsCount;
+            const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+            *stream = (char)lowByte + getHWRegIndex(opnds[idx].reg());
+            ++stream;
+            *pargsCount += 1;
+        }
+        break;
+    case OpcodeByteKind_ib>>8:
+    case OpcodeByteKind_iw>>8:
+    case OpcodeByteKind_id>>8:
+#ifdef _EM64T_
+    case OpcodeByteKind_io>>8:
+#endif //_EM64T_
+        //  ib, iw, id - A 1-byte (ib), 2-byte (iw), or 4-byte (id)
+        //  immediate operand to the instruction that follows the
+        //  opcode, ModR/M bytes or scale-indexing bytes. The opcode
+        //  determines if the operand is a signed value. All words
+        //  and double words are given with the low-order byte first.
+        {
+            unsigned idx = *pargsCount;
+            *pargsCount += 1;
+            assert(opnds[idx].is_imm());
+            if (kind == OpcodeByteKind_ib) {
+                *(unsigned char*)stream = (unsigned char)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 1;
+            }
+            else if (kind == OpcodeByteKind_iw) {
+                *(unsigned short*)stream = (unsigned short)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 2;
+            }
+            else if (kind == OpcodeByteKind_id) {
+                *(unsigned*)stream = (unsigned)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 4;
+            }
+#ifdef _EM64T_
+            else {
+                assert(kind == OpcodeByteKind_io);
+                *(long long*)stream = (long long)opnds[idx].imm();
+                curRelOpnd[idx] = stream;
+                stream += 8;
+            }
+#else
+            else {
+                assert(false);
+            }
+#endif
+        }
+        break;
+    case OpcodeByteKind_cb>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned char*)stream = (unsigned char)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 1;
+        *pargsCount += 1;
+        break;
+    case OpcodeByteKind_cw>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned short*)stream = (unsigned short)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 2;
+        *pargsCount += 1;
+        break;
+    case OpcodeByteKind_cd>>8:
+        assert(opnds[*pargsCount].is_imm());
+        *(unsigned*)stream = (unsigned)opnds[*pargsCount].imm();
+        curRelOpnd[*pargsCount]= stream;
+        stream += 4;
+        *pargsCount += 1;
+        break;
+    //OpcodeByteKind_cp                             = 0x0B00,
+    //OpcodeByteKind_co                             = 0x0C00,
+    //OpcodeByteKind_ct                             = 0x0D00,
+    case OpcodeByteKind_rb>>8:
+    case OpcodeByteKind_rw>>8:
+    case OpcodeByteKind_rd>>8:
+        //  +rb, +rw, +rd - A register code, from 0 through 7,
+        //  added to the hexadecimal byte given at the left of
+        //  the plus sign to form a single opcode byte.
+        assert(opnds.count() > 0);
+        assert(opnds[*pargsCount].is_reg());
+        {
+        const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask);
+        *(unsigned char*)stream = (unsigned char)lowByte +
+                                   getHWRegIndex(opnds[*pargsCount].reg());
+#ifdef _EM64T_
+        if (opnds[*pargsCount].need_rex() && needs_rex_r(opnds[*pargsCount].reg())) {
+        prex->b = 1;
+        }
+#endif
+        ++stream;
+        *pargsCount += 1;
+        }
+        break;
+    default:
+        assert(false);
+        break;
+    }
+    return stream;
+}
+
+char * EncoderBase::encode(char * stream, Mnemonic mn, const Operands& opnds)
+{
+#ifdef _DEBUG
+    if (opnds.count() > 0) {
+        if (opnds[0].is_mem()) {
+            assert(getRegKind(opnds[0].base()) != OpndKind_SReg);
+        }
+        else if (opnds.count() >1 && opnds[1].is_mem()) {
+            assert(getRegKind(opnds[1].base()) != OpndKind_SReg);
+        }
+    }
+#endif
+
+#ifdef JET_PROTO
+    char* saveStream = stream;
+#endif
+
+    const OpcodeDesc * odesc = lookup(mn, opnds);
+#if !defined(_EM64T_)
+    bool copy_opcode = true;
+    Rex *prex = NULL;
+#else
+    // We need rex if
+    //  either of registers used as operand or address form is new extended register
+    //  it's explicitly specified by opcode
+    // So, if we don't have REX in opcode but need_rex, then set rex here
+    // otherwise, wait until opcode is set, and then update REX
+
+    bool copy_opcode = true;
+    unsigned char _1st = odesc->opcode[0];
+
+    Rex *prex = (Rex*)stream;
+    if (opnds.need_rex() &&
+        ((_1st == 0x66) || (_1st == 0xF2 || _1st == 0xF3) && odesc->opcode[1] == 0x0F)) {
+        // Special processing
+        //
+        copy_opcode = false;
+        //
+        *(unsigned char*)stream = _1st;
+        ++stream;
+        //
+        prex = (Rex*)stream;
+        prex->dummy = 4;
+        prex->w = 0;
+        prex->b = 0;
+        prex->x = 0;
+        prex->r = 0;
+        ++stream;
+        //
+        memcpy(stream, &odesc->opcode[1], odesc->opcode_len-1);
+        stream += odesc->opcode_len-1;
+    }
+    else if (_1st != 0x48 && opnds.need_rex()) {
+        prex = (Rex*)stream;
+        prex->dummy = 4;
+        prex->w = 0;
+        prex->b = 0;
+        prex->x = 0;
+        prex->r = 0;
+        ++stream;
+    }
+#endif  // ifndef EM64T
+
+    if (copy_opcode) {
+        if (odesc->opcode_len==1) {
+        *(unsigned char*)stream = *(unsigned char*)&odesc->opcode;
+        }
+        else if (odesc->opcode_len==2) {
+        *(unsigned short*)stream = *(unsigned short*)&odesc->opcode;
+        }
+        else if (odesc->opcode_len==3) {
+        *(unsigned short*)stream = *(unsigned short*)&odesc->opcode;
+        *(unsigned char*)(stream+2) = odesc->opcode[2];
+        }
+        else if (odesc->opcode_len==4) {
+        *(unsigned*)stream = *(unsigned*)&odesc->opcode;
+        }
+        stream += odesc->opcode_len;
+    }
+
+    unsigned argsCount = odesc->first_opnd;
+
+    if (odesc->aux0) {
+        stream = encode_aux(stream, odesc->aux0, opnds, odesc, &argsCount, prex);
+        if (odesc->aux1) {
+            stream = encode_aux(stream, odesc->aux1, opnds, odesc, &argsCount, prex);
+        }
+    }
+#ifdef JET_PROTO
+    //saveStream
+    Inst inst;
+    unsigned len = DecoderBase::decode(saveStream, &inst);
+    assert(inst.mn == mn);
+    assert(len == (unsigned)(stream-saveStream));
+    if (mn == Mnemonic_CALL || mn == Mnemonic_JMP ||
+        Mnemonic_RET == mn ||
+        (Mnemonic_JO<=mn && mn<=Mnemonic_JG)) {
+        assert(inst.argc == opnds.count());
+
+        InstructionDisassembler idi(saveStream);
+
+        for (unsigned i=0; i<inst.argc; i++) {
+            const EncoderBase::Operand& original = opnds[i];
+            const EncoderBase::Operand& decoded = inst.operands[i];
+            assert(original.kind() == decoded.kind());
+            assert(original.size() == decoded.size());
+            if (original.is_imm()) {
+                assert(original.imm() == decoded.imm());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Imm);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_JUMP);
+                }
+                else if (mn == Mnemonic_RET) {
+                    assert(idi.get_type() == InstructionDisassembler::RET);
+                }
+                else {
+                    assert(idi.get_type() == InstructionDisassembler::RELATIVE_COND_JUMP);
+                }
+            }
+            else if (original.is_mem()) {
+                assert(original.base() == decoded.base());
+                assert(original.index() == decoded.index());
+                assert(original.scale() == decoded.scale());
+                assert(original.disp() == decoded.disp());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Mem);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP);
+                }
+                else {
+                    assert(false);
+                }
+            }
+            else {
+                assert(original.is_reg());
+                assert(original.reg() == decoded.reg());
+                assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Reg);
+                if (mn == Mnemonic_CALL) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL);
+                }
+                else if (mn == Mnemonic_JMP) {
+                    assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP);
+                }
+                else {
+                    assert(false);
+                }
+            }
+        }
+
+        Inst inst2;
+        len = DecoderBase::decode(saveStream, &inst2);
+    }
+
+ //   if(idi.get_length_with_prefix() != (int)len) {
+	//__asm { int 3 };
+ //   }
+#endif
+
+    return stream;
+}
+
+char* EncoderBase::encodeModRM(char* stream, const Operands& opnds,
+                               unsigned idx, const OpcodeDesc * odesc,
+                               Rex * prex)
+{
+    const Operand& op = opnds[idx];
+    assert(op.is_mem());
+    assert(idx < COUNTOF(curRelOpnd));
+    ModRM& modrm = *(ModRM*)stream;
+    ++stream;
+    SIB& sib = *(SIB*)stream;
+
+    // we need SIB if
+    //      we have index & scale (nb: having index w/o base and w/o scale
+    //      treated as error)
+    //      the base is EBP w/o disp, BUT let's use a fake disp8
+    //      the base is ESP (nb: cant have ESP as index)
+
+    RegName base = op.base();
+    // only disp ?..
+    if (base == RegName_Null && op.index() == RegName_Null) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // ... yes - only have disp
+        // On EM64T, the simply [disp] addressing means 'RIP-based' one -
+        // must have to use SIB to encode 'DS: based'
+#ifdef _EM64T_
+        modrm.mod = 0;  // 00 - ..
+        modrm.rm = 4;   // 100 - have SIB
+
+        sib.base = 5;   // 101 - none
+        sib.index = 4;  // 100 - none
+        sib.scale = 0;  //
+        ++stream; // bypass SIB
+#else
+        // ignore disp_fits8, always use disp32.
+        modrm.mod = 0;
+        modrm.rm = 5;
+#endif
+        *(unsigned*)stream = (unsigned)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+        return stream;
+    }
+
+    //climits: error when targeting compal
+#define CHAR_MIN -127
+#define CHAR_MAX 127
+    const bool disp_fits8 = CHAR_MIN <= op.disp() && op.disp() <= CHAR_MAX;
+    /*&& op.base() != RegName_Null - just checked above*/
+    if (op.index() == RegName_Null && getHWRegIndex(op.base()) != getHWRegIndex(REG_STACK)) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // ... luckily no SIB, only base and may be a disp
+
+        // EBP base is a special case. Need to use [EBP] + disp8 form
+        if (op.disp() == 0  && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) {
+            modrm.mod = 0; // mod=00, no disp et all
+        }
+        else if (disp_fits8) {
+            modrm.mod = 1; // mod=01, use disp8
+            *(unsigned char*)stream = (unsigned char)op.disp();
+            curRelOpnd[idx]= stream;
+            ++stream;
+        }
+        else {
+            modrm.mod = 2; // mod=10, use disp32
+            *(unsigned*)stream = (unsigned)op.disp();
+            curRelOpnd[idx]= stream;
+            stream += 4;
+        }
+        modrm.rm = getHWRegIndex(op.base());
+    if (is_em64t_extra_reg(op.base())) {
+        prex->b = 1;
+    }
+        return stream;
+    }
+
+    // cool, we do have SIB.
+    ++stream; // bypass SIB in stream
+
+    // {E|R}SP cannot be scaled index, however, R12 which has the same index in modrm - can
+    assert(op.index() == RegName_Null || !equals(op.index(), REG_STACK));
+
+    // Only GPRegs can be encoded in the SIB
+    assert(op.base() == RegName_Null ||
+            getRegKind(op.base()) == OpndKind_GPReg);
+    assert(op.index() == RegName_Null ||
+            getRegKind(op.index()) == OpndKind_GPReg);
+
+    modrm.rm = 4;   // r/m = 100, means 'we have SIB here'
+    if (op.base() == RegName_Null) {
+        // no base.
+        // already checked above if
+        // the first if() //assert(op.index() != RegName_Null);
+
+        modrm.mod = 0;  // mod=00 - here it means 'no base, but disp32'
+        sib.base = 5;   // 101 with mod=00  ^^^
+
+        // encode at least fake disp32 to avoid having [base=ebp]
+        *(unsigned*)stream = op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+
+        unsigned sc = op.scale();
+        if (sc == 1 || sc==0)   { sib.scale = 0; }    // SS=00
+        else if (sc == 2)       { sib.scale = 1; }    // SS=01
+        else if (sc == 4)       { sib.scale = 2; }    // SS=10
+        else if (sc == 8)       { sib.scale = 3; }    // SS=11
+        sib.index = getHWRegIndex(op.index());
+    if (is_em64t_extra_reg(op.index())) {
+        prex->x = 1;
+    }
+
+        return stream;
+    }
+
+    if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) {
+        modrm.mod = 0;  // mod=00, no disp
+    }
+    else if (disp_fits8) {
+        modrm.mod = 1;  // mod=01, use disp8
+        *(unsigned char*)stream = (unsigned char)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 1;
+    }
+    else {
+        modrm.mod = 2;  // mod=10, use disp32
+        *(unsigned*)stream = (unsigned)op.disp();
+        curRelOpnd[idx]= stream;
+        stream += 4;
+    }
+
+    if (op.index() == RegName_Null) {
+        assert(op.scale() == 0); // 'scale!=0' has no meaning without index
+        // the only reason we're here without index, is that we have {E|R}SP
+        // or R12 as a base. Another possible reason - EBP without a disp -
+        // is handled above by adding a fake disp8
+#ifdef _EM64T_
+        assert(op.base() != RegName_Null && (equals(op.base(), REG_STACK) ||
+                                             equals(op.base(), RegName_R12)));
+#else  // _EM64T_
+        assert(op.base() != RegName_Null && equals(op.base(), REG_STACK));
+#endif //_EM64T_
+        sib.scale = 0;  // SS = 00
+        sib.index = 4;  // SS + index=100 means 'no index'
+    }
+    else {
+        unsigned sc = op.scale();
+        if (sc == 1 || sc==0)   { sib.scale = 0; }    // SS=00
+        else if (sc == 2)       { sib.scale = 1; }    // SS=01
+        else if (sc == 4)       { sib.scale = 2; }    // SS=10
+        else if (sc == 8)       { sib.scale = 3; }    // SS=11
+        sib.index = getHWRegIndex(op.index());
+    if (is_em64t_extra_reg(op.index())) {
+        prex->x = 1;
+    }
+        // not an error by itself, but the usage of [index*1] instead
+        // of [base] is discouraged
+        assert(op.base() != RegName_Null || op.scale() != 1);
+    }
+    sib.base = getHWRegIndex(op.base());
+    if (is_em64t_extra_reg(op.base())) {
+    prex->b = 1;
+    }
+    return stream;
+}
+
+char * EncoderBase::nops(char * stream, unsigned howMany)
+{
+    // Recommended multi-byte NOPs from the Intel architecture manual
+    static const unsigned char nops[10][9] = {
+        { 0, },                                                     // 0, this line is dummy and not used in the loop below
+        { 0x90, },                                                  // 1-byte NOP
+        { 0x66, 0x90, },                                            // 2
+        { 0x0F, 0x1F, 0x00, },                                      // 3
+        { 0x0F, 0x1F, 0x40, 0x00, },                                // 4
+        { 0x0F, 0x1F, 0x44, 0x00, 0x00, },                          // 5
+        { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, },                    // 6
+        { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, },              // 7
+        { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, },        // 8
+        { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },   // 9-byte NOP
+    };
+
+    // Start from delivering the longest possible NOPs, then proceed with shorter ones
+    for (unsigned nopSize=9; nopSize!=0; nopSize--) {
+        while(howMany>=nopSize) {
+            const unsigned char* nopBytes = nops[nopSize];
+            for (unsigned i=0; i<nopSize; i++) {
+                stream[i] = nopBytes[i];
+            }
+            stream += nopSize;
+            howMany -= nopSize;
+        }
+    }
+    char* end = stream + howMany;
+    return end;
+}
+
+char * EncoderBase::prefix(char* stream, InstPrefix pref)
+{
+    if (pref== InstPrefix_Null) {
+        // nothing to do
+        return stream;
+    }
+    *stream = (char)pref;
+    return stream + 1;
+}
+
+
+/**
+ *
+ */
+bool EncoderBase::extAllowed(OpndExt opndExt, OpndExt instExt) {
+    if (instExt == opndExt || instExt == OpndExt_Any || opndExt == OpndExt_Any) {
+            return true;
+    }
+//asm("int3");
+assert(0);
+    return false;
+}
+
+/**
+ *
+ */
+static bool match(const EncoderBase::OpcodeDesc& odesc,
+                      const EncoderBase::Operands& opnds) {
+
+    assert(odesc.roles.count == opnds.count());
+
+    for(unsigned j = 0; j < odesc.roles.count; j++) {
+        const EncoderBase::OpndDesc& desc = odesc.opnds[j];
+        const EncoderBase::Operand& op = opnds[j];
+        // location must match exactly
+        if ((desc.kind & op.kind()) != op.kind()) {
+//assert(0);
+            return false;
+        }
+        // size must match exactly
+        if (desc.size != op.size()) {
+//assert(0);
+            return false;
+        }
+        // extentions should be consistent
+        if (!EncoderBase::extAllowed(op.ext(), desc.ext)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+
+static bool try_match(const EncoderBase::OpcodeDesc& odesc,
+                      const EncoderBase::Operands& opnds, bool strict) {
+
+    assert(odesc.roles.count == opnds.count());
+
+    for(unsigned j=0; j<odesc.roles.count; j++) {
+        // - the location must match exactly
+        if ((odesc.opnds[j].kind & opnds[j].kind()) != opnds[j].kind()) {
+            return false;
+        }
+        if (strict) {
+            // the size must match exactly
+            if (odesc.opnds[j].size != opnds[j].size()) {
+                return false;
+            }
+        }
+        else {
+            // must match only for def operands, and dont care about use ones
+            // situations like 'mov r8, imm32/mov r32, imm8' so the
+            // destination operand defines the overall size
+            if (EncoderBase::getOpndRoles(odesc.roles, j) & OpndRole_Def) {
+                if (odesc.opnds[j].size != opnds[j].size()) {
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+
+//
+//Subhash implementaion - may be useful in case of many misses during fast
+//opcode lookup.
+//
+
+#ifdef ENCODER_USE_SUBHASH
+static unsigned subHash[32];
+
+static unsigned find(Mnemonic mn, unsigned hash)
+{
+    unsigned key = hash % COUNTOF(subHash);
+    unsigned pack = subHash[key];
+    unsigned _hash = pack & 0xFFFF;
+    if (_hash != hash) {
+        stat.miss(mn);
+        return EncoderBase::NOHASH;
+    }
+    unsigned _mn = (pack >> 24)&0xFF;
+    if (_mn != _mn) {
+        stat.miss(mn);
+        return EncoderBase::NOHASH;
+    }
+    unsigned idx = (pack >> 16) & 0xFF;
+    stat.hit(mn);
+    return idx;
+}
+
+static void put(Mnemonic mn, unsigned hash, unsigned idx)
+{
+    unsigned pack = hash | (idx<<16) | (mn << 24);
+    unsigned key = hash % COUNTOF(subHash);
+    subHash[key] = pack;
+}
+#endif
+
+const EncoderBase::OpcodeDesc *
+EncoderBase::lookup(Mnemonic mn, const Operands& opnds)
+{
+    const unsigned hash = opnds.hash();
+    unsigned opcodeIndex = opcodesHashMap[mn][hash];
+#ifdef ENCODER_USE_SUBHASH
+    if (opcodeIndex == NOHASH) {
+        opcodeIndex = find(mn, hash);
+    }
+#endif
+
+    if (opcodeIndex == NOHASH) {
+        // fast-path did no work. try to lookup sequentially
+        const OpcodeDesc * odesc = opcodes[mn];
+        int idx = -1;
+        bool found = false;
+        for (idx=0; !odesc[idx].last; idx++) {
+            const OpcodeDesc& opcode = odesc[idx];
+            if (opcode.platf == OpcodeInfo::decoder) {
+                continue;
+            }
+            if (opcode.roles.count != opnds.count()) {
+                continue;
+            }
+            if (try_match(opcode, opnds, true)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            for (idx=0; !odesc[idx].last; idx++) {
+                const OpcodeDesc& opcode = odesc[idx];
+                if (opcode.platf == OpcodeInfo::decoder) {
+                    continue;
+                }
+                if (opcode.roles.count != opnds.count()) {
+                    continue;
+                }
+                if (try_match(opcode, opnds, false)) {
+                    found = true;
+                    break;
+                }
+            }
+        }
+        assert(found);
+        opcodeIndex = idx;
+#ifdef ENCODER_USE_SUBHASH
+        put(mn, hash, opcodeIndex);
+#endif
+    }
+    assert(opcodeIndex != NOHASH);
+    const OpcodeDesc * odesc = &opcodes[mn][opcodeIndex];
+    assert(!odesc->last);
+    assert(odesc->roles.count == opnds.count());
+    assert(odesc->platf != OpcodeInfo::decoder);
+#if !defined(_EM64T_)
+    // tuning was done for IA32 only, so no size restriction on EM64T
+    //assert(sizeof(OpcodeDesc)==128);
+#endif
+    return odesc;
+}
+
+char* EncoderBase::getOpndLocation(int index) {
+     assert(index < 3);
+     return curRelOpnd[index];
+}
+
+
+Mnemonic EncoderBase::str2mnemonic(const char * mn_name)
+{
+    for (unsigned m = 1; m<Mnemonic_Count; m++) {
+        if (!strcmpi(mnemonics[m].name, mn_name)) {
+            return (Mnemonic)m;
+        }
+    }
+    return Mnemonic_Null;
+}
+
+static const char * conditionStrings[ConditionMnemonic_Count] = {
+    "O",
+    "NO",
+    "B",
+    "AE",
+    "Z",
+    "NZ",
+    "BE",
+    "A",
+
+    "S",
+    "NS",
+    "P",
+    "NP",
+    "L",
+    "GE",
+    "LE",
+    "G",
+};
+
+const char * getConditionString(ConditionMnemonic cm) {
+    return conditionStrings[cm];
+}
+
+static const struct {
+        char            sizeString[12];
+        OpndSize        size;
+}
+sizes[] = {
+    { "Sz8", OpndSize_8 },
+    { "Sz16", OpndSize_16 },
+    { "Sz32", OpndSize_32 },
+    { "Sz64", OpndSize_64 },
+#if !defined(TESTING_ENCODER)
+    { "Sz80", OpndSize_80 },
+    { "Sz128", OpndSize_128 },
+#endif
+    { "SzAny", OpndSize_Any },
+};
+
+
+OpndSize getOpndSize(const char * sizeString)
+{
+    assert(sizeString);
+    for (unsigned i = 0; i<COUNTOF(sizes); i++) {
+        if (!strcmpi(sizeString, sizes[i].sizeString)) {
+            return sizes[i].size;
+        }
+    }
+    return OpndSize_Null;
+}
+
+const char * getOpndSizeString(OpndSize size) {
+    for( unsigned i = 0; i<COUNTOF(sizes); i++ ) {
+        if( sizes[i].size==size ) {
+            return sizes[i].sizeString;
+        }
+    }
+    return NULL;
+}
+
+static const struct {
+    char            kindString[16];
+    OpndKind        kind;
+}
+kinds[] = {
+    { "Null", OpndKind_Null },
+    { "GPReg", OpndKind_GPReg },
+    { "SReg", OpndKind_SReg },
+    { "FPReg", OpndKind_FPReg },
+    { "XMMReg", OpndKind_XMMReg },
+#ifdef _HAVE_MMX_
+    { "MMXReg", OpndKind_MMXReg },
+#endif
+    { "StatusReg", OpndKind_StatusReg },
+    { "Reg", OpndKind_Reg },
+    { "Imm", OpndKind_Imm },
+    { "Mem", OpndKind_Mem },
+    { "Any", OpndKind_Any },
+};
+
+const char * getOpndKindString(OpndKind kind)
+{
+    for (unsigned i = 0; i<COUNTOF(kinds); i++) {
+        if (kinds[i].kind==kind) {
+            return kinds[i].kindString;
+        }
+    }
+    return NULL;
+}
+
+OpndKind getOpndKind(const char * kindString)
+{
+    assert(kindString);
+    for (unsigned i = 0; i<COUNTOF(kinds); i++) {
+        if (!strcmpi(kindString, kinds[i].kindString)) {
+            return kinds[i].kind;
+        }
+    }
+    return OpndKind_Null;
+}
+
+/**
+ * A mapping between register string representation and its RegName constant.
+ */
+static const struct {
+        char    regstring[7];
+        RegName regname;
+}
+
+registers[] = {
+#ifdef _EM64T_
+    {"RAX",         RegName_RAX},
+    {"RBX",         RegName_RBX},
+    {"RCX",         RegName_RCX},
+    {"RDX",         RegName_RDX},
+    {"RBP",         RegName_RBP},
+    {"RSI",         RegName_RSI},
+    {"RDI",         RegName_RDI},
+    {"RSP",         RegName_RSP},
+    {"R8",          RegName_R8},
+    {"R9",          RegName_R9},
+    {"R10",         RegName_R10},
+    {"R11",         RegName_R11},
+    {"R12",         RegName_R12},
+    {"R13",         RegName_R13},
+    {"R14",         RegName_R14},
+    {"R15",         RegName_R15},
+#endif
+
+    {"EAX",         RegName_EAX},
+    {"ECX",         RegName_ECX},
+    {"EDX",         RegName_EDX},
+    {"EBX",         RegName_EBX},
+    {"ESP",         RegName_ESP},
+    {"EBP",         RegName_EBP},
+    {"ESI",         RegName_ESI},
+    {"EDI",         RegName_EDI},
+#ifdef _EM64T_
+    {"R8D",         RegName_R8D},
+    {"R9D",         RegName_R9D},
+    {"R10D",        RegName_R10D},
+    {"R11D",        RegName_R11D},
+    {"R12D",        RegName_R12D},
+    {"R13D",        RegName_R13D},
+    {"R14D",        RegName_R14D},
+    {"R15D",        RegName_R15D},
+#endif
+
+    {"AX",          RegName_AX},
+    {"CX",          RegName_CX},
+    {"DX",          RegName_DX},
+    {"BX",          RegName_BX},
+    {"SP",          RegName_SP},
+    {"BP",          RegName_BP},
+    {"SI",          RegName_SI},
+    {"DI",          RegName_DI},
+
+    {"AL",          RegName_AL},
+    {"CL",          RegName_CL},
+    {"DL",          RegName_DL},
+    {"BL",          RegName_BL},
+#if !defined(_EM64T_)
+    {"AH",          RegName_AH},
+    {"CH",          RegName_CH},
+    {"DH",          RegName_DH},
+    {"BH",          RegName_BH},
+#else
+    {"SPL",         RegName_SPL},
+    {"BPL",         RegName_BPL},
+    {"SIL",         RegName_SIL},
+    {"DIL",         RegName_DIL},
+    {"R8L",         RegName_R8L},
+    {"R9L",         RegName_R9L},
+    {"R10L",        RegName_R10L},
+    {"R11L",        RegName_R11L},
+    {"R12L",        RegName_R12L},
+    {"R13L",        RegName_R13L},
+    {"R14L",        RegName_R14L},
+    {"R15L",        RegName_R15L},
+#endif
+    {"ES",          RegName_ES},
+    {"CS",          RegName_CS},
+    {"SS",          RegName_SS},
+    {"DS",          RegName_DS},
+    {"FS",          RegName_FS},
+    {"GS",          RegName_GS},
+
+    {"FP0",         RegName_FP0},
+/*
+    {"FP1",         RegName_FP1},
+    {"FP2",         RegName_FP2},
+    {"FP3",         RegName_FP3},
+    {"FP4",         RegName_FP4},
+    {"FP5",         RegName_FP5},
+    {"FP6",         RegName_FP6},
+    {"FP7",         RegName_FP7},
+*/
+    {"FP0S",        RegName_FP0S},
+    {"FP1S",        RegName_FP1S},
+    {"FP2S",        RegName_FP2S},
+    {"FP3S",        RegName_FP3S},
+    {"FP4S",        RegName_FP4S},
+    {"FP5S",        RegName_FP5S},
+    {"FP6S",        RegName_FP6S},
+    {"FP7S",        RegName_FP7S},
+
+    {"FP0D",        RegName_FP0D},
+    {"FP1D",        RegName_FP1D},
+    {"FP2D",        RegName_FP2D},
+    {"FP3D",        RegName_FP3D},
+    {"FP4D",        RegName_FP4D},
+    {"FP5D",        RegName_FP5D},
+    {"FP6D",        RegName_FP6D},
+    {"FP7D",        RegName_FP7D},
+
+    {"XMM0",        RegName_XMM0},
+    {"XMM1",        RegName_XMM1},
+    {"XMM2",        RegName_XMM2},
+    {"XMM3",        RegName_XMM3},
+    {"XMM4",        RegName_XMM4},
+    {"XMM5",        RegName_XMM5},
+    {"XMM6",        RegName_XMM6},
+    {"XMM7",        RegName_XMM7},
+#ifdef _EM64T_
+    {"XMM8",       RegName_XMM8},
+    {"XMM9",       RegName_XMM9},
+    {"XMM10",      RegName_XMM10},
+    {"XMM11",      RegName_XMM11},
+    {"XMM12",      RegName_XMM12},
+    {"XMM13",      RegName_XMM13},
+    {"XMM14",      RegName_XMM14},
+    {"XMM15",      RegName_XMM15},
+#endif
+
+
+    {"XMM0S",       RegName_XMM0S},
+    {"XMM1S",       RegName_XMM1S},
+    {"XMM2S",       RegName_XMM2S},
+    {"XMM3S",       RegName_XMM3S},
+    {"XMM4S",       RegName_XMM4S},
+    {"XMM5S",       RegName_XMM5S},
+    {"XMM6S",       RegName_XMM6S},
+    {"XMM7S",       RegName_XMM7S},
+#ifdef _EM64T_
+    {"XMM8S",       RegName_XMM8S},
+    {"XMM9S",       RegName_XMM9S},
+    {"XMM10S",      RegName_XMM10S},
+    {"XMM11S",      RegName_XMM11S},
+    {"XMM12S",      RegName_XMM12S},
+    {"XMM13S",      RegName_XMM13S},
+    {"XMM14S",      RegName_XMM14S},
+    {"XMM15S",      RegName_XMM15S},
+#endif
+
+    {"XMM0D",       RegName_XMM0D},
+    {"XMM1D",       RegName_XMM1D},
+    {"XMM2D",       RegName_XMM2D},
+    {"XMM3D",       RegName_XMM3D},
+    {"XMM4D",       RegName_XMM4D},
+    {"XMM5D",       RegName_XMM5D},
+    {"XMM6D",       RegName_XMM6D},
+    {"XMM7D",       RegName_XMM7D},
+#ifdef _EM64T_
+    {"XMM8D",       RegName_XMM8D},
+    {"XMM9D",       RegName_XMM9D},
+    {"XMM10D",      RegName_XMM10D},
+    {"XMM11D",      RegName_XMM11D},
+    {"XMM12D",      RegName_XMM12D},
+    {"XMM13D",      RegName_XMM13D},
+    {"XMM14D",      RegName_XMM14D},
+    {"XMM15D",      RegName_XMM15D},
+#endif
+
+    {"EFLGS",       RegName_EFLAGS},
+};
+
+
+const char * getRegNameString(RegName reg)
+{
+    for (unsigned i = 0; i<COUNTOF(registers); i++) {
+        if (registers[i].regname == reg) {
+            return registers[i].regstring;
+        }
+    }
+    return NULL;
+}
+
+RegName getRegName(const char * regname)
+{
+    if (NULL == regname) {
+        return RegName_Null;
+    }
+
+    for (unsigned i = 0; i<COUNTOF(registers); i++) {
+        if (!strcmpi(regname,registers[i].regstring)) {
+            return registers[i].regname;
+        }
+    }
+    return RegName_Null;
+}
+
+ENCODER_NAMESPACE_END
diff --git a/vm/compiler/codegen/x86/libenc/enc_base.h b/vm/compiler/codegen/x86/libenc/enc_base.h
new file mode 100644
index 0000000..e90ad2b
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_base.h
@@ -0,0 +1,740 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+/**
+ * @file
+ * @brief Main encoding routines and structures.
+ */
+
+#ifndef __ENC_BASE_H_INCLUDED__
+#define __ENC_BASE_H_INCLUDED__
+
+#include "enc_defs.h"
+
+
+#include <stdlib.h>
+#include <assert.h>
+#include <memory.h>
+
+ENCODER_NAMESPACE_START
+struct MnemonicInfo;
+struct OpcodeInfo;
+struct Rex;
+
+/**
+ * @brief Basic facilities for generation of processor's instructions.
+ *
+ * The class EncoderBase represents the basic facilities for the encoding of
+ * processor's instructions on IA32 and EM64T platforms.
+ *
+ * The class provides general interface to generate the instructions as well
+ * as to retrieve some static data about instructions (number of arguments,
+ * their roles, etc).
+ *
+ * Currently, the EncoderBase class is used for both LIL and Jitrino code
+ * generators. Each of these code generators has its own wrapper to adapt
+ * this general interface for specific needs - see encoder.h for LIL wrappers
+ * and Ia32Encoder.h for Jitrino's adapter.
+ *
+ * Interface is provided through static methods, no instances of EncoderBase
+ * to be created.
+ *
+ * @todo RIP-based addressing on EM64T - it's not yet supported currently.
+ */
+class EncoderBase {
+public:
+    class Operands;
+    struct MnemonicDesc;
+    /**
+     * @brief Generates processor's instruction.
+     *
+     * @param stream - a buffer to generate into
+     * @param mn - \link Mnemonic mnemonic \endlink of the instruction
+     * @param opnds - operands for the instruction
+     * @returns (stream + length of the just generated instruction)
+     */
+    static char * encode(char * stream, Mnemonic mn, const Operands& opnds);
+    static char * getOpndLocation(int index);
+
+    /**
+     * @brief Generates the smallest possible number of NOP-s.
+     *
+     * Effectively generates the smallest possible number of instructions,
+     * which are NOP-s for CPU. Normally used to make a code alignment.
+     *
+     * The method inserts exactly number of bytes specified. It's a caller's
+     * responsibility to make sure the buffer is big enough.
+     *
+     * @param stream - buffer where to generate code into, can not be NULL
+     * @param howMany - how many bytes to fill with NOP-s
+     * @return \c (stream+howMany)
+     */
+    static char * nops(char * stream, unsigned howMany);
+
+    /**
+     * @brief Inserts a prefix into the code buffer.
+     *
+     * The method writes no more than one byte into the buffer. This is a
+     * caller's responsibility to make sure the buffer is big enough.
+     *
+     * @param stream - buffer where to insert the prefix
+     * @param pref - prefix to be inserted. If it's InstPrefix_Null, then
+     *        no action performed and return value is \c stream.
+     * @return \c (stream+1) if pref is not InstPrefix_Null, or \c stream
+     *         otherwise
+     */
+     static char * prefix(char* stream, InstPrefix pref);
+
+    /**
+     * @brief Determines if operand with opndExt suites the position with instExt.
+     */
+    static bool extAllowed(OpndExt opndExt, OpndExt instExt);
+
+    /**
+     * @brief Returns #MnemonicDesc by the given Mnemonic.
+     */
+    static const MnemonicDesc * getMnemonicDesc(Mnemonic mn)
+    {
+        assert(mn < Mnemonic_Count);
+        return mnemonics + mn;
+    }
+
+    /**
+     * @brief Returns a Mnemonic for the given name.
+     *
+     * The lookup is case insensitive, if no mnemonic found for the given
+     * string, then Mnemonic_Null returned.
+     */
+    static Mnemonic str2mnemonic(const char * mn_name);
+
+    /**
+     * @brief Returns a string representation of the given Mnemonic.
+     *
+     * If invalid mnemonic passed, then the behavior is unpredictable.
+     */
+    static const char * getMnemonicString(Mnemonic mn)
+    {
+        return getMnemonicDesc(mn)->name;
+    }
+
+    static const char * toStr(Mnemonic mn)
+    {
+        return getMnemonicDesc(mn)->name;
+    }
+
+
+    /**
+     * @brief Description of operand.
+     *
+     * Description of an operand in opcode - its kind, size or RegName if
+     * operand must be a particular register.
+     */
+    struct OpndDesc {
+        /**
+         * @brief Location of the operand.
+         *
+         * May be a mask, i.e. OpndKind_Imm|OpndKind_Mem.
+         */
+        OpndKind        kind;
+        /**
+         * @brief Size of the operand.
+         */
+        OpndSize        size;
+        /**
+         * @brief Extention of the operand.
+         */
+        OpndExt         ext;
+        /**
+         * @brief Appropriate RegName if operand must reside on a particular
+         *        register (i.e. CWD/CDQ instructions), RegName_Null
+         *        otherwise.
+         */
+        RegName         reg;
+    };
+
+    /**
+     * @brief Description of operands' roles in instruction.
+     */
+    struct OpndRolesDesc {
+        /**
+         * @brief Total number of operands in the operation.
+         */
+        unsigned                count;
+        /**
+         * @brief Number of defs in the operation.
+         */
+        unsigned                defCount;
+        /**
+         * @brief Number of uses in the operation.
+         */
+        unsigned                useCount;
+        /**
+         * @brief Operand roles, bit-packed.
+         *
+         * A bit-packed info about operands' roles. Each operand's role is
+         * described by two bits, counted from right-to-left - the less
+         * significant bits (0,1) represent operand#0.
+         *
+         * The mask is build by ORing #OpndRole_Def and #OpndRole_Use
+         * appropriately and shifting left, i.e. operand#0's role would be
+         * - '(OpndRole_Def|OpndRole_Use)'
+         * - opnd#1's role would be 'OpndRole_Use<<2'
+         * - and operand#2's role would be, say, 'OpndRole_Def<<4'.
+         */
+        unsigned                roles;
+    };
+
+    /**
+     * @brief Extracts appropriate OpndRole for a given operand.
+     *
+     * The order of operands is left-to-right, i.e. for MOV, it
+     * would be 'MOV op0, op1'
+     */
+    static OpndRole getOpndRoles(OpndRolesDesc ord, unsigned idx)
+    {
+        assert(idx < ord.count);
+        return (OpndRole)(ord.roles>>((ord.count-1-idx)*2) & 0x3);
+    }
+
+    /**
+     * @brief Info about single opcode - its opcode bytes, operands,
+     *        operands' roles.
+     */
+   union OpcodeDesc {
+       char dummy[128]; // To make total size a power of 2
+
+       struct {
+           /**
+           * @brief Raw opcode bytes.
+           *
+           * 'Raw' opcode bytes which do not require any analysis and are
+           * independent from arguments/sizes/etc (may include opcode size
+           * prefix).
+           */
+           char        opcode[5];
+           unsigned    opcode_len;
+           unsigned    aux0;
+           unsigned    aux1;
+           /**
+           * @brief Info about opcode's operands.
+           *
+           * The [3] mostly comes from IDIV/IMUL which both may have up to 3
+           * operands.
+           */
+           OpndDesc        opnds[3];
+           unsigned        first_opnd;
+           /**
+           * @brief Info about operands - total number, number of uses/defs,
+           *        operands' roles.
+           */
+           OpndRolesDesc   roles;
+           /**
+           * @brief If not zero, then this is final OpcodeDesc structure in
+           *        the list of opcodes for a given mnemonic.
+           */
+           char            last;
+           char            platf;
+       };
+   };
+public:
+    /**
+     * @brief General info about mnemonic.
+     */
+    struct MnemonicDesc {
+        /**
+        * @brief The mnemonic itself.
+        */
+        Mnemonic        mn;
+        /**
+        * Various characteristics of mnemonic.
+        * @see MF_
+         */
+        unsigned    flags;
+        /**
+         * @brief Operation's operand's count and roles.
+         *
+         * For the operations whose opcodes may use different number of
+         * operands (i.e. IMUL/SHL) either most common value used, or empty
+         * value left.
+         */
+        OpndRolesDesc   roles;
+        /**
+         * @brief Print name of the mnemonic.
+         */
+        const char *    name;
+    };
+
+
+    /**
+     * @brief Magic number, shows a maximum value a hash code can take.
+     *
+     * For meaning and arithmetics see enc_tabl.cpp.
+     *
+     * The value was increased from '5155' to '8192' to make it aligned
+     * for faster access in EncoderBase::lookup().
+     */
+    static const unsigned int               HASH_MAX = 8192; //5155;
+    /**
+     * @brief Empty value, used in hash-to-opcode map to show an empty slot.
+     */
+    static const unsigned char              NOHASH = 0xFF;
+    /**
+     * @brief The name says it all.
+     */
+    static const unsigned char              HASH_BITS_PER_OPERAND = 5;
+
+    /**
+     * @brief Contains info about a single instructions's operand - its
+     *        location, size and a value for immediate or RegName for
+     *        register operands.
+     */
+    class Operand {
+    public:
+        /**
+         * @brief Initializes the instance with empty size and kind.
+         */
+        Operand() : m_kind(OpndKind_Null), m_size(OpndSize_Null), m_ext(OpndExt_None), m_need_rex(false) {}
+        /**
+         * @brief Creates register operand from given RegName.
+         */
+        Operand(RegName reg, OpndExt ext = OpndExt_None) : m_kind(getRegKind(reg)),
+                               m_size(getRegSize(reg)),
+                               m_ext(ext), m_reg(reg)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates register operand from given RegName and with the
+         *        specified size and kind.
+         *
+         * Used to speedup Operand creation as there is no need to extract
+         * size and kind from the RegName.
+         * The provided size and kind must match the RegName's ones though.
+         */
+        Operand(OpndSize sz, OpndKind kind, RegName reg, OpndExt ext = OpndExt_None) :
+            m_kind(kind), m_size(sz), m_ext(ext), m_reg(reg)
+        {
+            assert(m_size == getRegSize(reg));
+            assert(m_kind == getRegKind(reg));
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand with the given size and value.
+         */
+        Operand(OpndSize size, long long ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(size), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand of OpndSize_32.
+         */
+        Operand(int ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_32), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+        /**
+         * @brief Creates immediate operand of OpndSize_16.
+         */
+        Operand(short ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_16), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+
+        /**
+         * @brief Creates immediate operand of OpndSize_8.
+         */
+        Operand(char ival, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Imm), m_size(OpndSize_8), m_ext(ext), m_imm64(ival)
+        {
+            hash_it();
+        }
+
+        /**
+         * @brief Creates memory operand.
+         */
+        Operand(OpndSize size, RegName base, RegName index, unsigned scale,
+                int disp, OpndExt ext = OpndExt_None) : m_kind(OpndKind_Mem), m_size(size), m_ext(ext)
+        {
+            m_base = base;
+            m_index = index;
+            m_scale = scale;
+            m_disp = disp;
+            hash_it();
+        }
+
+        /**
+         * @brief Creates memory operand with only base and displacement.
+         */
+        Operand(OpndSize size, RegName base, int disp, OpndExt ext = OpndExt_None) :
+            m_kind(OpndKind_Mem), m_size(size), m_ext(ext)
+        {
+            m_base = base;
+            m_index = RegName_Null;
+            m_scale = 0;
+            m_disp = disp;
+            hash_it();
+        }
+        //
+        // general info
+        //
+        /**
+         * @brief Returns kind of the operand.
+         */
+        OpndKind kind(void) const { return m_kind; }
+        /**
+         * @brief Returns size of the operand.
+         */
+        OpndSize size(void) const { return m_size; }
+        /**
+         * @brief Returns extention of the operand.
+         */
+        OpndExt ext(void) const { return m_ext; }
+        /**
+         * @brief Returns hash of the operand.
+         */
+        unsigned hash(void) const { return m_hash; }
+        //
+#ifdef _EM64T_
+        bool need_rex(void) const { return m_need_rex; }
+#else
+        bool need_rex(void) const { return false; }
+#endif
+        /**
+         * @brief Tests whether operand is memory operand.
+         */
+        bool is_mem(void) const { return is_placed_in(OpndKind_Mem); }
+        /**
+         * @brief Tests whether operand is immediate operand.
+         */
+        bool is_imm(void) const { return is_placed_in(OpndKind_Imm); }
+        /**
+         * @brief Tests whether operand is register operand.
+         */
+        bool is_reg(void) const { return is_placed_in(OpndKind_Reg); }
+        /**
+         * @brief Tests whether operand is general-purpose register operand.
+         */
+        bool is_gpreg(void) const { return is_placed_in(OpndKind_GPReg); }
+        /**
+         * @brief Tests whether operand is float-point pseudo-register operand.
+         */
+        bool is_fpreg(void) const { return is_placed_in(OpndKind_FPReg); }
+        /**
+         * @brief Tests whether operand is XMM register operand.
+         */
+        bool is_xmmreg(void) const { return is_placed_in(OpndKind_XMMReg); }
+#ifdef _HAVE_MMX_
+        /**
+         * @brief Tests whether operand is MMX register operand.
+         */
+        bool is_mmxreg(void) const { return is_placed_in(OpndKind_MMXReg); }
+#endif
+        /**
+         * @brief Tests whether operand is signed immediate operand.
+         */
+        //bool is_signed(void) const { assert(is_imm()); return m_is_signed; }
+
+        /**
+         * @brief Returns base of memory operand (RegName_Null if not memory).
+         */
+        RegName base(void) const { return is_mem() ? m_base : RegName_Null; }
+        /**
+         * @brief Returns index of memory operand (RegName_Null if not memory).
+         */
+        RegName index(void) const { return is_mem() ? m_index : RegName_Null; }
+        /**
+         * @brief Returns scale of memory operand (0 if not memory).
+         */
+        unsigned scale(void) const { return is_mem() ? m_scale : 0; }
+        /**
+         * @brief Returns displacement of memory operand (0 if not memory).
+         */
+        int disp(void) const { return is_mem() ? m_disp : 0; }
+        /**
+         * @brief Returns RegName of register operand (RegName_Null if not
+         *        register).
+         */
+        RegName reg(void) const { return is_reg() ? m_reg : RegName_Null; }
+        /**
+         * @brief Returns value of immediate operand (0 if not immediate).
+         */
+        long long imm(void) const { return is_imm() ? m_imm64 : 0; }
+    private:
+        bool is_placed_in(OpndKind kd) const
+        {
+                return kd == OpndKind_Reg ?
+                        m_kind == OpndKind_GPReg ||
+#ifdef _HAVE_MMX_
+                        m_kind == OpndKind_MMXReg ||
+#endif
+                        m_kind == OpndKind_FPReg ||
+                        m_kind == OpndKind_XMMReg
+                        : kd == m_kind;
+        }
+        void hash_it(void)
+        {
+            m_hash = get_size_hash(m_size) | get_kind_hash(m_kind);
+#ifdef _EM64T_
+            m_need_rex = false;
+            if (is_reg() && is_em64t_extra_reg(m_reg)) {
+                m_need_rex = true;
+            }
+            else if (is_mem() && (is_em64t_extra_reg(m_base) ||
+                                  is_em64t_extra_reg(m_index))) {
+                m_need_rex = true;
+            }
+#endif
+        }
+        // general info
+        OpndKind    m_kind;
+        OpndSize    m_size;
+        OpndExt     m_ext;
+        // complex address form support
+        RegName     m_base;
+        RegName     m_index;
+        unsigned    m_scale;
+        union {
+            int         m_disp;
+            RegName     m_reg;
+            long long   m_imm64;
+        };
+        unsigned    m_hash;
+        bool        m_need_rex;
+        friend class EncoderBase::Operands;
+    };
+    /**
+     * @brief Simple container for up to 3 Operand-s.
+     */
+    class Operands {
+    public:
+        Operands(void)
+        {
+            clear();
+        }
+        Operands(const Operand& op0)
+        {
+            clear();
+            add(op0);
+        }
+
+        Operands(const Operand& op0, const Operand& op1)
+        {
+            clear();
+            add(op0); add(op1);
+        }
+
+        Operands(const Operand& op0, const Operand& op1, const Operand& op2)
+        {
+            clear();
+            add(op0); add(op1); add(op2);
+        }
+
+        unsigned count(void) const { return m_count; }
+        unsigned hash(void) const { return m_hash; }
+        const Operand& operator[](unsigned idx) const
+        {
+            assert(idx<m_count);
+            return m_operands[idx];
+        }
+
+        void add(const Operand& op)
+        {
+            assert(m_count < COUNTOF(m_operands));
+            m_hash = (m_hash<<HASH_BITS_PER_OPERAND) | op.hash();
+            m_operands[m_count++] = op;
+            m_need_rex = m_need_rex || op.m_need_rex;
+        }
+#ifdef _EM64T_
+        bool need_rex(void) const { return m_need_rex; }
+#else
+        bool need_rex(void) const { return false; }
+#endif
+        void clear(void)
+        {
+            m_count = 0; m_hash = 0; m_need_rex = false;
+        }
+    private:
+        unsigned    m_count;
+        Operand     m_operands[COUNTOF( ((OpcodeDesc*)NULL)->opnds )];
+        unsigned    m_hash;
+        bool        m_need_rex;
+    };
+public:
+#ifdef _DEBUG
+    /**
+     * Verifies some presumptions about encoding data table.
+     * Called automaticaly during statics initialization.
+     */
+    static int verify(void);
+#endif
+
+private:
+    /**
+     * @brief Returns found OpcodeDesc by the given Mnemonic and operands.
+     */
+    static const OpcodeDesc * lookup(Mnemonic mn, const Operands& opnds);
+    /**
+     * @brief Encodes mod/rm byte.
+     */
+    static char* encodeModRM(char* stream, const Operands& opnds,
+                             unsigned idx, const OpcodeDesc * odesc, Rex * prex);
+    /**
+     * @brief Encodes special things of opcode description - '/r', 'ib', etc.
+     */
+    static char* encode_aux(char* stream, unsigned aux,
+                            const Operands& opnds, const OpcodeDesc * odesc,
+                            unsigned * pargsCount, Rex* prex);
+#ifdef _EM64T_
+    /**
+     * @brief Returns true if the 'reg' argument represents one of the new
+     *        EM64T registers - R8(D)-R15(D).
+     *
+     * The 64 bits versions of 'old-fashion' registers, i.e. RAX are not
+     * considered as 'extra'.
+     */
+    static bool is_em64t_extra_reg(const RegName reg)
+    {
+        if (needs_rex_r(reg)) {
+            return true;
+        }
+        if (RegName_SPL <= reg && reg <= RegName_R15L) {
+            return true;
+        }
+        return false;
+    }
+    static bool needs_rex_r(const RegName reg)
+    {
+        if (RegName_R8 <= reg && reg <= RegName_R15) {
+            return true;
+        }
+        if (RegName_R8D <= reg && reg <= RegName_R15D) {
+            return true;
+        }
+        if (RegName_R8S <= reg && reg <= RegName_R15S) {
+            return true;
+        }
+        if (RegName_R8L <= reg && reg <= RegName_R15L) {
+            return true;
+        }
+        if (RegName_XMM8 <= reg && reg <= RegName_XMM15) {
+            return true;
+        }
+        if (RegName_XMM8D <= reg && reg <= RegName_XMM15D) {
+            return true;
+        }
+        if (RegName_XMM8S <= reg && reg <= RegName_XMM15S) {
+            return true;
+        }
+        return false;
+    }
+    /**
+     * @brief Returns an 'processor's index' of the register - the index
+     *        used to encode the register in ModRM/SIB bytes.
+     *
+     * For the new EM64T registers the 'HW index' differs from the index
+     * encoded in RegName. For old-fashion registers it's effectively the
+     * same as ::getRegIndex(RegName).
+     */
+    static unsigned char getHWRegIndex(const RegName reg)
+    {
+        if (getRegKind(reg) != OpndKind_GPReg) {
+            return getRegIndex(reg);
+        }
+        if (RegName_SPL <= reg && reg<=RegName_DIL) {
+            return getRegIndex(reg);
+        }
+        if (RegName_R8L<= reg && reg<=RegName_R15L) {
+            return getRegIndex(reg) - getRegIndex(RegName_R8L);
+        }
+        return is_em64t_extra_reg(reg) ?
+                getRegIndex(reg)-getRegIndex(RegName_R8D) : getRegIndex(reg);
+    }
+#else
+    static unsigned char getHWRegIndex(const RegName reg)
+    {
+        return getRegIndex(reg);
+    }
+    static bool is_em64t_extra_reg(const RegName reg)
+    {
+        return false;
+    }
+#endif
+public:
+    static unsigned char get_size_hash(OpndSize size) {
+        return (size <= OpndSize_64) ? size_hash[size] : 0xFF;
+    }
+    static unsigned char get_kind_hash(OpndKind kind) {
+        return (kind <= OpndKind_Mem) ? kind_hash[kind] : 0xFF;
+    }
+
+    /**
+     * @brief A table used for the fast computation of hash value.
+     *
+     * A change must be strictly balanced with hash-related functions and data
+     * in enc_base.h/.cpp.
+     */
+    static const unsigned char size_hash[OpndSize_64+1];
+    /**
+     * @brief A table used for the fast computation of hash value.
+     *
+     * A change must be strictly balanced with hash-related functions and data
+     * in enc_base.h/.cpp.
+     */
+    static const unsigned char kind_hash[OpndKind_Mem+1];
+    /**
+     * @brief Maximum number of opcodes used for a single mnemonic.
+     *
+     * No arithmetics behind the number, simply estimated.
+     */
+    static const unsigned int   MAX_OPCODES = 32; //20;
+    /**
+     * @brief Mapping between operands hash code and operands.
+     */
+    static unsigned char    opcodesHashMap[Mnemonic_Count][HASH_MAX];
+    /**
+     * @brief Array of mnemonics.
+     */
+    static MnemonicDesc         mnemonics[Mnemonic_Count];
+    /**
+     * @brief Array of available opcodes.
+     */
+    static OpcodeDesc opcodes[Mnemonic_Count][MAX_OPCODES];
+
+    static int buildTable(void);
+    static void buildMnemonicDesc(const MnemonicInfo * minfo);
+    /**
+     * @brief Computes hash value for the given operands.
+     */
+    static unsigned short getHash(const OpcodeInfo* odesc);
+    /**
+     * @brief Dummy variable, for automatic invocation of buildTable() at
+     *        startup.
+     */
+    static int dummy;
+
+    static char * curRelOpnd[3];
+};
+
+ENCODER_NAMESPACE_END
+
+#endif // ifndef __ENC_BASE_H_INCLUDED__
diff --git a/vm/compiler/codegen/x86/libenc/enc_defs.h b/vm/compiler/codegen/x86/libenc/enc_defs.h
new file mode 100644
index 0000000..ac0bb3b
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_defs.h
@@ -0,0 +1,786 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#ifndef _ENCODER_DEFS_H_
+#define _ENCODER_DEFS_H_
+
+
+// Used to isolate experimental or being tuned encoder into a separate
+// namespace so it can coexist with a stable one in the same bundle.
+#ifdef ENCODER_ISOLATE
+    #define ENCODER_NAMESPACE_START namespace enc_ia32 {
+    #define ENCODER_NAMESPACE_END };
+#else
+    #define ENCODER_NAMESPACE_START
+    #define ENCODER_NAMESPACE_END
+#endif
+
+#include <assert.h>
+#include "enc_defs_ext.h"
+
+#ifndef COUNTOF
+    /**
+     * Number of items in an array.
+     */
+    #define COUNTOF(a)      (sizeof(a)/sizeof(a[0]))
+#endif
+
+#ifdef _EM64T_
+    /**
+     * A stack pointer of default platform's size.
+     */
+    #define REG_STACK       RegName_RSP
+    /**
+     * A max GP register (with a highest index number)
+     */
+    #define REG_MAX         RegName_R15
+    /**
+     * Total number of GP registers including stack pointer.
+     */
+    #define MAX_REGS        15
+#else
+    #define REG_STACK       RegName_ESP
+    #define REG_MAX         RegName_EDI
+    #define MAX_REGS        8
+#endif
+
+ENCODER_NAMESPACE_START
+
+/**
+ * A number of bytes 'eaten' by an ordinary PUSH/POP.
+ */
+#define STACK_SLOT_SIZE (sizeof(void*))
+
+
+/**
+ * A recommended by Intel Arch Manual aligment for instructions that
+ * are targets for jmps.
+ */
+#define JMP_TARGET_ALIGMENT     (16)
+/**
+ * A maximum possible size of native instruction.
+ */
+#define MAX_NATIVE_INST_SIZE (15)
+/**
+ * The enum OpndKind describes an operand's location - memory, immediate or a register.
+ * It can be used as a bit mask.
+ */
+typedef enum OpndKind {
+    /**
+     * A change must be balanced with at least the following places:
+     *              Ia32::Constraint-s use the OpndKind as a mask
+     *              encoder.cpp & encoder_master_info.cpp uses OpndKind as an index for hashing
+     *              - perhaps there are much more places
+     *
+     * NOTE: an MMXReg kind is incompatible with the current constraints framework,
+     *              as it's not encoded as a mask.
+     */
+    OpndKind_Null=0,
+    OpndKind_GPReg          = 0x01, OpndKind_MinRegKind = OpndKind_GPReg,
+    OpndKind_SReg           = 0x02,
+#ifdef _HAVE_MMX_
+    OpndKind_MMXReg         = 0x03,
+#endif
+    OpndKind_FPReg          = 0x04,
+    OpndKind_XMMReg         = 0x08,
+    OpndKind_OtherReg       = 0x10,
+    OpndKind_StatusReg      = OpndKind_OtherReg,
+    OpndKind_MaxRegKind     = OpndKind_StatusReg,   // a max existing kind of register
+    OpndKind_MaxReg,                                // -'- + 1 to be used in array defs
+    //
+    OpndKind_Immediate      = 0x20, OpndKind_Imm=OpndKind_Immediate,
+    OpndKind_Memory         = 0x40, OpndKind_Mem=OpndKind_Memory,
+    //
+    OpndKind_Reg            = 0x1F,
+    OpndKind_Any            = 0x7F,
+    // syntetic constants. Normally not used anywhere, but are used for
+    // human-readable showing under the debugger
+    OpndKind_GPReg_Mem      = OpndKind_GPReg|OpndKind_Mem,
+#ifdef _HAVE_MMX_
+    OpndKind_MMXReg_Mem     = OpndKind_MMXReg|OpndKind_Mem,
+#endif
+    OpndKind_XMMReg_Mem     = OpndKind_XMMReg|OpndKind_Mem,
+} OpndKind;
+
+/**
+ * Defines type of extention allowed for particular operand.
+ * For example imul r32,r_m32,imm8 sign extend imm8 before performing multiplication.
+ * To satisfy instruction constraints immediate operand should be either OpndExt_Signed
+ * or OpndExt_Any.
+ */
+typedef enum OpndExt {
+    OpndExt_None    = 0x0,
+    OpndExt_Signed  = 0x1,
+    OpndExt_Zero    = 0x2,
+    OpndExt_Any     = 0x3,
+}OpndExt;
+
+/**
+ * enum OpndRole defines the role of an operand in an instruction
+ * Can be used as mask to combine def and use. The complete def+use
+ * info can be combined in 2 bits which is used, say in Encoder::OpndRole.
+ */
+//TODO: this duplicates an Role used in the Ia32::Inst. That duplicate enum should be removed.
+typedef enum OpndRole {
+    OpndRole_Null=0,
+    OpndRole_Use=0x1,
+    OpndRole_Def=0x2,
+    OpndRole_UseDef=OpndRole_Use|OpndRole_Def,
+    OpndRole_All=0xffff,
+} OpndRole;
+
+
+#define REGNAME(k,s,i) ( ((k & OpndKind_Any)<<24) | ((s & OpndSize_Any)<<16) | (i&0xFF) )
+
+// Gregory -
+// It is critical that all register indexes (3rd number) inside of the
+// following table go in ascending order. That is R8 goes after
+// RDI. It is necessary for decoder when extending registers from RAX-RDI
+// to R8-R15 by simply adding 8 to the index on EM64T architecture
+typedef enum RegName {
+
+    RegName_Null = 0,
+
+#ifdef _EM64T_
+    /*
+    An index part of the RegName-s for RAX-RDI, EAX-ESI, AX-SI and AL-BH is
+    the same as the index used during instructions encoding. The same rule
+    applies for XMM regsters for IA32.
+    For new EM64T registers (both GP and XMM) the index need to be corrected to
+    obtain the index used in processor's instructions.
+    */
+    RegName_RAX = REGNAME(OpndKind_GPReg,OpndSize_64,0),
+    RegName_RCX = REGNAME(OpndKind_GPReg,OpndSize_64,1),
+    RegName_RDX = REGNAME(OpndKind_GPReg,OpndSize_64,2),
+    RegName_RBX = REGNAME(OpndKind_GPReg,OpndSize_64,3),
+    RegName_RSP = REGNAME(OpndKind_GPReg,OpndSize_64,4),
+    RegName_RBP = REGNAME(OpndKind_GPReg,OpndSize_64,5),
+    RegName_RSI = REGNAME(OpndKind_GPReg,OpndSize_64,6),
+    RegName_RDI = REGNAME(OpndKind_GPReg,OpndSize_64,7),
+
+    RegName_R8  = REGNAME(OpndKind_GPReg,OpndSize_64,8),
+    RegName_R9  = REGNAME(OpndKind_GPReg,OpndSize_64,9),
+    RegName_R10 = REGNAME(OpndKind_GPReg,OpndSize_64,10),
+    RegName_R11 = REGNAME(OpndKind_GPReg,OpndSize_64,11),
+    RegName_R12 = REGNAME(OpndKind_GPReg,OpndSize_64,12),
+    RegName_R13 = REGNAME(OpndKind_GPReg,OpndSize_64,13),
+    RegName_R14 = REGNAME(OpndKind_GPReg,OpndSize_64,14),
+    RegName_R15 = REGNAME(OpndKind_GPReg,OpndSize_64,15),
+#endif //~_EM64T_
+
+    RegName_EAX=REGNAME(OpndKind_GPReg,OpndSize_32,0),
+    RegName_ECX=REGNAME(OpndKind_GPReg,OpndSize_32,1),
+    RegName_EDX=REGNAME(OpndKind_GPReg,OpndSize_32,2),
+    RegName_EBX=REGNAME(OpndKind_GPReg,OpndSize_32,3),
+    RegName_ESP=REGNAME(OpndKind_GPReg,OpndSize_32,4),
+    RegName_EBP=REGNAME(OpndKind_GPReg,OpndSize_32,5),
+    RegName_ESI=REGNAME(OpndKind_GPReg,OpndSize_32,6),
+    RegName_EDI=REGNAME(OpndKind_GPReg,OpndSize_32,7),
+
+#ifdef _EM64T_
+    RegName_R8D  = REGNAME(OpndKind_GPReg,OpndSize_32,8),
+    RegName_R9D  = REGNAME(OpndKind_GPReg,OpndSize_32,9),
+    RegName_R10D = REGNAME(OpndKind_GPReg,OpndSize_32,10),
+    RegName_R11D = REGNAME(OpndKind_GPReg,OpndSize_32,11),
+    RegName_R12D = REGNAME(OpndKind_GPReg,OpndSize_32,12),
+    RegName_R13D = REGNAME(OpndKind_GPReg,OpndSize_32,13),
+    RegName_R14D = REGNAME(OpndKind_GPReg,OpndSize_32,14),
+    RegName_R15D = REGNAME(OpndKind_GPReg,OpndSize_32,15),
+#endif //~_EM64T_
+
+    RegName_AX=REGNAME(OpndKind_GPReg,OpndSize_16,0),
+    RegName_CX=REGNAME(OpndKind_GPReg,OpndSize_16,1),
+    RegName_DX=REGNAME(OpndKind_GPReg,OpndSize_16,2),
+    RegName_BX=REGNAME(OpndKind_GPReg,OpndSize_16,3),
+    RegName_SP=REGNAME(OpndKind_GPReg,OpndSize_16,4),
+    RegName_BP=REGNAME(OpndKind_GPReg,OpndSize_16,5),
+    RegName_SI=REGNAME(OpndKind_GPReg,OpndSize_16,6),
+    RegName_DI=REGNAME(OpndKind_GPReg,OpndSize_16,7),
+
+#ifdef _EM64T_
+    RegName_R8S  = REGNAME(OpndKind_GPReg,OpndSize_16,8),
+    RegName_R9S  = REGNAME(OpndKind_GPReg,OpndSize_16,9),
+    RegName_R10S = REGNAME(OpndKind_GPReg,OpndSize_16,10),
+    RegName_R11S = REGNAME(OpndKind_GPReg,OpndSize_16,11),
+    RegName_R12S = REGNAME(OpndKind_GPReg,OpndSize_16,12),
+    RegName_R13S = REGNAME(OpndKind_GPReg,OpndSize_16,13),
+    RegName_R14S = REGNAME(OpndKind_GPReg,OpndSize_16,14),
+    RegName_R15S = REGNAME(OpndKind_GPReg,OpndSize_16,15),
+#endif //~_EM64T_
+
+    RegName_AL=REGNAME(OpndKind_GPReg,OpndSize_8,0),
+    RegName_CL=REGNAME(OpndKind_GPReg,OpndSize_8,1),
+    RegName_DL=REGNAME(OpndKind_GPReg,OpndSize_8,2),
+    RegName_BL=REGNAME(OpndKind_GPReg,OpndSize_8,3),
+    // FIXME: Used in enc_tabl.cpp
+    // AH is not accessible on EM64T, instead encoded register is SPL, so decoded
+    // register will return incorrect enum
+    RegName_AH=REGNAME(OpndKind_GPReg,OpndSize_8,4),
+#if !defined(_EM64T_)
+    RegName_CH=REGNAME(OpndKind_GPReg,OpndSize_8,5),
+    RegName_DH=REGNAME(OpndKind_GPReg,OpndSize_8,6),
+    RegName_BH=REGNAME(OpndKind_GPReg,OpndSize_8,7),
+#else
+    RegName_SPL=REGNAME(OpndKind_GPReg,OpndSize_8,4),
+    RegName_BPL=REGNAME(OpndKind_GPReg,OpndSize_8,5),
+    RegName_SIL=REGNAME(OpndKind_GPReg,OpndSize_8,6),
+    RegName_DIL=REGNAME(OpndKind_GPReg,OpndSize_8,7),
+    RegName_R8L=REGNAME(OpndKind_GPReg,OpndSize_8,8),
+    RegName_R9L=REGNAME(OpndKind_GPReg,OpndSize_8,9),
+    RegName_R10L=REGNAME(OpndKind_GPReg,OpndSize_8,10),
+    RegName_R11L=REGNAME(OpndKind_GPReg,OpndSize_8,11),
+    RegName_R12L=REGNAME(OpndKind_GPReg,OpndSize_8,12),
+    RegName_R13L=REGNAME(OpndKind_GPReg,OpndSize_8,13),
+    RegName_R14L=REGNAME(OpndKind_GPReg,OpndSize_8,14),
+    RegName_R15L=REGNAME(OpndKind_GPReg,OpndSize_8,15),
+#endif
+
+    RegName_ES=REGNAME(OpndKind_SReg,OpndSize_16,0),
+    RegName_CS=REGNAME(OpndKind_SReg,OpndSize_16,1),
+    RegName_SS=REGNAME(OpndKind_SReg,OpndSize_16,2),
+    RegName_DS=REGNAME(OpndKind_SReg,OpndSize_16,3),
+    RegName_FS=REGNAME(OpndKind_SReg,OpndSize_16,4),
+    RegName_GS=REGNAME(OpndKind_SReg,OpndSize_16,5),
+
+    RegName_EFLAGS=REGNAME(OpndKind_StatusReg,OpndSize_32,0),
+
+#if !defined(TESTING_ENCODER)
+    RegName_FP0=REGNAME(OpndKind_FPReg,OpndSize_80,0),
+    RegName_FP1=REGNAME(OpndKind_FPReg,OpndSize_80,1),
+    RegName_FP2=REGNAME(OpndKind_FPReg,OpndSize_80,2),
+    RegName_FP3=REGNAME(OpndKind_FPReg,OpndSize_80,3),
+    RegName_FP4=REGNAME(OpndKind_FPReg,OpndSize_80,4),
+    RegName_FP5=REGNAME(OpndKind_FPReg,OpndSize_80,5),
+    RegName_FP6=REGNAME(OpndKind_FPReg,OpndSize_80,6),
+    RegName_FP7=REGNAME(OpndKind_FPReg,OpndSize_80,7),
+#endif
+    RegName_FP0S=REGNAME(OpndKind_FPReg,OpndSize_32,0),
+    RegName_FP1S=REGNAME(OpndKind_FPReg,OpndSize_32,1),
+    RegName_FP2S=REGNAME(OpndKind_FPReg,OpndSize_32,2),
+    RegName_FP3S=REGNAME(OpndKind_FPReg,OpndSize_32,3),
+    RegName_FP4S=REGNAME(OpndKind_FPReg,OpndSize_32,4),
+    RegName_FP5S=REGNAME(OpndKind_FPReg,OpndSize_32,5),
+    RegName_FP6S=REGNAME(OpndKind_FPReg,OpndSize_32,6),
+    RegName_FP7S=REGNAME(OpndKind_FPReg,OpndSize_32,7),
+
+    RegName_FP0D=REGNAME(OpndKind_FPReg,OpndSize_64,0),
+    RegName_FP1D=REGNAME(OpndKind_FPReg,OpndSize_64,1),
+    RegName_FP2D=REGNAME(OpndKind_FPReg,OpndSize_64,2),
+    RegName_FP3D=REGNAME(OpndKind_FPReg,OpndSize_64,3),
+    RegName_FP4D=REGNAME(OpndKind_FPReg,OpndSize_64,4),
+    RegName_FP5D=REGNAME(OpndKind_FPReg,OpndSize_64,5),
+    RegName_FP6D=REGNAME(OpndKind_FPReg,OpndSize_64,6),
+    RegName_FP7D=REGNAME(OpndKind_FPReg,OpndSize_64,7),
+
+#if !defined(TESTING_ENCODER)
+    RegName_XMM0=REGNAME(OpndKind_XMMReg,OpndSize_128,0),
+    RegName_XMM1=REGNAME(OpndKind_XMMReg,OpndSize_128,1),
+    RegName_XMM2=REGNAME(OpndKind_XMMReg,OpndSize_128,2),
+    RegName_XMM3=REGNAME(OpndKind_XMMReg,OpndSize_128,3),
+    RegName_XMM4=REGNAME(OpndKind_XMMReg,OpndSize_128,4),
+    RegName_XMM5=REGNAME(OpndKind_XMMReg,OpndSize_128,5),
+    RegName_XMM6=REGNAME(OpndKind_XMMReg,OpndSize_128,6),
+    RegName_XMM7=REGNAME(OpndKind_XMMReg,OpndSize_128,7),
+
+#ifdef _EM64T_
+    RegName_XMM8  = REGNAME(OpndKind_XMMReg,OpndSize_128,0),
+    RegName_XMM9  = REGNAME(OpndKind_XMMReg,OpndSize_128,1),
+    RegName_XMM10 = REGNAME(OpndKind_XMMReg,OpndSize_128,2),
+    RegName_XMM11 = REGNAME(OpndKind_XMMReg,OpndSize_128,3),
+    RegName_XMM12 = REGNAME(OpndKind_XMMReg,OpndSize_128,4),
+    RegName_XMM13 = REGNAME(OpndKind_XMMReg,OpndSize_128,5),
+    RegName_XMM14 = REGNAME(OpndKind_XMMReg,OpndSize_128,6),
+    RegName_XMM15 = REGNAME(OpndKind_XMMReg,OpndSize_128,7),
+#endif //~_EM64T_
+
+#endif  // ~TESTING_ENCODER
+
+    RegName_XMM0S=REGNAME(OpndKind_XMMReg,OpndSize_32,0),
+    RegName_XMM1S=REGNAME(OpndKind_XMMReg,OpndSize_32,1),
+    RegName_XMM2S=REGNAME(OpndKind_XMMReg,OpndSize_32,2),
+    RegName_XMM3S=REGNAME(OpndKind_XMMReg,OpndSize_32,3),
+    RegName_XMM4S=REGNAME(OpndKind_XMMReg,OpndSize_32,4),
+    RegName_XMM5S=REGNAME(OpndKind_XMMReg,OpndSize_32,5),
+    RegName_XMM6S=REGNAME(OpndKind_XMMReg,OpndSize_32,6),
+    RegName_XMM7S=REGNAME(OpndKind_XMMReg,OpndSize_32,7),
+#ifdef _EM64T_
+    RegName_XMM8S=REGNAME(OpndKind_XMMReg,OpndSize_32,8),
+    RegName_XMM9S=REGNAME(OpndKind_XMMReg,OpndSize_32,9),
+    RegName_XMM10S=REGNAME(OpndKind_XMMReg,OpndSize_32,10),
+    RegName_XMM11S=REGNAME(OpndKind_XMMReg,OpndSize_32,11),
+    RegName_XMM12S=REGNAME(OpndKind_XMMReg,OpndSize_32,12),
+    RegName_XMM13S=REGNAME(OpndKind_XMMReg,OpndSize_32,13),
+    RegName_XMM14S=REGNAME(OpndKind_XMMReg,OpndSize_32,14),
+    RegName_XMM15S=REGNAME(OpndKind_XMMReg,OpndSize_32,15),
+#endif // ifdef _EM64T_
+    RegName_XMM0D=REGNAME(OpndKind_XMMReg,OpndSize_64,0),
+    RegName_XMM1D=REGNAME(OpndKind_XMMReg,OpndSize_64,1),
+    RegName_XMM2D=REGNAME(OpndKind_XMMReg,OpndSize_64,2),
+    RegName_XMM3D=REGNAME(OpndKind_XMMReg,OpndSize_64,3),
+    RegName_XMM4D=REGNAME(OpndKind_XMMReg,OpndSize_64,4),
+    RegName_XMM5D=REGNAME(OpndKind_XMMReg,OpndSize_64,5),
+    RegName_XMM6D=REGNAME(OpndKind_XMMReg,OpndSize_64,6),
+    RegName_XMM7D=REGNAME(OpndKind_XMMReg,OpndSize_64,7),
+#ifdef _EM64T_
+    RegName_XMM8D=REGNAME(OpndKind_XMMReg,OpndSize_64,8),
+    RegName_XMM9D=REGNAME(OpndKind_XMMReg,OpndSize_64,9),
+    RegName_XMM10D=REGNAME(OpndKind_XMMReg,OpndSize_64,10),
+    RegName_XMM11D=REGNAME(OpndKind_XMMReg,OpndSize_64,11),
+    RegName_XMM12D=REGNAME(OpndKind_XMMReg,OpndSize_64,12),
+    RegName_XMM13D=REGNAME(OpndKind_XMMReg,OpndSize_64,13),
+    RegName_XMM14D=REGNAME(OpndKind_XMMReg,OpndSize_64,14),
+    RegName_XMM15D=REGNAME(OpndKind_XMMReg,OpndSize_64,15),
+#endif // ifdef _EM64T_
+#ifdef _HAVE_MMX_
+    RegName_MMX0=REGNAME(OpndKind_MMXReg,OpndSize_64,0),
+    RegName_MMX1=REGNAME(OpndKind_MMXReg,OpndSize_64,1),
+    RegName_MMX2=REGNAME(OpndKind_MMXReg,OpndSize_64,2),
+    RegName_MMX3=REGNAME(OpndKind_MMXReg,OpndSize_64,3),
+    RegName_MMX4=REGNAME(OpndKind_MMXReg,OpndSize_64,4),
+    RegName_MMX5=REGNAME(OpndKind_MMXReg,OpndSize_64,5),
+    RegName_MMX6=REGNAME(OpndKind_MMXReg,OpndSize_64,6),
+    RegName_MMX7=REGNAME(OpndKind_MMXReg,OpndSize_64,7),
+#endif  // _HAVE_MMX_
+} RegName;
+
+#if 0   // Android x86: use mnemonics defined in enc_defs_ext.h
+/**
+ * Conditional mnemonics.
+ * The values match the 'real' (==processor's) values of the appropriate
+ * condition values used in the opcodes.
+ */
+enum ConditionMnemonic {
+
+    ConditionMnemonic_O=0,
+    ConditionMnemonic_NO=1,
+    ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B,
+    ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB,
+    ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z,
+    ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ,
+    ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE,
+    ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE,
+
+    ConditionMnemonic_S=8,
+    ConditionMnemonic_NS=9,
+    ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P,
+    ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP,
+    ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L,
+    ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL,
+    ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE,
+    ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE,
+    ConditionMnemonic_Count=16
+};
+
+
+#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond
+
+//=========================================================================================================
+enum Mnemonic {
+
+Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL,
+Mnemonic_ADC,                           // Add with Carry
+Mnemonic_ADD,                           // Add
+Mnemonic_ADDSD,                         // Add Scalar Double-Precision Floating-Point Values
+Mnemonic_ADDSS,                         // Add Scalar Single-Precision Floating-Point Values
+Mnemonic_AND,                           // Logical AND
+
+Mnemonic_BSF,                           // Bit scan forward
+Mnemonic_BSR,                           // Bit scan reverse
+
+Mnemonic_CALL,                          // Call Procedure
+Mnemonic_CMC,                           // Complement Carry Flag
+Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword
+Mnemonic_CMOVcc,                        // Conditional Move
+    CCM(CMOV,O),
+    CCM(CMOV,NO),
+    CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C),
+    CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC),
+    CCM(CMOV,Z), CCM(CMOV,E),
+    CCM(CMOV,NZ), CCM(CMOV,NE),
+    CCM(CMOV,BE), CCM(CMOV,NA),
+    CCM(CMOV,NBE), CCM(CMOV,A),
+
+    CCM(CMOV,S),
+    CCM(CMOV,NS),
+    CCM(CMOV,P), CCM(CMOV,PE),
+    CCM(CMOV,NP), CCM(CMOV,PO),
+    CCM(CMOV,L), CCM(CMOV,NGE),
+    CCM(CMOV,NL), CCM(CMOV,GE),
+    CCM(CMOV,LE), CCM(CMOV,NG),
+    CCM(CMOV,NLE), CCM(CMOV,G),
+
+Mnemonic_CMP,                           // Compare Two Operands
+Mnemonic_CMPXCHG,                       // Compare and exchange
+Mnemonic_CMPXCHG8B,                     // Compare and Exchange 8 Bytes
+Mnemonic_CMPSB,                         // Compare Two Bytes at DS:ESI and ES:EDI
+Mnemonic_CMPSW,                         // Compare Two Words at DS:ESI and ES:EDI
+Mnemonic_CMPSD,                         // Compare Two Doublewords at DS:ESI and ES:EDI
+//
+// double -> float
+Mnemonic_CVTSD2SS,                      // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
+// double -> I_32
+Mnemonic_CVTSD2SI,                      // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer
+// double [truncated] -> I_32
+Mnemonic_CVTTSD2SI,                     // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer
+//
+// float -> double
+Mnemonic_CVTSS2SD,                      // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value
+// float -> I_32
+Mnemonic_CVTSS2SI,                      // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer
+// float [truncated] -> I_32
+Mnemonic_CVTTSS2SI,                     // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer
+//
+// I_32 -> double
+Mnemonic_CVTSI2SD,                      // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value
+// I_32 -> float
+Mnemonic_CVTSI2SS,                      // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value
+
+Mnemonic_COMISD,                        // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_COMISS,                        // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_DEC,                           // Decrement by 1
+//Mnemonic_DIV,                         // Unsigned Divide
+Mnemonic_DIVSD,                         // Divide Scalar Double-Precision Floating-Point Values
+Mnemonic_DIVSS,                         // Divide Scalar Single-Precision Floating-Point Values
+
+#ifdef _HAVE_MMX_
+Mnemonic_EMMS,                          // Empty MMX Technology State
+#endif
+
+Mnemonic_ENTER,                         // ENTER-Make Stack Frame for Procedure Parameters
+Mnemonic_FLDCW,                         // Load FPU control word
+Mnemonic_FADDP,
+Mnemonic_FLDZ,
+Mnemonic_FADD,
+Mnemonic_FSUBP,
+Mnemonic_FSUB,
+Mnemonic_FISUB,
+Mnemonic_FMUL,
+Mnemonic_FMULP,
+Mnemonic_FDIVP,
+Mnemonic_FDIV,
+Mnemonic_FUCOMPP,
+Mnemonic_FRNDINT,
+Mnemonic_FNSTCW,                        // Store FPU control word
+Mnemonic_FSTSW,                         // Store FPU status word
+Mnemonic_FNSTSW,                         // Store FPU status word
+//Mnemonic_FDECSTP,                     // Decrement Stack-Top Pointer
+Mnemonic_FILD,                          // Load Integer
+Mnemonic_FLD,                           // Load Floating Point Value
+Mnemonic_FLDLG2,
+Mnemonic_FLDLN2,
+Mnemonic_FLD1,
+
+Mnemonic_FCLEX,                         // Clear Exceptions
+Mnemonic_FCHS,                          // Change sign of ST0
+Mnemonic_FNCLEX,                        // Clear Exceptions
+
+//Mnemonic_FINCSTP,                     // Increment Stack-Top Pointer
+Mnemonic_FIST,                          // Store Integer
+Mnemonic_FISTP,                         // Store Integer, pop FPU stack
+Mnemonic_FISTTP,                        // Store Integer with Truncation
+Mnemonic_FPREM,                         // Partial Remainder
+Mnemonic_FPREM1,                        // Partial Remainder
+Mnemonic_FST,                           // Store Floating Point Value
+Mnemonic_FSTP,                          // Store Floating Point Value and pop the FP stack
+Mnemonic_FSQRT,                         //Computes the square root of the source value in the stack and pop the FP stack
+Mnemonic_FABS,                          //Computes the absolute value of the source value in the stack and pop the FP stack
+Mnemonic_FSIN,                          //Computes the sine of the source value in the stack and pop the FP stack
+Mnemonic_FCOS,                          //Computes the cosine of the source value in the stack and pop the FP stack
+Mnemonic_FPTAN,                         //Computes the tangent of the source value in the stack and pop the FP stack
+Mnemonic_FYL2X,
+Mnemonic_FYL2XP1,
+Mnemonic_F2XM1,
+Mnemonic_FPATAN,
+Mnemonic_FXCH,
+Mnemonic_FSCALE,
+
+Mnemonic_XCHG,
+Mnemonic_DIV,                           // Unsigned Divide
+Mnemonic_IDIV,                          // Signed Divide
+Mnemonic_MUL,                           // Unsigned Multiply
+Mnemonic_IMUL,                          // Signed Multiply
+Mnemonic_INC,                           // Increment by 1
+Mnemonic_INT3,                          // Call break point
+Mnemonic_Jcc,                           // Jump if Condition Is Met
+    CCM(J,O),
+    CCM(J,NO),
+    CCM(J,B), CCM(J,NAE), CCM(J,C),
+    CCM(J,NB), CCM(J,AE), CCM(J,NC),
+    CCM(J,Z), CCM(J,E),
+    CCM(J,NZ), CCM(J,NE),
+    CCM(J,BE), CCM(J,NA),
+    CCM(J,NBE), CCM(J,A),
+    CCM(J,S),
+    CCM(J,NS),
+    CCM(J,P), CCM(J,PE),
+    CCM(J,NP), CCM(J,PO),
+    CCM(J,L), CCM(J,NGE),
+    CCM(J,NL), CCM(J,GE),
+    CCM(J,LE), CCM(J,NG),
+    CCM(J,NLE), CCM(J,G),
+Mnemonic_JMP,                           // Jump
+Mnemonic_LEA,                           // Load Effective Address
+Mnemonic_LEAVE,                         // High Level Procedure Exit
+Mnemonic_LOOP,                          // Loop according to ECX counter
+Mnemonic_LOOPE,                          // Loop according to ECX counter
+Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX
+Mnemonic_LAHF,                          // Load Flags into AH
+Mnemonic_MOV,                           // Move
+Mnemonic_MOVD,                          // Move Double word
+Mnemonic_MOVQ,                          // Move Quadword
+/*Mnemonic_MOVS,                        // Move Data from String to String*/
+// MOVS is a special case: see encoding table for more details,
+Mnemonic_MOVS8, Mnemonic_MOVS16, Mnemonic_MOVS32, Mnemonic_MOVS64,
+//
+Mnemonic_MOVAPD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSS,                         // Move Scalar Single-Precision Floating-Point Values
+Mnemonic_MOVSX,                         // Move with Sign-Extension
+Mnemonic_MOVZX,                         // Move with Zero-Extend
+//Mnemonic_MUL,                         // Unsigned Multiply
+Mnemonic_MULSD,                         // Multiply Scalar Double-Precision Floating-Point Values
+Mnemonic_MULSS,                         // Multiply Scalar Single-Precision Floating-Point Values
+Mnemonic_NEG,                           // Two's Complement Negation
+Mnemonic_NOP,                           // No Operation
+Mnemonic_NOT,                           // One's Complement Negation
+Mnemonic_OR,                            // Logical Inclusive OR
+Mnemonic_PREFETCH,                      // prefetch
+
+#ifdef _HAVE_MMX_
+    Mnemonic_PADDQ,                     // Add Packed Quadword Integers
+    Mnemonic_PAND,                      // Logical AND
+    Mnemonic_POR,                       // Bitwise Logical OR
+    Mnemonic_PSUBQ,                     // Subtract Packed Quadword Integers
+#endif
+
+Mnemonic_PXOR,                          // Logical Exclusive OR
+Mnemonic_POP,                           // Pop a Value from the Stack
+Mnemonic_POPFD,                         // Pop a Value of EFLAGS register from the Stack
+Mnemonic_PUSH,                          // Push Word or Doubleword Onto the Stack
+Mnemonic_PUSHFD,                        // Push EFLAGS Doubleword Onto the Stack
+Mnemonic_RET,                           // Return from Procedure
+
+Mnemonic_SETcc,                         // Set Byte on Condition
+    CCM(SET,O),
+    CCM(SET,NO),
+    CCM(SET,B), CCM(SET,NAE), CCM(SET,C),
+    CCM(SET,NB), CCM(SET,AE), CCM(SET,NC),
+    CCM(SET,Z), CCM(SET,E),
+    CCM(SET,NZ), CCM(SET,NE),
+    CCM(SET,BE), CCM(SET,NA),
+    CCM(SET,NBE), CCM(SET,A),
+    CCM(SET,S),
+    CCM(SET,NS),
+    CCM(SET,P), CCM(SET,PE),
+    CCM(SET,NP), CCM(SET,PO),
+    CCM(SET,L), CCM(SET,NGE),
+    CCM(SET,NL), CCM(SET,GE),
+    CCM(SET,LE), CCM(SET,NG),
+    CCM(SET,NLE), CCM(SET,G),
+
+Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left
+Mnemonic_SAR,                           // Shift right
+Mnemonic_ROR,                           // Rotate right
+Mnemonic_RCR,                           // Rotate right through CARRY flag
+Mnemonic_ROL,                           // Rotate left
+Mnemonic_RCL,                           // Rotate left through CARRY flag
+Mnemonic_SHR,                           // Unsigned shift right
+Mnemonic_SHRD,                          // Double Precision Shift Right
+Mnemonic_SHLD,                          // Double Precision Shift Left
+
+Mnemonic_SBB,                           // Integer Subtraction with Borrow
+Mnemonic_SUB,                           // Subtract
+Mnemonic_SUBSD,                         // Subtract Scalar Double-Precision Floating-Point Values
+Mnemonic_SUBSS,                         // Subtract Scalar Single-Precision Floating-Point Values
+
+Mnemonic_TEST,                          // Logical Compare
+
+Mnemonic_UCOMISD,                       // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_UCOMISS,                       // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS
+
+Mnemonic_XOR,                           // Logical Exclusive OR
+//
+// packed things,
+//
+Mnemonic_XORPD,                         // Bitwise Logical XOR for Double-Precision Floating-Point Values
+Mnemonic_XORPS,                         // Bitwise Logical XOR for Single-Precision Floating-Point Values
+
+Mnemonic_CVTDQ2PD,                      // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
+Mnemonic_CVTTPD2DQ,                     // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
+
+Mnemonic_CVTDQ2PS,                      // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values
+Mnemonic_CVTTPS2DQ,                     // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
+//
+// String operations
+//
+Mnemonic_STD,                           // Set direction flag
+Mnemonic_CLD,                           // Clear direction flag
+Mnemonic_SCAS,                          // Scan string
+Mnemonic_STOS,                          // Store string
+
+//
+Mnemonic_WAIT,                          // Check pending pending unmasked floating-point exception
+//
+Mnemonic_Count
+};
+
+#undef CCM
+#endif
+
+/**
+ * @brief Instruction prefixes, according to arch manual.
+ */
+typedef enum InstPrefix {
+    InstPrefix_Null = 0,
+    // Group 1
+    InstPrefix_LOCK = 0xF0,
+    InstPrefix_REPNE = 0xF2,
+    InstPrefix_REPNZ = InstPrefix_REPNE,
+    InstPrefix_REP = 0xF3, InstPrefix_REPZ = InstPrefix_REP,
+    // Group 2
+    InstPrefix_CS = 0x2E,
+    InstPrefix_SS = 0x36,
+    InstPrefix_DS = 0x3E,
+    InstPrefix_ES = 0x26,
+    InstPrefix_FS = 0x64,
+    InstPrefix_GS = 0x65,
+    //
+    InstPrefix_HintTaken = 0x3E,
+    InstPrefix_HintNotTaken = 0x2E,
+    // Group 3
+    InstPrefix_OpndSize = 0x66,
+    // Group 4
+    InstPrefix_AddrSize = 0x67
+} InstPrefix;
+
+inline unsigned getSizeBytes(OpndSize sz)
+{
+    if (sz==OpndSize_64) { return 8; }
+    if (sz==OpndSize_32) { return 4; }
+    if (sz==OpndSize_16) { return 2; }
+    if (sz==OpndSize_8)  { return 1; }
+    assert(false);
+    return 0;
+}
+
+inline bool isRegKind(OpndKind kind)
+{
+    return OpndKind_GPReg<= kind && kind<=OpndKind_MaxRegKind;
+}
+
+/**
+ * @brief Returns #RegName for a given name.
+ *
+ * Name is case-insensitive.
+ * @param regname - string name of a register
+ * @return #RegName for the given name, or #RegName_Null if name is invalid
+ */
+RegName         getRegName(const char * regname);
+/**
+ * Constructs RegName from the given OpndKind, size and index.
+ */
+inline RegName  getRegName(OpndKind k, OpndSize s, int idx)
+{
+    return (RegName)REGNAME(k,s,idx);
+}
+/**
+ * Extracts a bit mask with a bit set at the position of the register's index.
+ */
+inline unsigned getRegMask(RegName reg)
+{
+    return 1<<(reg&0xff);
+}
+/**
+ * @brief Extracts #RegKind from the #RegName.
+ */
+inline OpndKind getRegKind(RegName reg)
+{
+    return (OpndKind)(reg>>24);
+}
+/**
+ * @brief Extracts #OpndSize from #RegName.
+ */
+inline OpndSize getRegSize(RegName reg)
+{
+    return (OpndSize)((reg>>16)&0xFF);
+}
+/**
+ * Extracts an index from the given RegName.
+ */
+inline unsigned char getRegIndex(RegName reg)
+{
+    return (unsigned char)(reg&0xFF);
+}
+/**
+ * Returns a string name of the given RegName. The name returned is in upper-case.
+ * Returns NULL if invalid RegName specified.
+ */
+const char *    getRegNameString(RegName reg);
+/**
+ * Returns string name of a given OpndSize.
+ * Returns NULL if invalid OpndSize passed.
+ */
+const char *    getOpndSizeString(OpndSize size);
+/**
+ * Returns OpndSize passed by its string representation (case insensitive).
+ * Returns OpndSize_Null if invalid string specified.
+ * The 'sizeString' can not be NULL.
+ */
+OpndSize        getOpndSize(const char * sizeString);
+/**
+ * Returns string name of a given OpndKind.
+ * Returns NULL if the passed kind is invalid.
+ */
+const char *    getOpndKindString(OpndKind kind);
+/**
+ * Returns OpndKind found by its string representation (case insensitive).
+ * Returns OpndKind_Null if the name is invalid.
+ * The 'kindString' can not be NULL.
+ */
+OpndKind        getOpndKind(const char * kindString);
+/**
+ *
+ */
+const char *    getConditionString(ConditionMnemonic cm);
+
+/**
+ * Constructs an RegName with the same index and kind, but with a different size from
+ * the given RegName (i.e. getRegAlias(EAX, OpndSize_16) => AX; getRegAlias(BL, OpndSize_32) => EBX).
+ * The constructed RegName is not checked in any way and thus may be invalid.
+ * Note, that the aliasing does not work for at least AH,BH,CH,DH, ESI, EDI, ESP and EBP regs.
+ */
+inline RegName getAliasReg(RegName reg, OpndSize sz)
+{
+    return (RegName)REGNAME(getRegKind(reg), sz, getRegIndex(reg));
+}
+
+/**
+ * brief Tests two RegName-s of the same kind for equality.
+ *
+ * @note Does work for 8 bit general purpose registers (AH, AL, BH, BL, etc).
+ */
+inline bool equals(RegName r0, RegName r1)
+{
+    return getRegKind(r0) == getRegKind(r1) &&
+           getRegIndex(r0) == getRegIndex(r1);
+}
+
+ENCODER_NAMESPACE_END
+
+#endif  // ifndef _ENCODER_DEFS_H_
diff --git a/vm/compiler/codegen/x86/libenc/enc_defs_ext.h b/vm/compiler/codegen/x86/libenc/enc_defs_ext.h
new file mode 100644
index 0000000..3592513
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_defs_ext.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef _ENCODER_DEFS_EXT_H_
+#define _ENCODER_DEFS_EXT_H_
+
+
+// Used to isolate experimental or being tuned encoder into a separate
+// namespace so it can coexist with a stable one in the same bundle.
+#ifdef ENCODER_ISOLATE
+    #define ENCODER_NAMESPACE_START namespace enc_ia32 {
+    #define ENCODER_NAMESPACE_END };
+#else
+    #define ENCODER_NAMESPACE_START
+    #define ENCODER_NAMESPACE_END
+#endif
+
+ENCODER_NAMESPACE_START
+typedef enum OpndSize {
+    /**
+     * A change must be balanced with at least the following places:
+     *              Ia32IRConstants.h :: getByteSize() uses some presumptions about OpndSize_ values
+     *              Ia32::Constraint-s use the OpndSize as a mask
+     *              encoder.cpp & encoder_master_info.cpp uses OpndSize as an index for hashing
+     *              - perhaps there are much more places
+     */
+    OpndSize_Null           = 0,
+    OpndSize_8             = 0x01,
+    OpndSize_16            = 0x02,
+    OpndSize_32            = 0x04,
+    OpndSize_64            = 0x08,
+#if !defined(TESTING_ENCODER)
+    OpndSize_80            = 0x10,
+    OpndSize_128           = 0x20,
+#endif
+    OpndSize_Max,
+    OpndSize_Any            = 0x3F,
+    OpndSize_Default        = OpndSize_Any
+} OpndSize;
+
+/**
+ * Conditional mnemonics.
+ * The values match the 'real' (==processor's) values of the appropriate
+ * condition values used in the opcodes.
+ */
+typedef enum ConditionMnemonic {
+
+    ConditionMnemonic_O=0,
+    ConditionMnemonic_NO=1,
+    ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B,
+    ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB,
+    ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z,
+    ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ,
+    ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE,
+    ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE,
+
+    ConditionMnemonic_S=8,
+    ConditionMnemonic_NS=9,
+    ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P,
+    ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP,
+    ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L,
+    ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL,
+    ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE,
+    ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE,
+    ConditionMnemonic_Count=16
+} ConditionMnemonic;
+
+
+#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond
+
+//=========================================================================================================
+typedef enum Mnemonic {
+
+Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL,
+Mnemonic_ADC,                           // Add with Carry
+Mnemonic_ADD,                           // Add
+Mnemonic_ADDSD,                         // Add Scalar Double-Precision Floating-Point Values
+Mnemonic_ADDSS,                         // Add Scalar Single-Precision Floating-Point Values
+Mnemonic_AND,                           // Logical AND
+
+Mnemonic_BSF,                           // Bit scan forward
+Mnemonic_BSR,                           // Bit scan reverse
+
+Mnemonic_CALL,                          // Call Procedure
+Mnemonic_CMC,                           // Complement Carry Flag
+Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword
+Mnemonic_CMOVcc,                        // Conditional Move
+    CCM(CMOV,O),
+    CCM(CMOV,NO),
+    CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C),
+    CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC),
+    CCM(CMOV,Z), CCM(CMOV,E),
+    CCM(CMOV,NZ), CCM(CMOV,NE),
+    CCM(CMOV,BE), CCM(CMOV,NA),
+    CCM(CMOV,NBE), CCM(CMOV,A),
+
+    CCM(CMOV,S),
+    CCM(CMOV,NS),
+    CCM(CMOV,P), CCM(CMOV,PE),
+    CCM(CMOV,NP), CCM(CMOV,PO),
+    CCM(CMOV,L), CCM(CMOV,NGE),
+    CCM(CMOV,NL), CCM(CMOV,GE),
+    CCM(CMOV,LE), CCM(CMOV,NG),
+    CCM(CMOV,NLE), CCM(CMOV,G),
+
+Mnemonic_CMP,                           // Compare Two Operands
+Mnemonic_CMPXCHG,                       // Compare and exchange
+Mnemonic_CMPXCHG8B,                     // Compare and Exchange 8 Bytes
+Mnemonic_CMPSB,                         // Compare Two Bytes at DS:ESI and ES:EDI
+Mnemonic_CMPSW,                         // Compare Two Words at DS:ESI and ES:EDI
+Mnemonic_CMPSD,                         // Compare Two Doublewords at DS:ESI and ES:EDI
+//
+// double -> float
+Mnemonic_CVTSD2SS,                      // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
+// double -> I_32
+Mnemonic_CVTSD2SI,                      // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer
+// double [truncated] -> I_32
+Mnemonic_CVTTSD2SI,                     // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer
+//
+// float -> double
+Mnemonic_CVTSS2SD,                      // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value
+// float -> I_32
+Mnemonic_CVTSS2SI,                      // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer
+// float [truncated] -> I_32
+Mnemonic_CVTTSS2SI,                     // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer
+//
+// I_32 -> double
+Mnemonic_CVTSI2SD,                      // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value
+// I_32 -> float
+Mnemonic_CVTSI2SS,                      // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value
+
+Mnemonic_COMISD,                        // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_COMISS,                        // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_DEC,                           // Decrement by 1
+//Mnemonic_DIV,                         // Unsigned Divide
+Mnemonic_DIVSD,                         // Divide Scalar Double-Precision Floating-Point Values
+Mnemonic_DIVSS,                         // Divide Scalar Single-Precision Floating-Point Values
+
+#ifdef _HAVE_MMX_
+Mnemonic_EMMS,                          // Empty MMX Technology State
+#endif
+
+Mnemonic_ENTER,                         // ENTER-Make Stack Frame for Procedure Parameters
+Mnemonic_FLDCW,                         // Load FPU control word
+Mnemonic_FADDP,
+Mnemonic_FLDZ,
+Mnemonic_FADD,
+Mnemonic_FSUBP,
+Mnemonic_FSUB,
+Mnemonic_FISUB,
+Mnemonic_FMUL,
+Mnemonic_FMULP,
+Mnemonic_FDIVP,
+Mnemonic_FDIV,
+Mnemonic_FUCOM,
+Mnemonic_FUCOMI,
+Mnemonic_FUCOMP,
+Mnemonic_FUCOMIP,
+Mnemonic_FUCOMPP,
+Mnemonic_FRNDINT,
+Mnemonic_FNSTCW,                        // Store FPU control word
+Mnemonic_FSTSW,                         // Store FPU status word
+Mnemonic_FNSTSW,                         // Store FPU status word
+//Mnemonic_FDECSTP,                     // Decrement Stack-Top Pointer
+Mnemonic_FILD,                          // Load Integer
+Mnemonic_FLD,                           // Load Floating Point Value
+Mnemonic_FLDLG2,
+Mnemonic_FLDLN2,
+Mnemonic_FLD1,
+
+Mnemonic_FCLEX,                         // Clear Exceptions
+Mnemonic_FCHS,                          // Change sign of ST0
+Mnemonic_FNCLEX,                        // Clear Exceptions
+
+//Mnemonic_FINCSTP,                     // Increment Stack-Top Pointer
+Mnemonic_FIST,                          // Store Integer
+Mnemonic_FISTP,                         // Store Integer, pop FPU stack
+Mnemonic_FISTTP,                        // Store Integer with Truncation
+Mnemonic_FPREM,                         // Partial Remainder
+Mnemonic_FPREM1,                        // Partial Remainder
+Mnemonic_FST,                           // Store Floating Point Value
+Mnemonic_FSTP,                          // Store Floating Point Value and pop the FP stack
+Mnemonic_FSQRT,                         //Computes the square root of the source value in the stack and pop the FP stack
+Mnemonic_FABS,                          //Computes the absolute value of the source value in the stack and pop the FP stack
+Mnemonic_FSIN,                          //Computes the sine of the source value in the stack and pop the FP stack
+Mnemonic_FCOS,                          //Computes the cosine of the source value in the stack and pop the FP stack
+Mnemonic_FPTAN,                         //Computes the tangent of the source value in the stack and pop the FP stack
+Mnemonic_FYL2X,
+Mnemonic_FYL2XP1,
+Mnemonic_F2XM1,
+Mnemonic_FPATAN,
+Mnemonic_FXCH,
+Mnemonic_FSCALE,
+
+Mnemonic_XCHG,
+Mnemonic_DIV,                           // Unsigned Divide
+Mnemonic_IDIV,                          // Signed Divide
+Mnemonic_MUL,                           // Unsigned Multiply
+Mnemonic_IMUL,                          // Signed Multiply
+Mnemonic_INC,                           // Increment by 1
+Mnemonic_INT3,                          // Call break point
+Mnemonic_Jcc,                           // Jump if Condition Is Met
+    CCM(J,O),
+    CCM(J,NO),
+    CCM(J,B), CCM(J,NAE), CCM(J,C),
+    CCM(J,NB), CCM(J,AE), CCM(J,NC),
+    CCM(J,Z), CCM(J,E),
+    CCM(J,NZ), CCM(J,NE),
+    CCM(J,BE), CCM(J,NA),
+    CCM(J,NBE), CCM(J,A),
+    CCM(J,S),
+    CCM(J,NS),
+    CCM(J,P), CCM(J,PE),
+    CCM(J,NP), CCM(J,PO),
+    CCM(J,L), CCM(J,NGE),
+    CCM(J,NL), CCM(J,GE),
+    CCM(J,LE), CCM(J,NG),
+    CCM(J,NLE), CCM(J,G),
+Mnemonic_JMP,                           // Jump
+Mnemonic_LEA,                           // Load Effective Address
+Mnemonic_LEAVE,                         // High Level Procedure Exit
+Mnemonic_LOOP,                          // Loop according to ECX counter
+Mnemonic_LOOPE,                          // Loop according to ECX counter
+Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX
+Mnemonic_LAHF,                          // Load Flags into AH
+Mnemonic_MOV,                           // Move
+Mnemonic_MOVD,                          // Move Double word
+Mnemonic_MOVQ,                          // Move Quadword
+/*Mnemonic_MOVS,                        // Move Data from String to String*/
+// MOVS is a special case: see encoding table for more details,
+Mnemonic_MOVS8, Mnemonic_MOVS16, Mnemonic_MOVS32, Mnemonic_MOVS64,
+//
+Mnemonic_MOVAPD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSD,                         // Move Scalar Double-Precision Floating-Point Value
+Mnemonic_MOVSS,                         // Move Scalar Single-Precision Floating-Point Values
+Mnemonic_MOVSX,                         // Move with Sign-Extension
+Mnemonic_MOVZX,                         // Move with Zero-Extend
+//Mnemonic_MUL,                         // Unsigned Multiply
+Mnemonic_MULSD,                         // Multiply Scalar Double-Precision Floating-Point Values
+Mnemonic_MULSS,                         // Multiply Scalar Single-Precision Floating-Point Values
+Mnemonic_NEG,                           // Two's Complement Negation
+Mnemonic_NOP,                           // No Operation
+Mnemonic_NOT,                           // One's Complement Negation
+Mnemonic_OR,                            // Logical Inclusive OR
+Mnemonic_PREFETCH,                      // prefetch
+
+#if 1 //def _HAVE_MMX_
+    Mnemonic_PADDQ,                     // Add Packed Quadword Integers
+    Mnemonic_PAND,                      // Logical AND
+    Mnemonic_POR,                       // Bitwise Logical OR
+    Mnemonic_PSUBQ,                     // Subtract Packed Quadword Integers
+#endif
+Mnemonic_PANDN,
+Mnemonic_PSLLQ,
+Mnemonic_PSRLQ,
+Mnemonic_PXOR,                          // Logical Exclusive OR
+Mnemonic_POP,                           // Pop a Value from the Stack
+Mnemonic_POPFD,                         // Pop a Value of EFLAGS register from the Stack
+Mnemonic_PUSH,                          // Push Word or Doubleword Onto the Stack
+Mnemonic_PUSHFD,                        // Push EFLAGS Doubleword Onto the Stack
+Mnemonic_RET,                           // Return from Procedure
+
+Mnemonic_SETcc,                         // Set Byte on Condition
+    CCM(SET,O),
+    CCM(SET,NO),
+    CCM(SET,B), CCM(SET,NAE), CCM(SET,C),
+    CCM(SET,NB), CCM(SET,AE), CCM(SET,NC),
+    CCM(SET,Z), CCM(SET,E),
+    CCM(SET,NZ), CCM(SET,NE),
+    CCM(SET,BE), CCM(SET,NA),
+    CCM(SET,NBE), CCM(SET,A),
+    CCM(SET,S),
+    CCM(SET,NS),
+    CCM(SET,P), CCM(SET,PE),
+    CCM(SET,NP), CCM(SET,PO),
+    CCM(SET,L), CCM(SET,NGE),
+    CCM(SET,NL), CCM(SET,GE),
+    CCM(SET,LE), CCM(SET,NG),
+    CCM(SET,NLE), CCM(SET,G),
+
+Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left
+Mnemonic_SAR,                           // Unsigned shift right
+Mnemonic_ROR,                           // Rotate right
+Mnemonic_RCR,                           // Rotate right through CARRY flag
+Mnemonic_ROL,                           // Rotate left
+Mnemonic_RCL,                           // Rotate left through CARRY flag
+Mnemonic_SHR,                           // Signed shift right
+Mnemonic_SHRD,                          // Double Precision Shift Right
+Mnemonic_SHLD,                          // Double Precision Shift Left
+
+Mnemonic_SBB,                           // Integer Subtraction with Borrow
+Mnemonic_SUB,                           // Subtract
+Mnemonic_SUBSD,                         // Subtract Scalar Double-Precision Floating-Point Values
+Mnemonic_SUBSS,                         // Subtract Scalar Single-Precision Floating-Point Values
+
+Mnemonic_TEST,                          // Logical Compare
+
+Mnemonic_UCOMISD,                       // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS
+Mnemonic_UCOMISS,                       // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS
+
+Mnemonic_XOR,                           // Logical Exclusive OR
+//
+// packed things,
+//
+Mnemonic_XORPD,                         // Bitwise Logical XOR for Double-Precision Floating-Point Values
+Mnemonic_XORPS,                         // Bitwise Logical XOR for Single-Precision Floating-Point Values
+
+Mnemonic_CVTDQ2PD,                      // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
+Mnemonic_CVTTPD2DQ,                     // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
+
+Mnemonic_CVTDQ2PS,                      // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values
+Mnemonic_CVTTPS2DQ,                     // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
+//
+// String operations
+//
+Mnemonic_STD,                           // Set direction flag
+Mnemonic_CLD,                           // Clear direction flag
+Mnemonic_SCAS,                          // Scan string
+Mnemonic_STOS,                          // Store string
+
+//
+Mnemonic_WAIT,                          // Check pending pending unmasked floating-point exception
+//
+Mnemonic_Count
+} Mnemonic;
+
+#undef CCM
+
+ENCODER_NAMESPACE_END
+
+#endif  // ifndef _ENCODER_DEFS_EXT_H_
diff --git a/vm/compiler/codegen/x86/libenc/enc_prvt.h b/vm/compiler/codegen/x86/libenc/enc_prvt.h
new file mode 100644
index 0000000..4300574
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_prvt.h
@@ -0,0 +1,382 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#ifndef __ENC_PRVT_H_INCLUDED__
+#define __ENC_PRVT_H_INCLUDED__
+
+#include "enc_base.h"
+
+ENCODER_NAMESPACE_START
+/*
+ * @file
+ * @brief Contains some definitions/constants and other stuff used by the
+ *        Encoder internally.
+ */
+
+enum OpcodeByteKind {
+    //OpcodeByteKind_Opcode = 0x0000,
+    OpcodeByteKind_ZeroOpcodeByte           = 0x0100,
+    //
+    // The names _SlashR,  _SlahsNum, _ib, _iw, etc
+    // represent the appropriate abbreviations used
+    // in the mnemonic descriptions in the Intel's arch manual.
+    //
+    OpcodeByteKind_SlashR                   = 0x0200,
+    OpcodeByteKind_SlashNum                 = 0x0300,
+    OpcodeByteKind_ib                       = 0x0400,
+    OpcodeByteKind_iw                       = 0x0500,
+    OpcodeByteKind_id                       = 0x0600,
+#ifdef _EM64T_
+    OpcodeByteKind_io                       = 0x0700,
+#endif
+    OpcodeByteKind_cb                       = 0x0800,
+    OpcodeByteKind_cw                       = 0x0900,
+    OpcodeByteKind_cd                       = 0x0A00,
+    //OpcodeByteKind_cp                     = 0x0B00,
+    //OpcodeByteKind_co                     = 0x0C00,
+    //OpcodeByteKind_ct                     = 0x0D00,
+
+    OpcodeByteKind_rb                       = 0x0E00,
+    OpcodeByteKind_rw                       = 0x0F00,
+    OpcodeByteKind_rd                       = 0x1000,
+#ifdef _EM64T_
+    OpcodeByteKind_ro                       = 0x1100,
+    //OpcodeByteKind_REX                    = 0x1200,
+    OpcodeByteKind_REX_W                    = 0x1300,
+#endif
+    OpcodeByteKind_plus_i                   = 0x1400,
+    /**
+        * a special marker, means 'no opcode on the given position'
+        * used in opcodes array, to specify the empty slot, say
+        * to fill an em64t-specific opcode on ia32.
+        * last 'e' made lowercase to avoid a mess with 'F' in
+        * OpcodeByteKind_LAST .
+        */
+    OpcodeByteKind_EMPTY                    = 0xFFFE,
+    /**
+        * a special marker, means 'no more opcodes in the array'
+        * used in in opcodes array to show that there are no more
+        * opcodes in the array for a given mnemonic.
+        */
+    OpcodeByteKind_LAST                     = 0xFFFF,
+    /**
+        * a mask to extract the OpcodeByteKind
+        */
+    OpcodeByteKind_KindMask                 = 0xFF00,
+    /**
+        * a mask to extract the opcode byte when presented
+        */
+    OpcodeByteKind_OpcodeMask               = 0x00FF
+};
+
+#ifdef USE_ENCODER_DEFINES
+
+#define N           {0, 0, 0, 0 }
+#define U           {1, 0, 1, OpndRole_Use }
+#define D           {1, 1, 0, OpndRole_Def }
+#define DU          {1, 1, 1, OpndRole_Def|OpndRole_Use }
+
+#define U_U         {2, 0, 2, OpndRole_Use<<2 | OpndRole_Use }
+#define D_U         {2, 1, 1, OpndRole_Def<<2 | OpndRole_Use }
+#define D_DU        {2, 2, 1, OpndRole_Def<<2 | (OpndRole_Def|OpndRole_Use) }
+#define DU_U        {2, 1, 2, ((OpndRole_Def|OpndRole_Use)<<2 | OpndRole_Use) }
+#define DU_DU       {2, 2, 2, ((OpndRole_Def|OpndRole_Use)<<2 | (OpndRole_Def|OpndRole_Use)) }
+
+#define DU_DU_DU    {3, 3, 3, ((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | (OpndRole_Def|OpndRole_Use) }
+#define DU_DU_U     {3, 2, 3, (((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
+#define D_DU_U      {3, 2, 2, (((OpndRole_Def)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
+#define D_U_U       {3, 1, 2, (((OpndRole_Def)<<4) | ((OpndRole_Use)<<2) | OpndRole_Use) }
+
+// Special encoding of 0x00 opcode byte. Note: it's all O-s, not zeros.
+#define OxOO        OpcodeByteKind_ZeroOpcodeByte
+
+#define Size16      InstPrefix_OpndSize
+
+#define _r          OpcodeByteKind_SlashR
+
+#define _0          OpcodeByteKind_SlashNum|0
+#define _1          OpcodeByteKind_SlashNum|1
+#define _2          OpcodeByteKind_SlashNum|2
+#define _3          OpcodeByteKind_SlashNum|3
+#define _4          OpcodeByteKind_SlashNum|4
+#define _5          OpcodeByteKind_SlashNum|5
+#define _6          OpcodeByteKind_SlashNum|6
+#define _7          OpcodeByteKind_SlashNum|7
+
+// '+i' for floating-point instructions
+#define _i          OpcodeByteKind_plus_i
+
+
+#define ib          OpcodeByteKind_ib
+#define iw          OpcodeByteKind_iw
+#define id          OpcodeByteKind_id
+
+#define cb          OpcodeByteKind_cb
+#define cw          OpcodeByteKind_cw
+#define cd          OpcodeByteKind_cd
+
+#define rb          OpcodeByteKind_rb
+#define rw          OpcodeByteKind_rw
+#define rd          OpcodeByteKind_rd
+
+#define AL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AL}
+#define AH          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AH}
+#define AX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_AX}
+#define EAX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EAX}
+#ifdef _EM64T_
+    #define RAX     {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RAX }
+#endif
+
+#define CL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_CL}
+#define ECX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ECX}
+#ifdef _EM64T_
+    #define RCX         {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RCX}
+#endif
+
+#define DX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_DX}
+#define EDX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDX}
+#ifdef _EM64T_
+    #define RDX     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDX }
+#endif
+
+#define ESI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ESI}
+#ifdef _EM64T_
+    #define RSI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RSI }
+#endif
+
+#define EDI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDI}
+#ifdef _EM64T_
+    #define RDI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDI }
+#endif
+
+#define r8          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_Null}
+#define r16         {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_Null}
+#define r32         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define r64     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+#define r_m8        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Any, RegName_Null}
+#define r_m16       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Any, RegName_Null}
+#define r_m32       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define r_m8s        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Signed, RegName_Null}
+#define r_m16s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Signed, RegName_Null}
+#define r_m32s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Signed, RegName_Null}
+
+#define r_m8u        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Zero, RegName_Null}
+#define r_m16u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Zero, RegName_Null}
+#define r_m32u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Zero, RegName_Null}
+
+//'m' was only used in LEA mnemonic, but is replaced with
+// set of exact sizes. See more comments for LEA instruction in TheTable.
+//#define m           {OpndKind_Mem, OpndSize_Null, RegName_Null}
+#define m8          {OpndKind_Mem, OpndSize_8, OpndExt_Any, RegName_Null}
+#define m16         {OpndKind_Mem, OpndSize_16, OpndExt_Any, RegName_Null}
+#define m32         {OpndKind_Mem, OpndSize_32, OpndExt_Any, RegName_Null}
+#define m64         {OpndKind_Mem, OpndSize_64, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define r_m64   { (OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+#define imm8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
+#define imm16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
+#define imm32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define imm8s        {OpndKind_Imm, OpndSize_8, OpndExt_Signed, RegName_Null}
+#define imm16s       {OpndKind_Imm, OpndSize_16, OpndExt_Signed, RegName_Null}
+#define imm32s       {OpndKind_Imm, OpndSize_32, OpndExt_Signed, RegName_Null}
+
+#define imm8u        {OpndKind_Imm, OpndSize_8, OpndExt_Zero, RegName_Null}
+#define imm16u       {OpndKind_Imm, OpndSize_16, OpndExt_Zero, RegName_Null}
+#define imm32u       {OpndKind_Imm, OpndSize_32, OpndExt_Zero, RegName_Null}
+
+#ifdef _EM64T_
+    #define imm64   {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null }
+#endif
+
+//FIXME: moff-s are in fact memory refs, but presented as immediate.
+// Need to specify this in OpndDesc.
+#define moff8        {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#define moff16       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#define moff32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+#ifdef _EM64T_
+    #define moff64       {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null}
+#endif
+
+
+#define rel8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
+#define rel16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
+#define rel32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define mm64        {OpndKind_MMXReg, OpndSize_64, OpndExt_Any, RegName_Null}
+#define mm_m64      {(OpndKind)(OpndKind_MMXReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}
+
+#define xmm64       {OpndKind_XMMReg, OpndSize_64, OpndExt_Any, RegName_Null}
+#define xmm_m64     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}
+
+#define xmm32       {OpndKind_XMMReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#define xmm_m32     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}
+
+#define FP0S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP0S}
+#define FP0D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP0D}
+#define FP1S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP1S}
+#define FP1D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP1D}
+#define fp32        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_Null}
+#define fp64        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_Null}
+
+#ifdef _EM64T_
+    #define io      OpcodeByteKind_io
+    #define REX_W   OpcodeByteKind_REX_W
+
+#endif
+
+#endif // USE_ENCODER_DEFINES
+
+/**
+ * @brief Represents the REX part of instruction.
+ */
+struct  Rex {
+    unsigned char b : 1;
+    unsigned char x : 1;
+    unsigned char r : 1;
+    unsigned char w : 1;
+    unsigned char dummy : 4;        // must be '0100'b
+    unsigned int  :24;
+};
+
+/**
+ * @brief Describes SIB (scale,index,base) byte.
+ */
+struct SIB {
+    unsigned char base:3;
+    unsigned char index:3;
+    unsigned char scale:2;
+    unsigned int  padding:24;
+};
+/**
+ * @brief Describes ModRM byte.
+ */
+struct ModRM
+{
+    unsigned char rm:3;
+    unsigned char reg:3;
+    unsigned char mod:2;
+    unsigned int  padding:24;
+};
+
+
+
+/**
+* exactly the same as EncoderBase::OpcodeDesc, but also holds info about
+* platform on which the opcode is applicable.
+*/
+struct OpcodeInfo {
+    enum platform {
+        /// an opcode is valid on all platforms
+        all,
+        // opcode is valid on IA-32 only
+        em64t,
+        // opcode is valid on Intel64 only
+        ia32,
+        // opcode is added for the sake of disassembling, should not be used in encoding
+        decoder,
+        // only appears in master table, replaced with 'decoder' in hashed version
+        decoder32,
+        // only appears in master table, replaced with 'decoder' in hashed version
+        decoder64,
+    };
+    platform                        platf;
+    unsigned                        opcode[4+1+1];
+    EncoderBase::OpndDesc           opnds[3];
+    EncoderBase::OpndRolesDesc      roles;
+};
+
+/**
+ * @defgroup MF_ Mnemonic flags
+*/
+
+    /**
+ * Operation has no special properties.
+    */
+#define MF_NONE             (0x00000000)
+    /**
+ * Operation affects flags
+    */
+#define MF_AFFECTS_FLAGS    (0x00000001)
+    /**
+ * Operation uses flags - conditional operations, ADC/SBB/ETC
+    */
+#define MF_USES_FLAGS       (0x00000002)
+    /**
+ * Operation is conditional - MOVcc/SETcc/Jcc/ETC
+    */
+#define MF_CONDITIONAL      (0x00000004)
+/**
+ * Operation is symmetric - its args can be swapped (ADD/MUL/etc).
+ */
+#define MF_SYMMETRIC        (0x00000008)
+/**
+ * Operation is XOR-like - XOR, SUB - operations of 'arg,arg' is pure def,
+ * without use.
+ */
+#define MF_SAME_ARG_NO_USE  (0x00000010)
+
+///@} // ~MNF
+
+/**
+ * @see same structure as EncoderBase::MnemonicDesc, but carries
+ * MnemonicInfo::OpcodeInfo[] instead of OpcodeDesc[].
+ * Only used during prebuilding the encoding tables, thus it's hidden under
+ * the appropriate define.
+ */
+struct MnemonicInfo {
+    /**
+    * The mnemonic itself
+    */
+    Mnemonic    mn;
+    /**
+     * Various characteristics of mnemonic.
+     * @see MF_
+     */
+    unsigned    flags;
+    /**
+     * Number of args/des/uses/roles for the operation. For the operations
+     * which may use different number of operands (i.e. IMUL/SHL) use the
+     * most common value, or leave '0' if you are sure this info is not
+     * required.
+     */
+    EncoderBase::OpndRolesDesc              roles;
+    /**
+     * Print name of the mnemonic
+     */
+    const char *                            name;
+    /**
+     * Array of opcodes.
+     * The terminating opcode description always have OpcodeByteKind_LAST
+     * at the opcodes[i].opcode[0].
+     * The size of '25' has nothing behind it, just counted the max
+     * number of opcodes currently used (MOV instruction).
+     */
+    OpcodeInfo                              opcodes[25];
+};
+
+ENCODER_NAMESPACE_END
+
+#endif  // ~__ENC_PRVT_H_INCLUDED__
diff --git a/vm/compiler/codegen/x86/libenc/enc_tabl.cpp b/vm/compiler/codegen/x86/libenc/enc_tabl.cpp
new file mode 100644
index 0000000..8a0789a
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_tabl.cpp
@@ -0,0 +1,1975 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h> //qsort
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <stdlib.h>
+
+
+// need to use EM64T-specifics - new registers, defines from enc_prvt, etc...
+#if !defined(_EM64T_)
+    #define UNDEF_EM64T
+    #define _EM64T_
+#endif
+
+#define USE_ENCODER_DEFINES
+#include "enc_prvt.h"
+#include "enc_defs.h"
+
+#ifdef UNDEF_EM64T
+    #undef _EM64T_
+#endif
+
+//Android x86
+#if 0 //!defined(_HAVE_MMX_)
+    #define Mnemonic_PADDQ  Mnemonic_Null
+    #define Mnemonic_PAND   Mnemonic_Null
+    #define Mnemonic_POR    Mnemonic_Null
+    #define Mnemonic_PSUBQ  Mnemonic_Null
+#endif
+
+ENCODER_NAMESPACE_START
+
+
+EncoderBase::MnemonicDesc EncoderBase::mnemonics[Mnemonic_Count];
+EncoderBase::OpcodeDesc EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES];
+unsigned char EncoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX];
+
+
+/**
+ * @file
+ * @brief 'Master' copy of encoding data.
+ */
+
+/*
+This file contains a 'master copy' of encoding table - this is the info used
+by both generator of native instructions (EncoderBase class) and by
+disassembling routines. The first one uses an info how to encode the
+instruction, and the second does an opposite - several separate tables are
+built at runtime from this main table.
+
+=============================================================================
+
+The table was designed for easy support and maintenance. Thus, it was made as
+much close as possible to the Intel's IA32 Architecture Manual descriptions.
+The info is based on the latest (at the moment of writing) revision which is
+June 2005, order number 253666-016.
+
+Normally, almost all of opcodes in the 'master' table represented exactly as
+they are shown in the Intel's Architecture manual (well, with slashes
+replaced with underscore). There are several exclusions especially marked.
+
+Normally, to add an opcode/instruction, one only need to copy the whole
+string from the manual, and simply replace '/' with '_'.
+
+I.e., TheManual reads for DEC:
+    (1)     FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1.
+    (2)     REX + FE /1 DEC r/m8* Valid N.E. Decrement r/m8 by 1.
+    (3)     REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1.
+
+1. Note, that there is no need to explicitly specify REX-based opcodes for
+    instruction to handle additional registers on EM64T:
+
+    (1)     FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1.
+    (3)     REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1.
+
+2. Copy the string, strip off the text comments, replace '/'=>'_'. Note, that
+    the second line is for EM64T only
+
+    (1)     FE /1 DEC r/m8
+    (3)     REX.W + FF /1 DEC r/m64
+
+3. Fill out the mnemonic, opcode parameters parts
+
+    BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU)
+    BEGIN_OPCODES()
+        {OpcodeInfo::all,   {0xFE, _1},         {r_m8},         DU },
+        {OpcodeInfo::em64t, {REX_W, 0xFF, _1},  {r_m64},        DU },
+
+    DU here - one argument, it's used and defined
+
+4. That's it, that simple !
+
+The operand roles (DU here) are used by Jitrino's optimizing engine to
+perform data flow analysis. It also used to store/obtain number of operands.
+
+Special cases are (see the table for details):
+LEA
+Some FPU operations (i.e. FSTP)
+packed things (XORPD, XORPS, CVTDQ2PD, CVTTPD2DQ)
+
+Also, the Jitrino's needs require to specify all operands - including
+implicit ones (see IMUL).
+
+The master table iself does not need to be ordered - it's get sorted before
+processing. It's recommended (though it's not a law) to group similar
+instructions together - i.e. FPU instructions, MMX, etc.
+
+=============================================================================
+
+The encoding engine builds several tables basing on the 'master' one (here
+'mnemonic' is a kind of synonim for 'instruction'):
+
+- list of mnemonics which holds general info about instructions
+    (EncoderBase::mnemonics)
+- an array of opcodes descriptions (EncodeBase::opcodes)
+- a mapping between a hash value and an opcode description record for a given
+    mnemonic (EncoderBase::opcodesHashMap)
+
+The EncoderBase::mnemonics holds general info about instructions.
+The EncoderBase::opcodesHashMap is used for fast opcode selection basing on
+a hash value.
+The EncodeBase::opcodes is used for the encoding itself.
+
+=============================================================================
+The hash value is calculated and used as follows:
+
+JIT-ted code uses the following operand sizes: 8-, 16-, 32- and 64-bits and
+size for an operand can be encoded in just 2 bits.
+
+The following operand locations are available: one of registers - GP, FP,
+MMX, XMM (not taking segment registers), a memory and an immediate, which
+gives us 6 variants and can be enumerated in 3 bits.
+
+As a grand total, the the whole operand's info needed for opcode selection
+can be packed in 5 bits. Taking into account the IMUL mnemonic with its 3
+operands (including implicit ones), we're getting 15 bits per instruction and
+the complete table is about 32768 items per single instruction.
+
+Seems too many, but luckily, the 15 bit limit will never be reached: the
+worst case is IMUL with its 3 operands:
+(IMUL r64, r/m64, imm32)/(IMUL r32, r/m32, imm32).
+So, assigning lowest value to GP register, the max value of hash can be
+reduced.
+
+The hash values to use are:
+sizes:
+        8               -> 11
+        16              -> 10
+        32              -> 01
+        64              -> 00
+locations:
+        gp reg          -> 000
+        memory          -> 001
+        fp reg          -> 010
+        mmx reg         -> 011
+        xmm reg         -> 100
+        immediate       -> 101
+and the grand total for the worst case would be
+[ GP 32] [GP  32] [Imm 32]
+[000-01] [000-01] [101 01] = 1077
+
+However, the implicit operands adds additional value, and the worstest case
+is 'SHLD r_m32, r32, CL=r8'. This gives us the maximum number of:
+
+[mem 32] [GP  32] [GP  8b]
+[001-01] [000-01] [000-11] = 5155.
+
+The max number is pretty big and the hash functions is quite rare, thus it
+is not resonable to use a direct addressing i.e.
+OpcodeDesc[mnemonic][hash_code] - there would be a huge waste of space.
+
+Instead, we use a kind of mapping: the opcodes info is stored in packed
+(here: non rare) array. The max number of opcodes will not exceed 255 for
+each instruction. And we have an index array in which we store a mapping
+between a hash code value and opcode position for each given instruction.
+
+Sounds a bit sophisticated, but in real is simple, the opcode gets selected
+in 2 simple steps:
+
+1. Select [hash,mnemonic] => 'n'.
+
+The array is pretty rare - many cells contain 0xFF which
+means 'invalid hash - no opcode with given characteristics'
+
+char EnbcoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX] =
+
++----+----+----+----+----+----+
+| 00 | 05 | FF | FF | 03 | 12 | ...
+|---------+-------------------+
+| 12 | FF | FF |  n | 04 | 25 | ...   <- Mnemonic
+|-----------------------------+
+| FF | 11 | FF | 10 | 13 | .. | ...
++-----------------------------+
+     ...         ^
+                 |
+                hash
+
+2. Select [n,mnemonic] => 'opcode_desc11'
+
+OpcodeDesc      EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES] =
+
++---------------+---------------+---------------+---------------+
+| opcode_desc00 | opcode_desc01 | opcode_desc02 | last_opcode   | ...
++---------------+---------------+---------------+---------------+
+| opcode_desc10 | opcode_desc11 | last_opcode   | xxx           | <- Mnemonic
++---------------+---------------+---------------+---------------+
+| opcode_desc20 | opcode_desc21 | opcode_desc22 | opcode_desc23 | ...
++---------------+---------------+---------------+---------------+
+     ...
+                      ^
+                      |
+                      n
+
+Now, use 'opcode_desc11'.
+
+=============================================================================
+The array of opcodes descriptions (EncodeBase::opcodes) is specially prepared
+to maximize performance - the EncoderBase::encode() is quite hot on client
+applications for the Jitrino/Jitrino.JET.
+The preparation is that opcode descriptions from the 'master' encoding table
+are preprocessed and a special set of OpcodeDesc prepared:
+First, the 'raw' opcode bytes are extracted. Here, 'raw' means the bytes that
+do not depened on any operands values, do not require any analysis and can be
+simply copied into the output buffer during encoding. Also, number of these
+'raw' bytes is counted. The fields are OpcodeDesc::opcode and
+OpcodeDesc::opcode_len.
+
+Then the fisrt non-implicit operand found and its index is stored in
+OpcodeDesc::first_opnd.
+
+The bytes that require processing and analysis ('/r', '+i', etc) are
+extracted and stored in OpcodeDesc::aux0 and OpcodeDesc::aux1 fields.
+
+Here, a special trick is performed:
+    Some opcodes have register/memory operand, but this is not reflected in
+    opcode column - for example, (MOVQ xmm64, xmm_m64). In this case, a fake
+    '_r' added to OpcodeDesc::aux field.
+    Some other opcodes have immediate operands, but this is again not
+    reflected in opcode column - for example, CALL cd or PUSH imm32.
+    In this case, a fake '/cd' or fake '/id' added to appropriate
+    OpcodeDesc::aux field.
+
+The OpcodeDesc::last is non-zero for the final OpcodeDesc record (which does
+not have valid data itself).
+*/
+
+// TODO: To extend flexibility, replace bool fields in MnemonicDesc &
+// MnemonicInfo with a set of flags packed into integer field.
+
+unsigned short EncoderBase::getHash(const OpcodeInfo* odesc)
+{
+    /*
+    NOTE: any changes in the hash computation must be stricty balanced with
+    EncoderBase::Operand::hash_it and EncoderBase::Operands()
+    */
+    unsigned short hash = 0;
+    // The hash computation, uses fast way - table selection instead of if-s.
+    if (odesc->roles.count > 0) {
+        OpndKind kind = odesc->opnds[0].kind;
+        OpndSize size = odesc->opnds[0].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = get_kind_hash(kind) | get_size_hash(size);
+    }
+
+    if (odesc->roles.count > 1) {
+        OpndKind kind = odesc->opnds[1].kind;
+        OpndSize size = odesc->opnds[1].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = (hash<<HASH_BITS_PER_OPERAND) |
+               (get_kind_hash(kind) | get_size_hash(size));
+    }
+
+    if (odesc->roles.count > 2) {
+        OpndKind kind = odesc->opnds[2].kind;
+        OpndSize size = odesc->opnds[2].size;
+        assert(kind<COUNTOF(kind_hash));
+        assert(size<COUNTOF(size_hash));
+        hash = (hash<<HASH_BITS_PER_OPERAND) |
+            (get_kind_hash(kind) | get_size_hash(size));
+    }
+    assert(hash <= HASH_MAX);
+    return hash;
+}
+
+
+#define BEGIN_MNEMONIC(mn, flags, roles)     \
+        { Mnemonic_##mn, flags, roles, #mn,
+#define END_MNEMONIC() },
+#define BEGIN_OPCODES() {
+#define END_OPCODES()   { OpcodeInfo::all, {OpcodeByteKind_LAST} }}
+
+//#define BEGIN_MNEMONIC(mn, affflags, ulags, cond, symm, roles)     \
+//        { Mnemonic_##mn, affflags, ulags, cond, symm, roles, #mn,
+
+
+static MnemonicInfo masterEncodingTable[] = {
+//
+// Null
+//
+BEGIN_MNEMONIC(Null, MF_NONE, N)
+BEGIN_OPCODES()
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LAHF, MF_USES_FLAGS, D)
+BEGIN_OPCODES()
+// TheManual says it's not always supported in em64t mode, thus excluding it
+    {OpcodeInfo::ia32,    {0x9F},         {EAX}, D },
+END_OPCODES()
+END_MNEMONIC()
+//
+// ALU mnemonics - add, adc, or, xor, and, cmp, sub, sbb
+// as they differ only in the opcode extention (/digit) number and
+// in which number the opcode start from, the opcode definitions
+// for those instructions are packed together
+//
+// The 'opcode_starts_from' and 'opcode_ext' in DEFINE_ALU_OPCODES()
+// are enough to define OpcodeInfo::all opcodes and the 'first_opcode'
+// parameter is only due to ADD instruction, which requires an zero opcode
+// byte which, in turn, is coded especially in the current coding scheme.
+//
+
+#define DEFINE_ALU_OPCODES( opc_ext, opcode_starts_from, first_opcode, def_use ) \
+\
+    {OpcodeInfo::decoder,   {opcode_starts_from + 4, ib},           {AL,    imm8},  DU_U },\
+    {OpcodeInfo::decoder,   {Size16, opcode_starts_from + 5, iw},   {AX,    imm16}, DU_U },\
+    {OpcodeInfo::decoder,   {opcode_starts_from + 5, id},           {EAX,   imm32}, DU_U },\
+    {OpcodeInfo::decoder64, {REX_W, opcode_starts_from+5, id},      {RAX,   imm32s},DU_U },\
+\
+    {OpcodeInfo::all,       {0x80, opc_ext, ib},          {r_m8,  imm8},    def_use },\
+    {OpcodeInfo::all,       {Size16, 0x81, opc_ext, iw},  {r_m16, imm16},   def_use },\
+    {OpcodeInfo::all,       {0x81, opc_ext, id},          {r_m32, imm32},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, 0x81, opc_ext, id},   {r_m64, imm32s},  def_use },\
+\
+    {OpcodeInfo::all,       {Size16, 0x83, opc_ext, ib},  {r_m16, imm8s},   def_use },\
+    {OpcodeInfo::all,       {0x83, opc_ext, ib},          {r_m32, imm8s},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, 0x83, opc_ext, ib},   {r_m64, imm8s},   def_use },\
+\
+    {OpcodeInfo::all,       {first_opcode,  _r},          {r_m8,  r8},      def_use },\
+\
+    {OpcodeInfo::all,       {Size16, opcode_starts_from+1,  _r},  {r_m16, r16},   def_use },\
+    {OpcodeInfo::all,       {opcode_starts_from+1,  _r},  {r_m32, r32},   def_use },\
+    {OpcodeInfo::em64t,     {REX_W, opcode_starts_from+1, _r},    {r_m64, r64},   def_use },\
+\
+    {OpcodeInfo::all,       {opcode_starts_from+2,  _r},  {r8,    r_m8},  def_use },\
+\
+    {OpcodeInfo::all,       {Size16, opcode_starts_from+3,  _r},  {r16,   r_m16}, def_use },\
+    {OpcodeInfo::all,       {opcode_starts_from+3,  _r},  {r32,   r_m32}, def_use },\
+    {OpcodeInfo::em64t,     {REX_W, opcode_starts_from+3, _r},    {r64,   r_m64}, def_use },
+
+BEGIN_MNEMONIC(ADD, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_0, 0x00, OxOO, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(OR, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_1, 0x08, 0x08, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ADC, MF_AFFECTS_FLAGS|MF_USES_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_2, 0x10, 0x10, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SBB, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_3, 0x18, 0x18, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(AND, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_4, 0x20, 0x20, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(SUB, MF_AFFECTS_FLAGS|MF_SAME_ARG_NO_USE, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES(_5, 0x28, 0x28, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(XOR, MF_AFFECTS_FLAGS|MF_SYMMETRIC|MF_SAME_ARG_NO_USE, DU_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES( _6, 0x30, 0x30, DU_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMP, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    DEFINE_ALU_OPCODES( _7, 0x38, 0x38, U_U )
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPXCHG, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xB0, _r},           {r_m8, r8, AL},     DU_DU_DU },
+    {OpcodeInfo::all,   {Size16, 0x0F, 0xB1, _r},   {r_m16, r16, AX},   DU_DU_DU },
+    {OpcodeInfo::all,   {0x0F, 0xB1, _r},           {r_m32, r32, EAX},  DU_DU_DU},
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0xB1, _r},    {r_m64, r64, RAX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPXCHG8B, MF_AFFECTS_FLAGS, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xC7, _1},         {m64},     DU },
+END_OPCODES()
+END_MNEMONIC()
+
+#undef DEFINE_ALU_OPCODES
+//
+//
+//
+BEGIN_MNEMONIC(ADDSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x58, _r},   {xmm64, xmm_m64},   DU_U},
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ADDSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x58, _r},   {xmm32, xmm_m32},   DU_U},
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(BSF, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xBC},   {r32, r_m32},   D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(BSR, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xBD},   {r32, r_m32},   D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(CALL, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE8, cd},        {rel32},     U },
+    {OpcodeInfo::ia32,  {Size16, 0xE8, cw}, {rel16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _2},        {r_m32},     U },
+    {OpcodeInfo::em64t, {0xFF, _2},        {r_m64},     U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMC, MF_USES_FLAGS|MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xF5},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+//TODO: Workaround. Actually, it's D_DU, but Jitrino's CG thinks it's D_U
+BEGIN_MNEMONIC(CDQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {0x99},         {DX, AX},       D_U },
+    {OpcodeInfo::all,       {0x99},         {EDX, EAX},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x99},  {RDX, RAX},     D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_CMOVcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(CMOV##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU_U ) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {Size16, 0x0F, 0x40 + ConditionMnemonic_##cc, _r},  {r16, r_m16},   DU_U }, \
+    {OpcodeInfo::all,   {0x0F, 0x40 + ConditionMnemonic_##cc, _r},          {r32, r_m32},   DU_U }, \
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0x40 + ConditionMnemonic_##cc, _r},   {r64, r_m64},   DU_U }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+DEFINE_CMOVcc_MNEMONIC(O)
+DEFINE_CMOVcc_MNEMONIC(NO)
+DEFINE_CMOVcc_MNEMONIC(B)
+DEFINE_CMOVcc_MNEMONIC(NB)
+DEFINE_CMOVcc_MNEMONIC(Z)
+DEFINE_CMOVcc_MNEMONIC(NZ)
+DEFINE_CMOVcc_MNEMONIC(BE)
+DEFINE_CMOVcc_MNEMONIC(NBE)
+DEFINE_CMOVcc_MNEMONIC(S)
+DEFINE_CMOVcc_MNEMONIC(NS)
+DEFINE_CMOVcc_MNEMONIC(P)
+DEFINE_CMOVcc_MNEMONIC(NP)
+DEFINE_CMOVcc_MNEMONIC(L)
+DEFINE_CMOVcc_MNEMONIC(NL)
+DEFINE_CMOVcc_MNEMONIC(LE)
+DEFINE_CMOVcc_MNEMONIC(NLE)
+
+#undef DEFINE_CMOVcc_MNEMONIC
+
+/*****************************************************************************
+                                ***** SSE conversion routines *****
+*****************************************************************************/
+//
+// double -> float
+BEGIN_MNEMONIC(CVTSD2SS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x5A, _r},   {xmm32, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// double -> I_32
+BEGIN_MNEMONIC(CVTSD2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2D, _r},         {r32, xmm_m64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2D, _r},  {r64, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// double [truncated] -> I_32
+BEGIN_MNEMONIC(CVTTSD2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2C, _r},         {r32, xmm_m64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2C, _r},  {r64, xmm_m64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// float -> double
+BEGIN_MNEMONIC(CVTSS2SD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5A, _r},         {xmm64, xmm_m32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+// float -> I_32
+BEGIN_MNEMONIC(CVTSS2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2D, _r},         {r32, xmm_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2D, _r},  {r64, xmm_m32}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// float [truncated] -> I_32
+BEGIN_MNEMONIC(CVTTSS2SI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2C, _r},         {r32, xmm_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2C, _r},  {r64, xmm_m32}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// I_32 -> double
+BEGIN_MNEMONIC(CVTSI2SD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x2A, _r},         {xmm64, r_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2A, _r},  {xmm64, r_m64}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+// I_32 -> float
+BEGIN_MNEMONIC(CVTSI2SS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x2A, _r},         {xmm32, r_m32}, D_U},
+    {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2A, _r},  {xmm32, r_m64}, D_U},
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~ SSE conversions
+//
+
+BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFE, _1},         {r_m8},     DU },
+
+    {OpcodeInfo::all,   {Size16, 0xFF, _1}, {r_m16},    DU },
+    {OpcodeInfo::all,   {0xFF, _1},         {r_m32},    DU },
+    {OpcodeInfo::em64t, {REX_W, 0xFF, _1},  {r_m64},    DU },
+
+    {OpcodeInfo::ia32,  {Size16, 0x48|rw},  {r16},      DU },
+    {OpcodeInfo::ia32,  {0x48|rd},          {r32},      DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(DIVSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x5E, _r},   {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(DIVSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x5E, _r},   {xmm32, xmm_m32},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+/****************************************************************************
+                 ***** FPU operations *****
+****************************************************************************/
+
+BEGIN_MNEMONIC(FADDP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xC1},       {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xC1},       {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDZ,  MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xEE},   {FP0D}, D },
+    {OpcodeInfo::all,   {0xD9, 0xEE},   {FP0S}, D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FADD,  MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _0},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _0},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSUBP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xE9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xE9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSUB,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _4},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _4},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISUB,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDA, _4},       {FP0S, m32}, DU_U },
+//    {OpcodeInfo::all,   {0xDE, _4},       {FP0S, m16}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+
+BEGIN_MNEMONIC(FMUL,   MF_NONE, DU_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD8, _1},     {FP0S, m32}, DU_U },
+    {OpcodeInfo::all,   {0xDC, _1},     {FP0D, m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FMULP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xC9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xC9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FDIVP, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDE, 0xF9},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xDE, 0xF9},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FDIV,   MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDC, _6},     {FP0D, m64}, DU_U },
+    {OpcodeInfo::all,   {0xD8, _6},     {FP0S, m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FUCOM, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, 0xE1},         {FP0D, FP1D},    DU_U },
+    {OpcodeInfo::all,   {0xDD, 0xE1},         {FP0S, FP1S},    DU_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xE0|_i},    {fp32},         DU },
+    {OpcodeInfo::all,   {0xDD, 0xE0|_i},    {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMI, MF_NONE, D_U )
+BEGIN_OPCODES()
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDB, 0xE8|_i},    {fp32},         DU },
+    {OpcodeInfo::all,   {0xDB, 0xE8|_i},    {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, 0xE9},             {FP0D, FP1D},    DU_U },
+    {OpcodeInfo::all,   {0xDD, 0xE9},             {FP0S, FP1S},    DU_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xE8|_i},        {fp32},         DU },
+    {OpcodeInfo::all,   {0xDD, 0xE8|_i},        {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMIP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDF, 0xE8|_i},        {fp32},         DU },
+    {OpcodeInfo::all,   {0xDF, 0xE8|_i},        {fp64},         DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FUCOMPP, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDA, 0xE9},   {FP0D, FP1D}, DU_U },
+    {OpcodeInfo::all,   {0xDA, 0xE9},   {FP0S, FP1S}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDCW, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _5},     {m16},  U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNSTCW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _7},     {m16},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSTSW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B, 0xDF, 0xE0}, {EAX},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNSTSW, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDF, 0xE0},   {EAX},  D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCHS, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xE0},   {FP0D}, DU },
+    {OpcodeInfo::all,   {0xD9, 0xE0},   {FP0S}, DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCLEX, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B, 0xDB, 0xE2}, {}, N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FNCLEX, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, 0xE2},       {}, N },
+END_OPCODES()
+END_MNEMONIC()
+
+//BEGIN_MNEMONIC(FDECSTP, MF_NONE, N)
+//  BEGIN_OPCODES()
+//          {OpcodeInfo::all, {0xD9, 0xF6},       {},     N },
+//  END_OPCODES()
+//END_MNEMONIC()
+
+BEGIN_MNEMONIC(FILD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _0}, {FP0S, m32},    D_U },
+    {OpcodeInfo::all,   {0xDF, _5}, {FP0D, m64},    D_U },
+    {OpcodeInfo::all,   {0xDB, _0}, {FP0S, m32},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//BEGIN_MNEMONIC(FINCSTP, MF_NONE, N)
+//  BEGIN_OPCODES()
+//          {OpcodeInfo::all, {0xD9, 0xF7},       {},     N },
+//  END_OPCODES()
+//END_MNEMONIC()
+
+BEGIN_MNEMONIC(FIST, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _2}, {m32, FP0S},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDB, _3}, {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDF, _7}, {m64, FP0D},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FISTTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xDD, _1}, {m64, FP0D},    D_U },
+    {OpcodeInfo::all,   {0xDB, _1}, {m32, FP0S},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FRNDINT, MF_NONE, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xFC}, {FP0S},    DU },
+    {OpcodeInfo::all,   {0xD9, 0xFC}, {FP0D},    DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _0}, {FP0S, m32},    D_U },
+    {OpcodeInfo::all,   {0xDD, _0}, {FP0D, m64},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDLG2, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xEC}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xEC}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLDLN2, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xED}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xED}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FLD1, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xE8}, {FP0S},    D },
+    {OpcodeInfo::all,   {0xD9, 0xE8}, {FP0D},    D },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FPREM, MF_NONE, N)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF8},       {},     N },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPREM1, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, 0xF5},       {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FST, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _2},         {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDD, _2},         {m64, FP0D},    D_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xD0|_i},    {fp32},         D },
+    {OpcodeInfo::all,   {0xDD, 0xD0|_i},    {fp64},         D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSTP, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xD9, _3},             {m32, FP0S},    D_U },
+    {OpcodeInfo::all,   {0xDD, _3},             {m64, FP0D},    D_U },
+    // A little trick: actually, these 2 opcodes take only index of the
+    // needed register. To make the things similar to other instructions
+    // we encode here as if they took FPREG.
+    {OpcodeInfo::all,   {0xDD, 0xD8|_i},        {fp32},         D },
+    {OpcodeInfo::all,   {0xDD, 0xD8|_i},        {fp64},         D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSQRT, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFA},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFA},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FYL2X, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF1},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF1},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(FYL2XP1, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF9},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF9},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(F2XM1, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF0},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF0},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPATAN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF3},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF3},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FXCH, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xC9},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xC9},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSCALE, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFD},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFD},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FABS, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xE1},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xE1},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FSIN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFE},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFE},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FCOS, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xFF},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xFF},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(FPTAN, MF_NONE, DU)
+  BEGIN_OPCODES()
+          {OpcodeInfo::all, {0xD9, 0xF2},       {FP0S},     DU   },
+          {OpcodeInfo::all, {0xD9, 0xF2},       {FP0D},     DU   },
+  END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~ FPU
+//
+
+BEGIN_MNEMONIC(DIV, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF7, _6},         {EDX, EAX, r_m32},  DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(IDIV, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+#if !defined(_EM64T_)
+    {OpcodeInfo::all,   {0xF6, _7},         {AH, AL, r_m8},     DU_DU_U },
+    {OpcodeInfo::all,   {Size16, 0xF7, _7}, {DX, AX, r_m16},    DU_DU_U },
+#endif
+    {OpcodeInfo::all,   {0xF7, _7},         {EDX, EAX, r_m32},  DU_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _7},  {RDX, RAX, r_m64},  DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(IMUL, MF_AFFECTS_FLAGS, D_DU_U)
+BEGIN_OPCODES()
+    /*{OpcodeInfo::all,   {0xF6, _5},               {AH, AL,        r_m8},  D_DU_U },
+    {OpcodeInfo::all,     {Size16, 0xF7, _5},       {DX, AX,        r_m16}, D_DU_U },
+    */
+    //
+    {OpcodeInfo::all,     {0xF7, _5},               {EDX, EAX, r_m32},  D_DU_U },
+    //todo: this opcode's hash conflicts with IMUL r64,r_m64 - they're both 0.
+    // this particular is not currently used, so we may safely drop it, but need to
+    // revisit the hash implementation
+    // {OpcodeInfo::em64t,   {REX_W, 0xF7, _5},        {RDX, RAX, r_m64},  D_DU_U },
+    //
+    {OpcodeInfo::all,   {Size16, 0x0F, 0xAF, _r}, {r16,r_m16},        DU_U },
+    {OpcodeInfo::all,   {0x0F, 0xAF, _r},         {r32,r_m32},        DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x0F, 0xAF, _r},  {r64,r_m64},        DU_U },
+    {OpcodeInfo::all,   {Size16, 0x6B, _r, ib},   {r16,r_m16,imm8s},  D_DU_U },
+    {OpcodeInfo::all,   {0x6B, _r, ib},           {r32,r_m32,imm8s},  D_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib},    {r64,r_m64,imm8s},  D_DU_U },
+    {OpcodeInfo::all,   {Size16, 0x6B, _r, ib},   {r16,imm8s},        DU_U },
+    {OpcodeInfo::all,   {0x6B, _r, ib},           {r32,imm8s},        DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib},    {r64,imm8s},        DU_U },
+    {OpcodeInfo::all,   {Size16, 0x69, _r, iw},   {r16,r_m16,imm16},  D_U_U },
+    {OpcodeInfo::all,   {0x69, _r, id},           {r32,r_m32,imm32},  D_U_U },
+    {OpcodeInfo::em64t, {REX_W, 0x69, _r, id},    {r64,r_m64,imm32s}, D_U_U },
+    {OpcodeInfo::all,   {Size16, 0x69, _r, iw},   {r16,imm16},        DU_U },
+    {OpcodeInfo::all,   {0x69, _r, id},           {r32,imm32},        DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MUL, MF_AFFECTS_FLAGS, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _4},           {AX, AL, r_m8},     D_DU_U },
+    {OpcodeInfo::all,   {Size16, 0xF7, _4},   {DX, AX, r_m16},    D_DU_U },
+    {OpcodeInfo::all,   {0xF7, _4},           {EDX, EAX, r_m32},  D_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _4},    {RDX, RAX, r_m64},  D_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(INC, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFE, _0},           {r_m8},         DU },
+    {OpcodeInfo::all,   {Size16, 0xFF, _0},   {r_m16},        DU },
+    {OpcodeInfo::all,   {0xFF, _0},           {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xFF, _0},    {r_m64},        DU },
+    {OpcodeInfo::ia32,  {Size16, 0x40|rw},    {r16},          DU },
+    {OpcodeInfo::ia32,  {0x40|rd},            {r32},          DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(INT3, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xCC},     {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_Jcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(J##cc, MF_USES_FLAGS|MF_CONDITIONAL, U ) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {0x70 + ConditionMnemonic_##cc, cb },           { rel8 },       U }, \
+    {OpcodeInfo::ia32,  {Size16, 0x0F, 0x80 + ConditionMnemonic_##cc, cw},      { rel16 },      U }, \
+    {OpcodeInfo::all,   {0x0F, 0x80 + ConditionMnemonic_##cc, cd},      { rel32 },      U }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+
+DEFINE_Jcc_MNEMONIC(O)
+DEFINE_Jcc_MNEMONIC(NO)
+DEFINE_Jcc_MNEMONIC(B)
+DEFINE_Jcc_MNEMONIC(NB)
+DEFINE_Jcc_MNEMONIC(Z)
+DEFINE_Jcc_MNEMONIC(NZ)
+DEFINE_Jcc_MNEMONIC(BE)
+DEFINE_Jcc_MNEMONIC(NBE)
+
+DEFINE_Jcc_MNEMONIC(S)
+DEFINE_Jcc_MNEMONIC(NS)
+DEFINE_Jcc_MNEMONIC(P)
+DEFINE_Jcc_MNEMONIC(NP)
+DEFINE_Jcc_MNEMONIC(L)
+DEFINE_Jcc_MNEMONIC(NL)
+DEFINE_Jcc_MNEMONIC(LE)
+DEFINE_Jcc_MNEMONIC(NLE)
+
+#undef DEFINE_Jcc_MNEMONIC
+
+BEGIN_MNEMONIC(JMP, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xEB, cb},         {rel8},     U },
+    {OpcodeInfo::ia32,  {Size16, 0xE9, cw}, {rel16},    U },
+    {OpcodeInfo::all,   {0xE9, cd},         {rel32},    U },
+    {OpcodeInfo::ia32,  {Size16, 0xFF, _4}, {r_m16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _4},         {r_m32},    U },
+    {OpcodeInfo::em64t, {0xFF, _4},         {r_m64},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LEA, MF_NONE, D_U )
+BEGIN_OPCODES()
+    /*
+    A special case: the LEA instruction itself does not care about size of
+    second operand. This is obviuos why it is, and thus in The Manual, a
+    simple 'm' without size is used.
+    However, in the Jitrino's instrucitons we'll have an operand with a size.
+    Also, the hashing scheme is not supposed to handle OpndSize_Null, and
+    making it to do so will lead to unnecessary complication of hashing
+    scheme. Thus, instead of handling it as a special case, we simply make
+    copies of the opcodes with sizes set.
+        {OpcodeInfo::all,     {0x8D, _r},             {r32, m},       D_U },
+        {OpcodeInfo::em64t, {0x8D, _r},               {r64, m},       D_U },
+    */
+    //Android x86: keep r32, m32 only, otherwise, will have decoding error
+    //{OpcodeInfo::all,   {0x8D, _r},     {r32, m8},      D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m8},      D_U },
+    //{OpcodeInfo::all,   {0x8D, _r},     {r32, m16},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m16},     D_U },
+    {OpcodeInfo::all,   {0x8D, _r},     {r32, m32},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m32},     D_U },
+    {OpcodeInfo::all,   {0x8D, _r},     {r32, m64},     D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8D, _r},     {r64, m64},     D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOP, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE2, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOPE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE1, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(LOOPNE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xE0, cb},     {ECX, rel8},    DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOV, MF_NONE, D_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x88, _r},         {r_m8,r8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0x89, _r}, {r_m16,r16},    D_U },
+    {OpcodeInfo::all,   {0x89, _r},         {r_m32,r32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x89, _r},  {r_m64,r64},    D_U },
+    {OpcodeInfo::all,   {0x8A, _r},         {r8,r_m8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0x8B, _r}, {r16,r_m16},    D_U },
+    {OpcodeInfo::all,   {0x8B, _r},         {r32,r_m32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x8B, _r},  {r64,r_m64},    D_U },
+
+    {OpcodeInfo::all,   {0xB0|rb},          {r8,imm8},      D_U },
+
+    {OpcodeInfo::all,   {Size16, 0xB8|rw},  {r16,imm16},    D_U },
+    {OpcodeInfo::all,   {0xB8|rd},          {r32,imm32},    D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xB8|rd},   {r64,imm64},    D_U },
+    {OpcodeInfo::all,   {0xC6, _0},         {r_m8,imm8},    D_U },
+
+    {OpcodeInfo::all,   {Size16, 0xC7, _0}, {r_m16,imm16},  D_U },
+    {OpcodeInfo::all,   {0xC7, _0},         {r_m32,imm32},  D_U },
+    {OpcodeInfo::em64t, {REX_W, 0xC7, _0},  {r_m64,imm32s}, D_U },
+
+    {OpcodeInfo::decoder,   {0xA0},         {AL,  moff8},  D_U },
+    {OpcodeInfo::decoder,   {Size16, 0xA1}, {AX,  moff16},  D_U },
+    {OpcodeInfo::decoder,   {0xA1},         {EAX, moff32},  D_U },
+    //{OpcodeInfo::decoder64,   {REX_W, 0xA1},  {RAX, moff64},  D_U },
+
+    {OpcodeInfo::decoder,   {0xA2},         {moff8, AL},  D_U },
+    {OpcodeInfo::decoder,   {Size16, 0xA3}, {moff16, AX},  D_U },
+    {OpcodeInfo::decoder,   {0xA3},         {moff32, EAX},  D_U },
+    //{OpcodeInfo::decoder64,   {REX_W, 0xA3},  {moff64, RAX},  D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+
+BEGIN_MNEMONIC(XCHG, MF_NONE, DU_DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x87, _r},   {r_m32,r32},    DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0x6F, _r},   {mm64, mm_m64}, D_U },
+    {OpcodeInfo::all,   {0x0F, 0x7F, _r},   {mm_m64, mm64}, D_U },
+#endif
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x7E },  {xmm64, xmm_m64},       D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD6 },  {xmm_m64, xmm64},       D_U },
+//    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r},  {xmm64, r_m64}, D_U },
+//    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r},  {r_m64, xmm64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r},  {xmm64, r64}, D_U },
+    {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r},  {r64, xmm64}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x6E, _r}, {xmm32, r_m32}, D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x7E, _r}, {r_m32, xmm32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// A bunch of MMX instructions
+//
+#ifdef _HAVE_MMX_
+
+BEGIN_MNEMONIC(EMMS, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x77},       {},             N },
+END_OPCODES()
+END_MNEMONIC()
+
+#endif
+
+BEGIN_MNEMONIC(PADDQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xD4, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD4, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PAND, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xDB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xDB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POR, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xEB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xEB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PSUBQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xFB, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xFB, _r},   {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PANDN, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xDF, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xDF, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+BEGIN_MNEMONIC(PSLLQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xF3, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xF3, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+BEGIN_MNEMONIC(PSRLQ, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xD3, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xD3, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PXOR, MF_NONE, DU_U)
+BEGIN_OPCODES()
+#ifdef _HAVE_MMX_
+    {OpcodeInfo::all,   {0x0F, 0xEF, _r},   {mm64, mm_m64}, DU_U },
+#endif
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xEF, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVAPD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x28, _r},   {xmm64, xmm_m64},   D_U },
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x29, _r},   {xmm_m64, xmm64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(MOVSD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x10, _r},   {xmm64, xmm_m64},   D_U },
+    {OpcodeInfo::all, {0xF2, 0x0F, 0x11, _r},   {xmm_m64, xmm64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVSS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x10, _r},   {xmm32, xmm_m32}, D_U },
+    {OpcodeInfo::all, {0xF3, 0x0F, 0x11, _r},   {xmm_m32, xmm32}, D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVSX, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {Size16, 0x0F, 0xBE, _r}, {r16, r_m8s},     D_U },
+    {OpcodeInfo::all,       {0x0F, 0xBE, _r},         {r32, r_m8s},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xBE, _r},  {r64, r_m8s},     D_U },
+
+    {OpcodeInfo::all,       {0x0F, 0xBF, _r},         {r32, r_m16s},    D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xBF, _r},  {r64, r_m16s},    D_U },
+
+    {OpcodeInfo::em64t,     {REX_W, 0x63, _r},        {r64, r_m32s},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVZX, MF_NONE, D_U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,       {Size16, 0x0F, 0xB6, _r}, {r16, r_m8u},     D_U },
+    {OpcodeInfo::all,       {0x0F, 0xB6, _r},         {r32, r_m8u},     D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xB6, _r},  {r64, r_m8u},     D_U },
+
+    {OpcodeInfo::all,       {0x0F, 0xB7, _r},         {r32, r_m16u},    D_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x0F, 0xB7, _r},  {r64, r_m16u},    D_U },
+    //workaround to get r/rm32->r64 ZX mov functionality:
+    //simple 32bit reg copying zeros high bits in 64bit reg
+    {OpcodeInfo::em64t,     {0x8B, _r},               {r64, r_m32u},    D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MULSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x59, _r}, {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MULSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x59, _r}, {xmm32, xmm_m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NEG, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _3},         {r_m8},         DU },
+
+    {OpcodeInfo::all,   {Size16, 0xF7, _3}, {r_m16},        DU },
+    {OpcodeInfo::all,   {0xF7, _3},         {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _3},  {r_m64},        DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NOP, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x90}, {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(NOT, MF_AFFECTS_FLAGS, DU )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF6, _2},           {r_m8},         DU },
+    {OpcodeInfo::all,   {Size16, 0xF7, _2},   {r_m16},        DU },
+    {OpcodeInfo::all,   {0xF7, _2},           {r_m32},        DU },
+    {OpcodeInfo::em64t, {REX_W, 0xF7, _2},    {r_m64},        DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POP, MF_NONE, D)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {Size16, 0x8F, _0}, {r_m16},    D },
+    {OpcodeInfo::ia32,  {0x8F, _0},         {r_m32},    D },
+    {OpcodeInfo::em64t, {0x8F, _0},         {r_m64},    D },
+
+    {OpcodeInfo::all,   {Size16, 0x58|rw }, {r16},      D },
+    {OpcodeInfo::ia32,  {0x58|rd },         {r32},      D },
+    {OpcodeInfo::em64t, {0x58|rd },         {r64},      D },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(POPFD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9D},     {},         N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PREFETCH, MF_NONE, U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x18, _0},   {m8},         U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PUSH, MF_NONE, U )
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {Size16, 0xFF, _6}, {r_m16},    U },
+    {OpcodeInfo::ia32,  {0xFF, _6},         {r_m32},    U },
+    {OpcodeInfo::em64t, {0xFF, _6},         {r_m64},    U },
+
+    {OpcodeInfo::all,   {Size16, 0x50|rw }, {r16},      U },
+    {OpcodeInfo::ia32,  {0x50|rd },         {r32},      U },
+    {OpcodeInfo::em64t, {0x50|rd },         {r64},      U },
+
+    {OpcodeInfo::all,   {0x6A},         {imm8},     U },
+    {OpcodeInfo::all,   {Size16, 0x68}, {imm16},    U },
+    {OpcodeInfo::ia32,  {0x68},         {imm32},    U },
+//          {OpcodeInfo::em64t,   {0x68},   {imm64},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(PUSHFD, MF_USES_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9C},             {},        N },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(RET, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xC3},       {},         N },
+    {OpcodeInfo::all,   {0xC2, iw},   {imm16},    U },
+END_OPCODES()
+END_MNEMONIC()
+
+#define DEFINE_SETcc_MNEMONIC( cc ) \
+        BEGIN_MNEMONIC(SET##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU) \
+BEGIN_OPCODES() \
+    {OpcodeInfo::all,   {0x0F,     0x90 + ConditionMnemonic_##cc}, {r_m8},  DU }, \
+END_OPCODES() \
+END_MNEMONIC()
+
+DEFINE_SETcc_MNEMONIC(O)
+DEFINE_SETcc_MNEMONIC(NO)
+DEFINE_SETcc_MNEMONIC(B)
+DEFINE_SETcc_MNEMONIC(NB)
+DEFINE_SETcc_MNEMONIC(Z)
+DEFINE_SETcc_MNEMONIC(NZ)
+DEFINE_SETcc_MNEMONIC(BE)
+DEFINE_SETcc_MNEMONIC(NBE)
+
+DEFINE_SETcc_MNEMONIC(S)
+DEFINE_SETcc_MNEMONIC(NS)
+DEFINE_SETcc_MNEMONIC(P)
+DEFINE_SETcc_MNEMONIC(NP)
+DEFINE_SETcc_MNEMONIC(L)
+DEFINE_SETcc_MNEMONIC(NL)
+DEFINE_SETcc_MNEMONIC(LE)
+DEFINE_SETcc_MNEMONIC(NLE)
+
+#undef DEFINE_SETcc_MNEMONIC
+
+#define DEFINE_SHIFT_MNEMONIC(nam, slash_num, flags) \
+BEGIN_MNEMONIC(nam, flags, DU_U) \
+BEGIN_OPCODES()\
+    /* D0 & D1 opcodes are added w/o 2nd operand (1) because */\
+    /* they are used for decoding only so only instruction length is needed */\
+    {OpcodeInfo::decoder,   {0xD0, slash_num},            {r_m8/*,const_1*/},   DU },\
+    {OpcodeInfo::all,       {0xD2, slash_num},              {r_m8,  CL},        DU_U },\
+    {OpcodeInfo::all,       {0xC0, slash_num, ib},          {r_m8,  imm8},      DU_U },\
+\
+    {OpcodeInfo::decoder,   {Size16, 0xD1, slash_num},    {r_m16/*,const_1*/},  DU },\
+    {OpcodeInfo::all,       {Size16, 0xD3, slash_num},      {r_m16, CL},        DU_U },\
+    {OpcodeInfo::all,       {Size16, 0xC1, slash_num, ib},  {r_m16, imm8 },     DU_U },\
+\
+    {OpcodeInfo::decoder,   {0xD1, slash_num},              {r_m32/*,const_1*/}, DU },\
+    {OpcodeInfo::decoder64, {REX_W, 0xD1, slash_num},       {r_m64/*,const_1*/}, DU },\
+\
+    {OpcodeInfo::all,       {0xD3, slash_num},              {r_m32, CL},        DU_U },\
+    {OpcodeInfo::em64t,     {REX_W, 0xD3, slash_num},       {r_m64, CL},        DU_U },\
+\
+    {OpcodeInfo::all,       {0xC1, slash_num, ib},          {r_m32, imm8},      DU_U },\
+    {OpcodeInfo::em64t,     {REX_W, 0xC1, slash_num, ib},   {r_m64, imm8},      DU_U },\
+END_OPCODES()\
+END_MNEMONIC()
+
+
+DEFINE_SHIFT_MNEMONIC(ROL, _0, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(ROR, _1, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(RCL, _2, MF_AFFECTS_FLAGS|MF_USES_FLAGS)
+DEFINE_SHIFT_MNEMONIC(RCR, _3, MF_AFFECTS_FLAGS|MF_USES_FLAGS)
+
+DEFINE_SHIFT_MNEMONIC(SAL, _4, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(SHR, _5, MF_AFFECTS_FLAGS)
+DEFINE_SHIFT_MNEMONIC(SAR, _7, MF_AFFECTS_FLAGS)
+
+#undef DEFINE_SHIFT_MNEMONIC
+
+BEGIN_MNEMONIC(SHLD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xA5},   {r_m32, r32, CL}, DU_DU_U },
+    {OpcodeInfo::all,   {0x0F, 0xA4},   {r_m32, r32, imm8}, DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SHRD, MF_AFFECTS_FLAGS, N)
+// TODO: the def/use info is wrong
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0xAD},   {r_m32, r32, CL}, DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(SUBSD, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF2, 0x0F, 0x5C, _r}, {xmm64, xmm_m64}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SUBSS, MF_NONE, DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5C, _r}, {xmm32, xmm_m32}, DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(TEST, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+
+    {OpcodeInfo::decoder,   {0xA8, ib},             { AL, imm8},    U_U },
+    {OpcodeInfo::decoder,   {0xA9, iw},             { AX, imm16},   U_U },
+    {OpcodeInfo::decoder,   {0xA9, id},             { EAX, imm32},  U_U },
+    {OpcodeInfo::decoder64, {REX_W, 0xA9, id},      { RAX, imm32s}, U_U },
+
+    {OpcodeInfo::all,       {0xF6, _0, ib},         {r_m8,imm8},    U_U },
+
+    {OpcodeInfo::all,       {Size16, 0xF7, _0, iw}, {r_m16,imm16},  U_U },
+    {OpcodeInfo::all,       {0xF7, _0, id},         {r_m32,imm32},  U_U },
+    {OpcodeInfo::em64t,     {REX_W, 0xF7, _0, id},  {r_m64,imm32s}, U_U },
+
+    {OpcodeInfo::all,       {0x84, _r},             {r_m8,r8},      U_U },
+
+    {OpcodeInfo::all,       {Size16, 0x85, _r},     {r_m16,r16},    U_U },
+    {OpcodeInfo::all,       {0x85, _r},             {r_m32,r32},    U_U },
+    {OpcodeInfo::em64t,     {REX_W, 0x85, _r},      {r_m64,r64},    U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(UCOMISD, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x2E, _r}, {xmm64, xmm_m64}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(UCOMISS, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x2E, _r},       {xmm32, xmm_m32}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(COMISD, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x2F, _r}, {xmm64, xmm_m64}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(COMISS, MF_AFFECTS_FLAGS, U_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x0F, 0x2F, _r},       {xmm32, xmm_m32}, U_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(XORPD, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x66, 0x0F, 0x57, _r},   {xmm64, xmm_m64},   DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(XORPS, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U)
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x0F, 0x57, _r},   {xmm32, xmm_m32},       DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTDQ2PD, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0xE6}, {xmm64, xmm_m64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTDQ2PS, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x0F, 0x5B, _r},   {xmm32, xmm_m32},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTTPD2DQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0x66, 0x0F, 0xE6}, {xmm64, xmm_m64},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CVTTPS2DQ, MF_NONE, D_U )
+BEGIN_OPCODES()
+    //Note: they're actually 128 bits
+    {OpcodeInfo::all,   {0xF3, 0x0F, 0x5B, _r},   {xmm32, xmm_m32},   D_U },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// String operations
+//
+BEGIN_MNEMONIC(STD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFD},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CLD, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xFC},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(SCAS, MF_AFFECTS_FLAGS, N)
+// to be symmetric, this mnemonic must have either m32 or RegName_EAX
+// but as long, as Jitrino's CG does not use the mnemonic, leaving it
+// in its natural form
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xAF},         {},     N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(STOS, MF_AFFECTS_FLAGS, DU_DU_U)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0xAB},         {EDI, ECX, EAX},   DU_DU_U },
+    {OpcodeInfo::all,   {0xAA},         {EDI, ECX, AL},    DU_DU_U },
+    {OpcodeInfo::em64t, {REX_W, 0xAB},  {RDI, RCX, RAX},   DU_DU_U },
+END_OPCODES()
+END_MNEMONIC()
+
+/*
+MOVS and CMPS are the special cases.
+Most the code in both CG and Encoder do not expect 2 memory operands.
+Also, they are not supposed to setup constrains on which register the
+memory reference must reside - m8,m8 or m32,m32 is not the choice.
+We can't use r8,r8 either - will have problem with 8bit EDI, ESI.
+So, as the workaround we do r32,r32 and specify size of the operand through
+the specific mnemonic - the same is in the codegen.
+*/
+BEGIN_MNEMONIC(MOVS8, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA4},         {r32,r32,ECX},    DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA4},         {r64,r64,RCX},    DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS16, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {Size16, 0xA5}, {r32,r32,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {Size16, 0xA5}, {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS32, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA5},         {r32,r32,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA5},         {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(MOVS64, MF_NONE, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::em64t, {REX_W,0xA5},   {r64,r64,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSB, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA6},         {ESI,EDI,ECX},    DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA6},         {RSI,RDI,RCX},    DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSW, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {Size16, 0xA7}, {ESI,EDI,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {Size16, 0xA7}, {RSI,RDI,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(CMPSD, MF_AFFECTS_FLAGS, DU_DU_DU)
+BEGIN_OPCODES()
+    {OpcodeInfo::ia32,  {0xA7},         {ESI,EDI,ECX},  DU_DU_DU },
+    {OpcodeInfo::em64t, {0xA7},         {RSI,RDI,RCX},  DU_DU_DU },
+END_OPCODES()
+END_MNEMONIC()
+
+
+BEGIN_MNEMONIC(WAIT, MF_AFFECTS_FLAGS, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::all,   {0x9B},         {},       N },
+END_OPCODES()
+END_MNEMONIC()
+
+//
+// ~String operations
+//
+
+//
+//Note: the instructions below added for the sake of disassembling routine.
+// They need to have flags, params and params usage to be defined more precisely.
+//
+BEGIN_MNEMONIC(LEAVE, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xC9},         {},       N },
+END_OPCODES()
+END_MNEMONIC()
+
+BEGIN_MNEMONIC(ENTER, MF_NONE, N)
+BEGIN_OPCODES()
+    {OpcodeInfo::decoder,   {0xC8, iw, ib},           {imm16, imm8},  N },
+END_OPCODES()
+END_MNEMONIC()
+
+};      // ~masterEncodingTable[]
+
+ENCODER_NAMESPACE_END
+
+//#include <algorithm>
+
+ENCODER_NAMESPACE_START
+
+static bool mnemonic_info_comparator(const MnemonicInfo& one,
+                                     const MnemonicInfo& two)
+{
+    return one.mn < two.mn;
+}
+
+
+static int compareMnemonicInfo(const void* info1, const void* info2)
+{
+    Mnemonic id1, id2;
+
+    id1 = ((const MnemonicInfo*) info1)->mn;
+    id2 = ((const MnemonicInfo*) info2)->mn;
+    if (id1 < id2)
+        return -1;
+    if (id1 > id2)
+        return 1;
+    return 0;
+}
+
+int EncoderBase::buildTable(void)
+{
+    // A check: all mnemonics must be covered
+    assert(COUNTOF(masterEncodingTable) == Mnemonic_Count);
+    // sort out the mnemonics so the list become ordered
+#if 0 //Android x86
+    std::sort(masterEncodingTable, masterEncodingTable+Mnemonic_Count,
+              mnemonic_info_comparator);
+#else
+    qsort(masterEncodingTable, Mnemonic_Count, sizeof(MnemonicInfo), compareMnemonicInfo);
+#endif
+    //
+    // clear the things
+    //
+    memset(opcodesHashMap, NOHASH, sizeof(opcodesHashMap));
+    memset(opcodes, 0, sizeof(opcodes));
+    //
+    // and, finally, build it
+    for (unsigned i=0; i<Mnemonic_Count; i++) {
+        assert((Mnemonic)i == (masterEncodingTable + i)->mn);
+        buildMnemonicDesc(masterEncodingTable+i);
+    }
+    return 0;
+}
+
+void EncoderBase::buildMnemonicDesc(const MnemonicInfo * minfo)
+{
+    MnemonicDesc& mdesc = mnemonics[minfo->mn];
+    mdesc.mn = minfo->mn;
+    mdesc.flags = minfo->flags;
+    mdesc.roles = minfo->roles;
+    mdesc.name = minfo->name;
+
+    //
+    // fill the used opcodes
+    //
+    for (unsigned i=0, oindex=0; i<COUNTOF(minfo->opcodes); i++) {
+
+        const OpcodeInfo& oinfo = minfo->opcodes[i];
+        OpcodeDesc& odesc = opcodes[minfo->mn][oindex];
+        // last opcode ?
+        if (oinfo.opcode[0] == OpcodeByteKind_LAST) {
+            // mark the opcode 'last', exit
+            odesc.opcode_len = 0;
+            odesc.last = 1;
+            break;
+        }
+        odesc.last = 0;
+#ifdef _EM64T_
+        if (oinfo.platf == OpcodeInfo::ia32) { continue; }
+        if (oinfo.platf == OpcodeInfo::decoder32) { continue; }
+#else
+        if (oinfo.platf == OpcodeInfo::em64t) { continue; }
+        if (oinfo.platf == OpcodeInfo::decoder64) { continue; }
+#endif
+        if (oinfo.platf == OpcodeInfo::decoder64 ||
+            oinfo.platf == OpcodeInfo::decoder32) {
+             odesc.platf = OpcodeInfo::decoder;
+        }
+        else {
+            odesc.platf = (char)oinfo.platf;
+        }
+        //
+        // fill out opcodes
+        //
+        unsigned j = 0;
+        odesc.opcode_len = 0;
+        for(; oinfo.opcode[j]; j++) {
+            unsigned opcod = oinfo.opcode[j];
+            unsigned kind = opcod&OpcodeByteKind_KindMask;
+            if (kind == OpcodeByteKind_REX_W) {
+                odesc.opcode[odesc.opcode_len++] = (unsigned char)0x48;
+                continue;
+            }
+            else if(kind != 0 && kind != OpcodeByteKind_ZeroOpcodeByte) {
+                break;
+            }
+            unsigned lowByte = (opcod & OpcodeByteKind_OpcodeMask);
+            odesc.opcode[odesc.opcode_len++] = (unsigned char)lowByte;
+        }
+        assert(odesc.opcode_len<5);
+        odesc.aux0 = odesc.aux1 = 0;
+        if (oinfo.opcode[j] != 0) {
+            odesc.aux0 = oinfo.opcode[j];
+            assert((odesc.aux0 & OpcodeByteKind_KindMask) != 0);
+            ++j;
+            if(oinfo.opcode[j] != 0) {
+                odesc.aux1 = oinfo.opcode[j];
+                assert((odesc.aux1 & OpcodeByteKind_KindMask) != 0);
+            }
+        }
+        else if (oinfo.roles.count>=2) {
+            if (((oinfo.opnds[0].kind&OpndKind_Mem) &&
+                 (isRegKind(oinfo.opnds[1].kind))) ||
+                ((oinfo.opnds[1].kind&OpndKind_Mem) &&
+                 (isRegKind(oinfo.opnds[0].kind)))) {
+                 // Example: MOVQ xmm1, xmm/m64 has only opcodes
+                 // same with SHRD
+                 // Adding fake /r
+                 odesc.aux0 = _r;
+            }
+        }
+        else if (oinfo.roles.count==1) {
+            if (oinfo.opnds[0].kind&OpndKind_Mem) {
+                 // Example: SETcc r/m8, adding fake /0
+                 odesc.aux0 = _0;
+            }
+        }
+        // check imm
+        if (oinfo.roles.count > 0 &&
+            (oinfo.opnds[0].kind == OpndKind_Imm ||
+            oinfo.opnds[oinfo.roles.count-1].kind == OpndKind_Imm)) {
+            // Example: CALL cd, PUSH imm32 - they fit both opnds[0] and
+            // opnds[oinfo.roles.count-1].
+            // The A3 opcode fits only opnds[0] - it's currently have
+            // MOV imm32, EAX. Looks ridiculous, but this is how the
+            // moffset is currently implemented. Will need to fix together
+            // with other usages of moff.
+            // adding fake /cd or fake /id
+            unsigned imm_opnd_index =
+                oinfo.opnds[0].kind == OpndKind_Imm ? 0 : oinfo.roles.count-1;
+            OpndSize sz = oinfo.opnds[imm_opnd_index].size;
+            unsigned imm_encode, coff_encode;
+            if (sz==OpndSize_8) {imm_encode = ib; coff_encode=cb; }
+            else if (sz==OpndSize_16) {imm_encode = iw; coff_encode=cw;}
+            else if (sz==OpndSize_32) {imm_encode = id; coff_encode=cd; }
+            else if (sz==OpndSize_64) {imm_encode = io; coff_encode=0xCC; }
+            else { assert(false); imm_encode=0xCC; coff_encode=0xCC; }
+            if (odesc.aux1 == 0) {
+                if (odesc.aux0==0) {
+                    odesc.aux0 = imm_encode;
+                }
+                else {
+                    if (odesc.aux0 != imm_encode && odesc.aux0 != coff_encode) {
+                        odesc.aux1 = imm_encode;
+                    }
+                }
+            }
+            else {
+                assert(odesc.aux1==imm_encode);
+            }
+
+        }
+
+        assert(sizeof(odesc.opnds) == sizeof(oinfo.opnds));
+        memcpy(odesc.opnds, oinfo.opnds, sizeof(odesc.opnds));
+        odesc.roles = oinfo.roles;
+        odesc.first_opnd = 0;
+        if (odesc.opnds[0].reg != RegName_Null) {
+            ++odesc.first_opnd;
+            if (odesc.opnds[1].reg != RegName_Null) {
+                ++odesc.first_opnd;
+            }
+        }
+
+        if (odesc.platf == OpcodeInfo::decoder) {
+            // if the opcode is only for decoding info, then do not hash it.
+            ++oindex;
+            continue;
+        }
+
+        //
+        // check whether the operand info is a mask (i.e. r_m*).
+        // in this case, split the info to have separate entries for 'r'
+        // and for 'm'.
+        // the good news is that there can be only one such operand.
+        //
+        int opnd2split = -1;
+        for (unsigned k=0; k<oinfo.roles.count; k++) {
+            if ((oinfo.opnds[k].kind & OpndKind_Mem) &&
+                (OpndKind_Mem != oinfo.opnds[k].kind)) {
+                opnd2split = k;
+                break;
+            }
+        };
+
+        if (opnd2split == -1) {
+            // not a mask, hash it, store it, continue.
+            unsigned short hash = getHash(&oinfo);
+            opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+            ++oindex;
+            continue;
+        };
+
+        OpcodeInfo storeItem = oinfo;
+        unsigned short hash;
+
+        // remove the memory part of the mask, and store only 'r' part
+        storeItem.opnds[opnd2split].kind = (OpndKind)(storeItem.opnds[opnd2split].kind & ~OpndKind_Mem);
+        hash = getHash(&storeItem);
+        if (opcodesHashMap[minfo->mn][hash] == NOHASH) {
+            opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+        }
+        // else {
+        // do not overwrite if there is something there, just check that operands match
+        // the reason is that for some instructions there are several possibilities:
+        // say 'DEC r' may be encode as either '48+r' or 'FF /1', and I believe
+        // the first one is better for 'dec r'.
+        // as we're currently processing an opcode with memory part in operand,
+        // leave already filled items intact, so if there is 'OP reg' there, this
+        // better choice will be left in the table instead of 'OP r_m'
+        // }
+
+        // compute hash of memory-based operand, 'm' part in 'r_m'
+        storeItem.opnds[opnd2split].kind = OpndKind_Mem;
+        hash = getHash(&storeItem);
+        // should not happen: for the r_m opcodes, there is a possibility
+        // that hash value of 'r' part intersects with 'OP r' value, but it's
+        // impossible for 'm' part.
+        assert(opcodesHashMap[minfo->mn][hash] == NOHASH);
+        opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex;
+
+        ++oindex;
+    }
+}
+
+ENCODER_NAMESPACE_END
diff --git a/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp b/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp
new file mode 100644
index 0000000..71e0e06
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_wrapper.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include "cutils/log.h"
+#include "enc_base.h"
+#include "enc_wrapper.h"
+#include "dec_base.h"
+
+//#define PRINT_ENCODER_STREAM
+bool dump_x86_inst = false;
+//map_reg
+const RegName map_of_regno_2_regname[] = {
+    RegName_EAX,    RegName_EBX,    RegName_ECX,    RegName_EDX,
+    RegName_EDI,    RegName_ESI,    RegName_ESP,    RegName_EBP,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null,
+    RegName_Null,   RegName_Null,   //SCRATCH
+    RegName_Null,   RegName_Null,   RegName_Null,   RegName_Null
+};
+
+//getRegSize, getAliasReg:
+//OpndSize, RegName, OpndExt: enum enc_defs.h
+inline void add_r(EncoderBase::Operands & args, int physicalReg, OpndSize sz, OpndExt ext = OpndExt_None) {
+    RegName reg = map_of_regno_2_regname[physicalReg];
+    if (sz != getRegSize(reg)) {
+       reg = getAliasReg(reg, sz);
+    }
+    args.add(EncoderBase::Operand(reg, ext));
+}
+inline void add_m(EncoderBase::Operands & args, int baseReg, int disp, OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  RegName_Null, 0,
+                                  disp, ext));
+}
+inline void add_m_scale(EncoderBase::Operands & args, int baseReg, int indexReg, int scale,
+                        OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  map_of_regno_2_regname[indexReg], scale,
+                                  0, ext));
+}
+inline void add_m_disp_scale(EncoderBase::Operands & args, int baseReg, int disp, int indexReg, int scale,
+                        OpndSize sz, OpndExt ext = OpndExt_None) {
+    args.add(EncoderBase::Operand(sz,
+                                  map_of_regno_2_regname[baseReg],
+                                  map_of_regno_2_regname[indexReg], scale,
+                                  disp, ext));
+}
+
+inline void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) {
+    return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i));
+}
+inline void add_imm(EncoderBase::Operands & args, OpndSize sz, int value, bool is_signed) {
+    //assert(n_size != imm.get_size());
+    args.add(EncoderBase::Operand(sz, value,
+             is_signed ? OpndExt_Signed : OpndExt_Zero));
+}
+
+#define MAX_DECODED_STRING_LEN 1024
+char tmpBuffer[MAX_DECODED_STRING_LEN];
+
+void printOperand(const EncoderBase::Operand & opnd) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    sz = strlen(tmpBuffer);
+    if(opnd.size() != OpndSize_32) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
+                       getOpndSizeString(opnd.size()));
+    }
+    if(opnd.is_mem()) {
+        if(opnd.scale() != 0) {
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz,
+                           "%d(%s,%s,%d)", opnd.disp(),
+                           getRegNameString(opnd.base()),
+                           getRegNameString(opnd.index()), opnd.scale());
+        } else {
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%d(%s)",
+                           opnd.disp(), getRegNameString(opnd.base()));
+        }
+    }
+    if(opnd.is_imm()) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "#%x",
+                       (int)opnd.imm());
+    }
+    if(opnd.is_reg()) {
+        sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s",
+                       getRegNameString(opnd.reg()));
+    }
+}
+//TODO: the order of operands
+//to make the printout have the same order as assembly in .S
+//I reverse the order here
+void printDecoderInst(Inst & decInst) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    sz = strlen(tmpBuffer);
+    sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ",
+                   EncoderBase::toStr(decInst.mn));
+    for(unsigned int k = 0; k < decInst.argc; k++) {
+        if(k > 0) {
+            sz = strlen(tmpBuffer);
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
+        }
+        printOperand(decInst.operands[decInst.argc-1-k]);
+    }
+    ALOGE("%s", tmpBuffer);
+}
+void printOperands(EncoderBase::Operands& opnds) {
+    unsigned int sz;
+    if(!dump_x86_inst) return;
+    for(unsigned int k = 0; k < opnds.count(); k++) {
+        if(k > 0) {
+            sz = strlen(tmpBuffer);
+            sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", ");
+        }
+        printOperand(opnds[opnds.count()-1-k]);
+    }
+}
+void printEncoderInst(Mnemonic m, EncoderBase::Operands& opnds) {
+    if(!dump_x86_inst) return;
+    snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- ENC %s ",
+             EncoderBase::toStr(m));
+    printOperands(opnds);
+    ALOGE("%s", tmpBuffer);
+}
+int decodeThenPrint(char* stream_start) {
+    if(!dump_x86_inst) return 0;
+    snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- INST @ %p: ",
+             stream_start);
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream_start, &decInst);
+    printDecoderInst(decInst);
+    return numBytes;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm(Mnemonic m, OpndSize size, int imm, char * stream) {
+    EncoderBase::Operands args;
+    //assert(imm.get_size() == size_32);
+    add_imm(args, size, imm, true/*is_signed*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    return numBytes;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_cur_operand_offset(int opnd_id)
+{
+    return (unsigned)EncoderBase::getOpndLocation(opnd_id);
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm(int imm, char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    EncoderBase::Operands args;
+    //assert(imm.get_size() == size_32);
+    add_imm(args, decInst.operands[0].size(), imm, true/*is_signed*/);
+    char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(decInst.mn, args);
+    decodeThenPrint(stream);
+#endif
+    return stream_next;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem(Mnemonic m, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    if(m == Mnemonic_IDIV || m == Mnemonic_MUL || m == Mnemonic_IMUL) {
+      add_r(args, 0/*eax*/, size);
+      add_r(args, 3/*edx*/, size);
+    }
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+//both operands have same size
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_reg(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type, char * stream) {
+    if((m == Mnemonic_MOV || m == Mnemonic_MOVQ) && reg == reg2) return stream;
+    EncoderBase::Operands args;
+    add_r(args, reg2, size); //destination
+    if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL || m == Mnemonic_SAR)
+      add_r(args, reg, OpndSize_8);
+    else
+      add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_reg(Mnemonic m, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m_scale(args, base_reg, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_m_scale(args, base_reg, index_reg, scale, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size);
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream) {
+    EncoderBase::Operands args;
+    add_m_disp_scale(args, base_reg, disp, index_reg, scale, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg(Mnemonic m, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, size); //dst
+    if(m == Mnemonic_IMUL) add_r(args, reg, size); //src CHECK
+    if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
+       || m == Mnemonic_SAR || m == Mnemonic_ROR)  //fix for shift opcodes
+      add_imm(args, OpndSize_8, imm, true/*is_signed*/);
+    else
+      add_imm(args, size, imm, true/*is_signed*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream) {
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    EncoderBase::Operands args;
+    args.add(decInst.operands[0]);
+    add_imm(args, decInst.operands[1].size(), imm, true/*is_signed*/);
+    char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(decInst.mn, args);
+    decodeThenPrint(stream);
+#endif
+    return stream_next;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_mem(Mnemonic m, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    if (m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL
+        || m == Mnemonic_SAR || m == Mnemonic_ROR)
+        size = OpndSize_8;
+    add_imm(args, size, imm, true);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_m(args, base_reg, disp, size);
+    // a fake FP register as operand
+    add_fp(args, reg, size == OpndSize_64/*is_double*/);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  int reg, char * stream) {
+    EncoderBase::Operands args;
+    // a fake FP register as operand
+    add_fp(args, reg, size == OpndSize_64/*is_double*/);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_return(char * stream) {
+    EncoderBase::Operands args;
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_RET, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_RET, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char * stream) {
+    //Mnemonic m = pop ? Mnemonic_FUCOMP : Mnemonic_FUCOM;
+    Mnemonic m = pop ? Mnemonic_FUCOMIP : Mnemonic_FUCOMI;
+    //a single operand or 2 operands?
+    //FST ST(i) has a single operand in encoder.inl?
+    EncoderBase::Operands args;
+    add_fp(args, reg, isDouble);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, m, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(m, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVZX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg, OpndSize_32);
+    add_m(args, base_reg, disp, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVSX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg2, OpndSize_32); //destination
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVZX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical,int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream) {
+    EncoderBase::Operands args;
+    add_r(args, reg2, OpndSize_32); //destination
+    add_r(args, reg, size);
+    char* stream_start = stream;
+    stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+#ifdef PRINT_ENCODER_STREAM
+    printEncoderInst(Mnemonic_MOVSX, args);
+    decodeThenPrint(stream_start);
+#endif
+    return stream;
+}
+
+// Disassemble the operand "opnd" and put the readable format in "strbuf"
+// up to a string length of "len".
+unsigned int DisassembleOperandToBuf(const EncoderBase::Operand& opnd, char* strbuf, unsigned int len)
+{
+    unsigned int sz = 0;
+    if(opnd.size() != OpndSize_32) {
+        sz += snprintf(&strbuf[sz], len-sz, "%s ",
+                       getOpndSizeString(opnd.size()));
+    }
+    if(opnd.is_mem()) {
+        if(opnd.scale() != 0) {
+            sz += snprintf(&strbuf[sz], len-sz, "%d(%s,%s,%d)", opnd.disp(),
+                           getRegNameString(opnd.base()),
+                           getRegNameString(opnd.index()), opnd.scale());
+        } else {
+            sz += snprintf(&strbuf[sz], len-sz, "%d(%s)",
+                           opnd.disp(), getRegNameString(opnd.base()));
+        }
+    } else if(opnd.is_imm()) {
+        sz += snprintf(&strbuf[sz], len-sz, "#%x", (int)opnd.imm());
+    } else if(opnd.is_reg()) {
+        sz += snprintf(&strbuf[sz], len-sz, "%s",
+                       getRegNameString(opnd.reg()));
+    }
+    return sz;
+}
+
+// Disassemble the instruction "decInst" and put the readable format
+// in "strbuf" up to a string length of "len".
+void DisassembleInstToBuf(Inst& decInst, char* strbuf, unsigned int len)
+{
+    unsigned int sz = 0;
+    int k;
+    sz += snprintf(&strbuf[sz], len-sz, "%s ", EncoderBase::toStr(decInst.mn));
+    if (decInst.argc > 0) {
+        sz += DisassembleOperandToBuf(decInst.operands[decInst.argc-1],
+                                 &strbuf[sz], len-sz);
+        for(k = decInst.argc-2; k >= 0; k--) {
+            sz += snprintf(&strbuf[sz], len-sz, ", ");
+            sz += DisassembleOperandToBuf(decInst.operands[k], &strbuf[sz], len-sz);
+        }
+    }
+}
+
+// Disassmble the x86 instruction pointed to by code pointer "stream."
+// Put the disassemble text in the "strbuf" up to string length "len".
+// Return the code pointer after the disassemble x86 instruction.
+extern "C" ENCODER_DECLARE_EXPORT
+char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len)
+{
+    Inst decInst;
+    unsigned numBytes = DecoderBase::decode(stream, &decInst);
+    DisassembleInstToBuf(decInst, strbuf, len);
+    return (stream + numBytes);
+}
diff --git a/vm/compiler/codegen/x86/libenc/enc_wrapper.h b/vm/compiler/codegen/x86/libenc/enc_wrapper.h
new file mode 100644
index 0000000..a2a223e
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/enc_wrapper.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef _VM_ENC_WRAPPER_H_
+#define _VM_ENC_WRAPPER_H_
+
+#include "enc_defs_ext.h"
+
+extern bool dump_x86_inst;
+typedef enum PhysicalReg {
+  PhysicalReg_EAX = 0, PhysicalReg_EBX, PhysicalReg_ECX, PhysicalReg_EDX,
+  PhysicalReg_EDI, PhysicalReg_ESI, PhysicalReg_ESP, PhysicalReg_EBP,
+  PhysicalReg_XMM0, PhysicalReg_XMM1, PhysicalReg_XMM2, PhysicalReg_XMM3,
+  PhysicalReg_XMM4, PhysicalReg_XMM5, PhysicalReg_XMM6, PhysicalReg_XMM7,
+  PhysicalReg_ST0,  PhysicalReg_ST1, PhysicalReg_ST2,  PhysicalReg_ST3,
+  PhysicalReg_ST4, PhysicalReg_ST5, PhysicalReg_ST6, PhysicalReg_ST7,
+  PhysicalReg_Null,
+  //used as scratch logical register in NCG O1
+  //should not overlap with regular logical register, start from 100
+  PhysicalReg_SCRATCH_1 = 100, PhysicalReg_SCRATCH_2, PhysicalReg_SCRATCH_3, PhysicalReg_SCRATCH_4,
+  PhysicalReg_SCRATCH_5, PhysicalReg_SCRATCH_6, PhysicalReg_SCRATCH_7, PhysicalReg_SCRATCH_8,
+  PhysicalReg_SCRATCH_9, PhysicalReg_SCRATCH_10,
+  PhysicalReg_GLUE_DVMDEX = 900,
+  PhysicalReg_GLUE = 901
+} PhysicalReg;
+
+typedef enum Reg_No {
+#ifdef _EM64T_
+    rax_reg = 0,rbx_reg,    rcx_reg,    rdx_reg,
+    rdi_reg,    rsi_reg,    rsp_reg,    rbp_reg,
+    r8_reg,     r9_reg,     r10_reg,    r11_reg,
+    r12_reg,    r13_reg,    r14_reg,    r15_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    xmm8_reg,   xmm9_reg,   xmm10_reg,  xmm11_reg,
+    xmm12_reg,  xmm13_reg,  xmm14_reg,  xmm15_reg,
+
+#else   // !defined(_EM64T_)
+
+    eax_reg = 0,ebx_reg,    ecx_reg,    edx_reg,
+    edi_reg,    esi_reg,    esp_reg,    ebp_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    fs_reg,
+#endif
+    /** @brief Total number of registers.*/
+    n_reg
+} Reg_No;
+//
+// instruction operand sizes: 8,16,32,64 bits
+//
+typedef enum Opnd_Size {
+    size_8 = 0,
+    size_16,
+    size_32,
+    size_64,
+    n_size,
+#ifdef _EM64T_
+    size_platf = size_64
+#else
+    size_platf = size_32
+#endif
+} Opnd_Size;
+
+//
+// opcodes for alu instructions
+//
+typedef enum ALU_Opcode {
+    add_opc = 0,or_opc,     adc_opc,    sbb_opc,
+    and_opc,    sub_opc,    xor_opc,    cmp_opc,
+    mul_opc,    imul_opc,   div_opc,    idiv_opc,
+    sll_opc,    srl_opc,    sra_opc, //shift right arithmetic
+    shl_opc,    shr_opc,
+    sal_opc,    sar_opc,
+    neg_opc,    not_opc,    andn_opc,
+    n_alu
+} ALU_Opcode;
+
+typedef enum ConditionCode {
+    Condition_O     = 0,
+    Condition_NO    = 1,
+    Condition_B     = 2,
+    Condition_NAE   = Condition_B,
+    Condition_C     = Condition_B,
+    Condition_NB    = 3,
+    Condition_AE    = Condition_NB,
+    Condition_NC    = Condition_NB,
+    Condition_Z     = 4,
+    Condition_E     = Condition_Z,
+    Condition_NZ    = 5,
+    Condition_NE    = Condition_NZ,
+    Condition_BE    = 6,
+    Condition_NA    = Condition_BE,
+    Condition_NBE   = 7,
+    Condition_A     = Condition_NBE,
+
+    Condition_S     = 8,
+    Condition_NS    = 9,
+    Condition_P     = 10,
+    Condition_PE    = Condition_P,
+    Condition_NP    = 11,
+    Condition_PO    = Condition_NP,
+    Condition_L     = 12,
+    Condition_NGE   = Condition_L,
+    Condition_NL    = 13,
+    Condition_GE    = Condition_NL,
+    Condition_LE    = 14,
+    Condition_NG    = Condition_LE,
+    Condition_NLE   = 15,
+    Condition_G     = Condition_NLE,
+    Condition_Count = 16
+} ConditionCode;
+
+//
+// prefix code
+//
+typedef enum InstrPrefix {
+    no_prefix,
+    lock_prefix                     = 0xF0,
+    hint_branch_taken_prefix        = 0x2E,
+    hint_branch_not_taken_prefix    = 0x3E,
+    prefix_repne                    = 0xF2,
+    prefix_repnz                    = prefix_repne,
+    prefix_repe                     = 0xF3,
+    prefix_repz                     = prefix_repe,
+    prefix_rep                      = 0xF3,
+    prefix_cs                       = 0x2E,
+    prefix_ss                       = 0x36,
+    prefix_ds                       = 0x3E,
+    prefix_es                       = 0x26,
+    prefix_fs                       = 0x64,
+    prefix_gs                       = 0x65
+} InstrPrefix;
+
+//last 2 bits: decide xmm, gp, fs
+//virtual, scratch, temp, hard match with ncg_o1_data.h
+typedef enum LowOpndRegType {
+  LowOpndRegType_gp = 0,
+  LowOpndRegType_fs = 1,
+  LowOpndRegType_xmm = 2,
+  LowOpndRegType_fs_s = 3,
+  LowOpndRegType_ss = 4,
+  LowOpndRegType_scratch = 8, //used by NCG O1
+  LowOpndRegType_temp = 16,
+  LowOpndRegType_hard = 32,   //NCG O1
+  LowOpndRegType_virtual = 64, //used by NCG O1
+  LowOpndRegType_glue = 128
+} LowOpndRegType;
+
+//if inline, separte enc_wrapper.cpp into two files, one of them is .inl
+//           enc_wrapper.cpp needs to handle both cases
+#ifdef ENCODER_INLINE
+    #define ENCODER_DECLARE_EXPORT inline
+    #include "enc_wrapper.inl"
+#else
+    #define ENCODER_DECLARE_EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ENCODER_DECLARE_EXPORT char* encoder_imm(Mnemonic m, OpndSize size,
+                  int imm, char* stream);
+ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_update_imm(int imm, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem(Mnemonic m, OpndSize size,
+               int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg(Mnemonic m, OpndSize size,
+               int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_reg(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int reg2, bool isPhysical2, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_reg(Mnemonic m, OpndSize size,
+                   int disp, int base_reg, bool isBasePhysical,
+                   int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         int reg, bool isPhysical, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size,
+                         int reg, bool isPhysical,
+                         int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale,
+                         LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_reg_mem(Mnemonic m, OpndSize size,
+                   int reg, bool isPhysical,
+                   int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_imm_reg(Mnemonic m, OpndSize size,
+                   int imm, int reg, bool isPhysical, LowOpndRegType type, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream);
+ENCODER_DECLARE_EXPORT char* encoder_imm_mem(Mnemonic m, OpndSize size,
+                   int imm,
+                   int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_fp_mem(Mnemonic m, OpndSize size, int reg,
+                  int disp, int base_reg, bool isBasePhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_mem_fp(Mnemonic m, OpndSize size,
+                  int disp, int base_reg, bool isBasePhysical,
+                  int reg, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_return(char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_movez_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char* stream);
+ENCODER_DECLARE_EXPORT char* encoder_moves_mem_to_reg(OpndSize size,
+                      int disp, int base_reg, bool isBasePhysical,
+                      int reg, bool isPhysical, char* stream);
+ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size,
+                      int reg, bool isPhysical, int reg2,
+                      bool isPhysical2, LowOpndRegType type, char * stream);
+ENCODER_DECLARE_EXPORT int decodeThenPrint(char* stream_start);
+ENCODER_DECLARE_EXPORT char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len);
+#ifdef __cplusplus
+}
+#endif
+#endif // _VM_ENC_WRAPPER_H_
diff --git a/vm/compiler/codegen/x86/libenc/encoder.cpp b/vm/compiler/codegen/x86/libenc/encoder.cpp
new file mode 100644
index 0000000..ef08a4d
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.cpp
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include <stdio.h>
+#include <assert.h>
+
+#include <limits.h>
+
+#include "enc_base.h"
+
+#ifdef NO_ENCODER_INLINE
+    #include "encoder.h"
+    #include "encoder.inl"
+#else
+    #define NO_ENCODER_INLINE
+    #include "encoder.h"
+    #undef NO_ENCODER_INLINE
+#endif
+
+
+
+#ifdef _EM64T_
+
+R_Opnd rax_opnd(rax_reg);
+R_Opnd rcx_opnd(rcx_reg);
+R_Opnd rdx_opnd(rdx_reg);
+R_Opnd rbx_opnd(rbx_reg);
+R_Opnd rsp_opnd(rsp_reg);
+R_Opnd rbp_opnd(rbp_reg);
+R_Opnd rsi_opnd(rsi_reg);
+R_Opnd rdi_opnd(rdi_reg);
+
+R_Opnd r8_opnd(r8_reg);
+R_Opnd r9_opnd(r9_reg);
+R_Opnd r10_opnd(r10_reg);
+R_Opnd r11_opnd(r11_reg);
+R_Opnd r12_opnd(r12_reg);
+R_Opnd r13_opnd(r13_reg);
+R_Opnd r14_opnd(r14_reg);
+R_Opnd r15_opnd(r15_reg);
+
+XMM_Opnd xmm8_opnd(xmm8_reg);
+XMM_Opnd xmm9_opnd(xmm9_reg);
+XMM_Opnd xmm10_opnd(xmm10_reg);
+XMM_Opnd xmm11_opnd(xmm11_reg);
+XMM_Opnd xmm12_opnd(xmm12_reg);
+XMM_Opnd xmm13_opnd(xmm13_reg);
+XMM_Opnd xmm14_opnd(xmm14_reg);
+XMM_Opnd xmm15_opnd(xmm15_reg);
+
+#else
+
+R_Opnd eax_opnd(eax_reg);
+R_Opnd ecx_opnd(ecx_reg);
+R_Opnd edx_opnd(edx_reg);
+R_Opnd ebx_opnd(ebx_reg);
+R_Opnd esp_opnd(esp_reg);
+R_Opnd ebp_opnd(ebp_reg);
+R_Opnd esi_opnd(esi_reg);
+R_Opnd edi_opnd(edi_reg);
+
+#endif //_EM64T_
+
+XMM_Opnd xmm0_opnd(xmm0_reg);
+XMM_Opnd xmm1_opnd(xmm1_reg);
+XMM_Opnd xmm2_opnd(xmm2_reg);
+XMM_Opnd xmm3_opnd(xmm3_reg);
+XMM_Opnd xmm4_opnd(xmm4_reg);
+XMM_Opnd xmm5_opnd(xmm5_reg);
+XMM_Opnd xmm6_opnd(xmm6_reg);
+XMM_Opnd xmm7_opnd(xmm7_reg);
+
+
+#define countof(a)      (sizeof(a)/sizeof(a[0]))
+
+extern const RegName map_of_regno_2_regname[];
+extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[];
+extern const Mnemonic map_of_alu_opcode_2_mnemonic[];
+extern const Mnemonic map_of_shift_opcode_2_mnemonic[];
+
+const RegName map_of_regno_2_regname [] = {
+#ifdef _EM64T_
+    RegName_RAX,    RegName_RBX,    RegName_RCX,    RegName_RDX,
+    RegName_RDI,    RegName_RSI,    RegName_RSP,    RegName_RBP,
+    RegName_R8,     RegName_R9,     RegName_R10,    RegName_R11,
+    RegName_R12,    RegName_R13,    RegName_R14,    RegName_R15,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_XMM8,   RegName_XMM9,   RegName_XMM10,  RegName_XMM11,
+    RegName_XMM12,  RegName_XMM13,   RegName_XMM14, RegName_XMM15,
+
+#else
+    RegName_EAX,    RegName_EBX,    RegName_ECX,    RegName_EDX,
+    RegName_EDI,    RegName_ESI,    RegName_ESP,    RegName_EBP,
+    RegName_XMM0,   RegName_XMM1,   RegName_XMM2,   RegName_XMM3,
+    RegName_XMM4,   RegName_XMM5,   RegName_XMM6,   RegName_XMM7,
+    RegName_FS,
+#endif  // _EM64T_
+
+    RegName_Null,
+};
+
+const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[] = {
+    OpndSize_8, OpndSize_16, OpndSize_32, OpndSize_64, OpndSize_Any
+};
+
+const Mnemonic map_of_alu_opcode_2_mnemonic[] = {
+    //add_opc=0,  or_opc,           adc_opc,        sbb_opc,
+    //and_opc,      sub_opc,        xor_opc,        cmp_opc,
+    //n_alu
+    Mnemonic_ADD,   Mnemonic_OR,    Mnemonic_ADC,   Mnemonic_SBB,
+    Mnemonic_AND,   Mnemonic_SUB,   Mnemonic_XOR,   Mnemonic_CMP,
+};
+
+const Mnemonic map_of_shift_opcode_2_mnemonic[] = {
+    //shld_opc, shrd_opc,
+    // shl_opc, shr_opc, sar_opc, ror_opc, max_shift_opcode=6,
+    // n_shift = 6
+    Mnemonic_SHLD,  Mnemonic_SHRD,
+    Mnemonic_SHL,   Mnemonic_SHR,   Mnemonic_SAR, Mnemonic_ROR
+};
+
+#ifdef _DEBUG
+
+static int debug_check() {
+    // Checks some assumptions.
+
+    // 1. all items of Encoder.h:enum Reg_No  must be mapped plus n_reg->RegName_Null
+    assert(countof(map_of_regno_2_regname) == n_reg + 1);
+    assert(countof(map_of_alu_opcode_2_mnemonic) == n_alu);
+    assert(countof(map_of_shift_opcode_2_mnemonic) == n_shift);
+    return 0;
+}
+
+static int dummy = debug_check();
+
+// can have this - initialization order problems.... static int dummy_run_the_debug_test = debug_check();
+
+#endif
diff --git a/vm/compiler/codegen/x86/libenc/encoder.h b/vm/compiler/codegen/x86/libenc/encoder.h
new file mode 100644
index 0000000..e29efce
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.h
@@ -0,0 +1,716 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+/**
+ * @file
+ * @brief Simple interface for generating processor instructions.
+ *
+ * The interface works for both IA32 and EM64T. By default, only IA32
+ * capabilities are presented. To enable EM64T feature, the _EM64T_ macro
+ * must be defined (and, of course, a proper library version to be used).
+ *
+ * The interface is based on the original ia32.h encoder interface,
+ * with some simplifications and add-ons - EM64T-specific, SSE and SSE2.
+ *
+ * The interface mostly intended for existing legacy code like LIL code
+ * generator. From the implementation point of view, it's just a wrapper
+ * around the EncoderBase functionality.
+ */
+
+#ifndef _VM_ENCODER_H_
+#define _VM_ENCODER_H_
+
+#include <limits.h>
+#include "enc_base.h"
+//#include "open/types.h"
+
+#ifdef _EM64T_
+// size of general-purpose value on the stack in bytes
+#define GR_STACK_SIZE 8
+// size of floating-point value on the stack in bytes
+#define FR_STACK_SIZE 8
+
+#if defined(WIN32) || defined(_WIN64)
+    // maximum number of GP registers for inputs
+    const int MAX_GR = 4;
+    // maximum number of FP registers for inputs
+    const int MAX_FR = 4;
+    // WIN64 reserves 4 words for shadow space
+    const int SHADOW = 4 * GR_STACK_SIZE;
+#else
+    // maximum number of GP registers for inputs
+    const int MAX_GR = 6;
+    // maximum number of FP registers for inputs
+    const int MAX_FR = 8;
+    // Linux x64 doesn't reserve shadow space
+    const int SHADOW = 0;
+#endif
+
+#else
+// size of general-purpose value on the stack in bytes
+#define GR_STACK_SIZE 4
+// size of general-purpose value on the stack in bytes
+#define FR_STACK_SIZE 8
+
+// maximum number of GP registers for inputs
+const int MAX_GR = 0;
+// maximum number of FP registers for inputs
+const int MAX_FR = 0;
+#endif
+
+typedef enum Reg_No {
+#ifdef _EM64T_
+    rax_reg = 0,rbx_reg,    rcx_reg,    rdx_reg,
+    rdi_reg,    rsi_reg,    rsp_reg,    rbp_reg,
+    r8_reg,     r9_reg,     r10_reg,    r11_reg,
+    r12_reg,    r13_reg,    r14_reg,    r15_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    xmm8_reg,   xmm9_reg,   xmm10_reg,  xmm11_reg,
+    xmm12_reg,  xmm13_reg,  xmm14_reg,  xmm15_reg,
+
+#else   // !defined(_EM64T_)
+
+    eax_reg = 0,ebx_reg,    ecx_reg,    edx_reg,
+    edi_reg,    esi_reg,    esp_reg,    ebp_reg,
+    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
+    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
+    fs_reg,
+#endif
+    /** @brief Total number of registers.*/
+    n_reg
+} Reg_No;
+//
+// instruction operand sizes: 8,16,32,64 bits
+//
+typedef enum Opnd_Size {
+    size_8 = 0,
+    size_16,
+    size_32,
+    size_64,
+    n_size,
+#ifdef _EM64T_
+    size_platf = size_64
+#else
+    size_platf = size_32
+#endif
+} Opnd_Size;
+
+//
+// opcodes for alu instructions
+//
+typedef enum ALU_Opcode {
+    add_opc = 0,or_opc,     adc_opc,    sbb_opc,
+    and_opc,    sub_opc,    xor_opc,    cmp_opc,
+    n_alu
+} ALU_Opcode;
+
+//
+// opcodes for shift instructions
+//
+typedef enum Shift_Opcode {
+    shld_opc,   shrd_opc,   shl_opc,    shr_opc,
+    sar_opc,    ror_opc, max_shift_opcode=6,     n_shift = 6
+} Shift_Opcode;
+
+typedef enum ConditionCode {
+    Condition_O     = 0,
+    Condition_NO    = 1,
+    Condition_B     = 2,
+    Condition_NAE   = Condition_B,
+    Condition_C     = Condition_B,
+    Condition_NB    = 3,
+    Condition_AE    = Condition_NB,
+    Condition_NC    = Condition_NB,
+    Condition_Z     = 4,
+    Condition_E     = Condition_Z,
+    Condition_NZ    = 5,
+    Condition_NE    = Condition_NZ,
+    Condition_BE    = 6,
+    Condition_NA    = Condition_BE,
+    Condition_NBE   = 7,
+    Condition_A     = Condition_NBE,
+
+    Condition_S     = 8,
+    Condition_NS    = 9,
+    Condition_P     = 10,
+    Condition_PE    = Condition_P,
+    Condition_NP    = 11,
+    Condition_PO    = Condition_NP,
+    Condition_L     = 12,
+    Condition_NGE   = Condition_L,
+    Condition_NL    = 13,
+    Condition_GE    = Condition_NL,
+    Condition_LE    = 14,
+    Condition_NG    = Condition_LE,
+    Condition_NLE   = 15,
+    Condition_G     = Condition_NLE,
+    Condition_Count = 16
+} ConditionCode;
+
+//
+// prefix code
+//
+typedef enum InstrPrefix {
+    no_prefix,
+    lock_prefix                     = 0xF0,
+    hint_branch_taken_prefix        = 0x2E,
+    hint_branch_not_taken_prefix    = 0x3E,
+    prefix_repne                    = 0xF2,
+    prefix_repnz                    = prefix_repne,
+    prefix_repe                     = 0xF3,
+    prefix_repz                     = prefix_repe,
+    prefix_rep                      = 0xF3,
+    prefix_cs                       = 0x2E,
+    prefix_ss                       = 0x36,
+    prefix_ds                       = 0x3E,
+    prefix_es                       = 0x26,
+    prefix_fs                       = 0x64,
+    prefix_gs                       = 0x65
+} InstrPrefix;
+
+
+//
+// an instruction operand
+//
+class Opnd {
+
+protected:
+    enum Tag { SignedImm, UnsignedImm, Reg, Mem, FP, XMM };
+
+    const Tag  tag;
+
+    Opnd(Tag t): tag(t) {}
+
+public:
+    void * operator new(size_t, void * mem) {
+        return mem;
+    }
+
+    void operator delete(void *) {}
+
+    void operator delete(void *, void *) {}
+
+private:
+    // disallow copying
+    Opnd(const Opnd &): tag(Mem) { assert(false); }
+    Opnd& operator=(const Opnd &) { assert(false); return *this; }
+};
+typedef int I_32;
+class Imm_Opnd: public Opnd {
+
+protected:
+    union {
+#ifdef _EM64T_
+        int64           value;
+        unsigned char   bytes[8];
+#else
+        I_32           value;
+        unsigned char   bytes[4];
+#endif
+    };
+    Opnd_Size           size;
+
+public:
+    Imm_Opnd(I_32 val, bool isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(size_32) {
+        if (isSigned) {
+            if (CHAR_MIN <= val && val <= CHAR_MAX) {
+                size = size_8;
+            } else if (SHRT_MIN <= val && val <= SHRT_MAX) {
+                size = size_16;
+            }
+        } else {
+            assert(val >= 0);
+            if (val <= UCHAR_MAX) {
+                size = size_8;
+            } else if (val <= USHRT_MAX) {
+                size = size_16;
+            }
+        }
+    }
+    Imm_Opnd(const Imm_Opnd& that): Opnd(that.tag), value(that.value), size(that.size) {};
+
+#ifdef _EM64T_
+    Imm_Opnd(Opnd_Size sz, int64 val, bool isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
+#ifndef NDEBUG
+        switch (size) {
+        case size_8:
+            assert(val == (int64)(I_8)val);
+            break;
+        case size_16:
+            assert(val == (int64)(int16)val);
+            break;
+        case size_32:
+            assert(val == (int64)(I_32)val);
+            break;
+        case size_64:
+            break;
+        case n_size:
+            assert(false);
+            break;
+        }
+#endif // NDEBUG
+    }
+
+    int64 get_value() const { return value; }
+
+#else
+
+    Imm_Opnd(Opnd_Size sz, I_32 val, int isSigned = true):
+        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
+#ifndef NDEBUG
+        switch (size) {
+        case size_8:
+            assert((I_32)val == (I_32)(I_8)val);
+            break;
+        case size_16:
+            assert((I_32)val == (I_32)(int16)val);
+            break;
+        case size_32:
+            break;
+        case size_64:
+        case n_size:
+            assert(false);
+            break;
+        }
+#endif // NDEBUG
+    }
+
+    I_32 get_value() const { return value; }
+
+#endif
+    Opnd_Size get_size() const { return size; }
+    bool      is_signed() const { return tag == SignedImm; }
+};
+
+class RM_Opnd: public Opnd {
+
+public:
+    bool is_reg() const { return tag != SignedImm && tag != UnsignedImm && tag != Mem; }
+
+protected:
+    RM_Opnd(Tag t): Opnd(t) {}
+
+private:
+    // disallow copying
+    RM_Opnd(const RM_Opnd &): Opnd(Reg) { assert(false); }
+};
+
+class R_Opnd: public RM_Opnd {
+
+protected:
+    Reg_No      _reg_no;
+
+public:
+    R_Opnd(Reg_No r): RM_Opnd(Reg), _reg_no(r) {}
+    Reg_No  reg_no() const { return _reg_no; }
+
+private:
+    // disallow copying
+    R_Opnd(const R_Opnd &): RM_Opnd(Reg) { assert(false); }
+};
+
+//
+// a memory operand with displacement
+// Can also serve as a full memory operand with base,index, displacement and scale.
+// Use n_reg to specify 'no register', say, for index.
+class M_Opnd: public RM_Opnd {
+
+protected:
+    Imm_Opnd        m_disp;
+    Imm_Opnd        m_scale;
+    R_Opnd          m_index;
+    R_Opnd          m_base;
+
+public:
+    //M_Opnd(Opnd_Size sz): RM_Opnd(Mem, K_M, sz), m_disp(0), m_scale(0), m_index(n_reg), m_base(n_reg) {}
+    M_Opnd(I_32 disp):
+        RM_Opnd(Mem), m_disp(disp), m_scale(0), m_index(n_reg), m_base(n_reg) {}
+    M_Opnd(Reg_No rbase, I_32 rdisp):
+        RM_Opnd(Mem), m_disp(rdisp), m_scale(0), m_index(n_reg), m_base(rbase) {}
+    M_Opnd(I_32 disp, Reg_No rbase, Reg_No rindex, unsigned scale):
+        RM_Opnd(Mem), m_disp(disp), m_scale(scale), m_index(rindex), m_base(rbase) {}
+    M_Opnd(const M_Opnd & that) : RM_Opnd(Mem),
+        m_disp((int)that.m_disp.get_value()), m_scale((int)that.m_scale.get_value()),
+        m_index(that.m_index.reg_no()), m_base(that.m_base.reg_no())
+        {}
+    //
+    inline const R_Opnd & base(void) const { return m_base; }
+    inline const R_Opnd & index(void) const { return m_index; }
+    inline const Imm_Opnd & scale(void) const { return m_scale; }
+    inline const Imm_Opnd & disp(void) const { return m_disp; }
+};
+
+//
+//  a memory operand with base register and displacement
+//
+class M_Base_Opnd: public M_Opnd {
+
+public:
+    M_Base_Opnd(Reg_No base, I_32 disp) : M_Opnd(disp, base, n_reg, 0) {}
+
+private:
+    // disallow copying - but it leads to ICC errors #734 in encoder.inl
+    // M_Base_Opnd(const M_Base_Opnd &): M_Opnd(0) { assert(false); }
+};
+
+//
+//  a memory operand with base register, scaled index register
+//  and displacement.
+//
+class M_Index_Opnd : public M_Opnd {
+
+public:
+    M_Index_Opnd(Reg_No base, Reg_No index, I_32 disp, unsigned scale):
+        M_Opnd(disp, base, index, scale) {}
+
+private:
+    // disallow copying - but it leads to ICC errors #734 in encoder.inl
+    // M_Index_Opnd(const M_Index_Opnd &): M_Opnd(0) { assert(false); }
+};
+
+class XMM_Opnd : public Opnd {
+
+protected:
+    unsigned        m_idx;
+
+public:
+    XMM_Opnd(unsigned _idx): Opnd(XMM), m_idx(_idx) {};
+    unsigned get_idx( void ) const { return m_idx; };
+
+private:
+    // disallow copying
+    XMM_Opnd(const XMM_Opnd &): Opnd(XMM) { assert(false); }
+};
+
+//
+// operand structures for ia32 registers
+//
+#ifdef _EM64T_
+
+extern R_Opnd rax_opnd;
+extern R_Opnd rcx_opnd;
+extern R_Opnd rdx_opnd;
+extern R_Opnd rbx_opnd;
+extern R_Opnd rdi_opnd;
+extern R_Opnd rsi_opnd;
+extern R_Opnd rsp_opnd;
+extern R_Opnd rbp_opnd;
+
+extern R_Opnd r8_opnd;
+extern R_Opnd r9_opnd;
+extern R_Opnd r10_opnd;
+extern R_Opnd r11_opnd;
+extern R_Opnd r12_opnd;
+extern R_Opnd r13_opnd;
+extern R_Opnd r14_opnd;
+extern R_Opnd r15_opnd;
+
+extern XMM_Opnd xmm8_opnd;
+extern XMM_Opnd xmm9_opnd;
+extern XMM_Opnd xmm10_opnd;
+extern XMM_Opnd xmm11_opnd;
+extern XMM_Opnd xmm12_opnd;
+extern XMM_Opnd xmm13_opnd;
+extern XMM_Opnd xmm14_opnd;
+extern XMM_Opnd xmm15_opnd;
+#else
+
+extern R_Opnd eax_opnd;
+extern R_Opnd ecx_opnd;
+extern R_Opnd edx_opnd;
+extern R_Opnd ebx_opnd;
+extern R_Opnd esp_opnd;
+extern R_Opnd ebp_opnd;
+extern R_Opnd esi_opnd;
+extern R_Opnd edi_opnd;
+
+#endif // _EM64T_
+
+extern XMM_Opnd xmm0_opnd;
+extern XMM_Opnd xmm1_opnd;
+extern XMM_Opnd xmm2_opnd;
+extern XMM_Opnd xmm3_opnd;
+extern XMM_Opnd xmm4_opnd;
+extern XMM_Opnd xmm5_opnd;
+extern XMM_Opnd xmm6_opnd;
+extern XMM_Opnd xmm7_opnd;
+
+#ifdef NO_ENCODER_INLINE
+    #define ENCODER_DECLARE_EXPORT
+#else
+    #define ENCODER_DECLARE_EXPORT inline
+    #include "encoder.inl"
+#endif
+
+// prefix
+ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p);
+
+// stack push and pop instructions
+ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm);
+ENCODER_DECLARE_EXPORT char * pop(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// cmpxchg or xchg
+ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// inc(rement), dec(rement), not, neg(ate) instructions
+ENCODER_DECLARE_EXPORT char * inc(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * dec(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * neg(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * nop(char * stream);
+ENCODER_DECLARE_EXPORT char * int3(char * stream);
+
+// alu instructions: add, or, adc, sbb, and, sub, xor, cmp
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// test instruction
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// shift instructions: shl, shr, sar, shld, shrd, ror
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
+
+// multiply instructions: mul, imul
+ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, const Imm_Opnd& imm, Opnd_Size sz = size_platf);
+
+// divide instructions: div, idiv
+ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// data movement: mov
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m,  const R_Opnd & r, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
+
+ENCODER_DECLARE_EXPORT char * movsx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * movzx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);
+
+// sse mov
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd & mem, const XMM_Opnd & xmm, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+// sse add, sub, mul, div
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+
+// xor, compare
+ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl);
+
+// sse conversions
+ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64);
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32);
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
+
+// condition operations
+ENCODER_DECLARE_EXPORT char * cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8);
+
+// load effective address: lea
+ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz = size_platf);
+ENCODER_DECLARE_EXPORT char * cdq(char * stream);
+ENCODER_DECLARE_EXPORT char * wait(char * stream);
+
+// control-flow instructions
+ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm);
+
+// jump with 8-bit relative
+ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm);
+
+// jump with 32-bit relative
+ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm);
+
+// register indirect jump
+ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// jump to target address
+ENCODER_DECLARE_EXPORT char *jump(char * stream, char *target);
+
+// jump with displacement
+//char * jump(char * stream, I_32 disp);
+
+// conditional branch with 8-bit branch offset
+ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);
+
+// conditional branch with 32-bit branch offset
+ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);
+
+// conditional branch with target label address
+//char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix = no_prefix);
+
+// conditional branch with displacement immediate
+ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, I_32 disp, InstrPrefix prefix = no_prefix);
+
+// call with displacement
+ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm);
+
+// indirect call through register or memory location
+ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
+
+// call target address
+ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target);
+
+// return instruction
+ENCODER_DECLARE_EXPORT char * ret(char * stream);
+ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop);
+ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm);
+
+// string operations
+ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set);
+ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix);
+ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix);
+
+// floating-point instructions
+
+// st(0) = st(0) fp_op m{32,64}real
+//!char * fp_op_mem(char * stream, FP_Opcode opc,const M_Opnd& mem,int is_double);
+
+// st(0) = st(0) fp_op st(i)
+//!char *fp_op(char * stream, FP_Opcode opc,unsigned i);
+
+// st(i) = st(i) fp_op st(0)    ; optionally pop stack
+//!char * fp_op(char * stream, FP_Opcode opc,unsigned i,unsigned pop_stk);
+
+// compare st(0),st(1) and pop stack twice
+//!char * fcompp(char * stream);
+ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem);
+ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem);
+ENCODER_DECLARE_EXPORT char * fnstsw(char * stream);
+//!char * fchs(char * stream);
+//!char * frem(char * stream);
+//!char * fxch(char * stream,unsigned i);
+//!char * fcomip(char * stream, unsigned i);
+
+// load from memory (as fp) into fp register stack
+ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m, bool is_double);
+//!char *fld80(char * stream,const M_Opnd& mem);
+
+// load from memory (as int) into fp register stack
+//!char * fild(char * stream,const M_Opnd& mem,int is_long);
+
+// push st(i) onto fp register stack
+//!char * fld(char * stream,unsigned i);
+
+// push the constants 0.0 and 1.0 onto the fp register stack
+//!char * fldz(char * stream);
+//!char * fld1(char * stream);
+
+// store stack to memory (as int), always popping the stack
+ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem, bool is_long, bool pop_stk);
+// store stack to to memory (as fp), optionally popping the stack
+ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m, bool is_double, bool pop_stk);
+// store ST(0) to ST(i), optionally popping the stack. Takes 1 clock
+ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk);
+
+//!char * pushad(char * stream);
+//!char * pushfd(char * stream);
+//!char * popad(char * stream);
+//!char * popfd(char * stream);
+
+// stack frame allocation instructions: enter & leave
+//
+//    enter frame_size
+//
+//    is equivalent to:
+//
+//    push    ebp
+//    mov     ebp,esp
+//    sub     esp,frame_size
+//
+//!char *enter(char * stream,const Imm_Opnd& imm);
+
+// leave
+// is equivalent to:
+//
+// mov        esp,ebp
+// pop        ebp
+//!char *leave(char * stream);
+
+// sahf  loads SF, ZF, AF, PF, and CF flags from eax
+//!char *sahf(char * stream);
+
+// Intrinsic FP math functions
+
+//!char *math_fsin(char * stream);
+//!char *math_fcos(char * stream);
+//!char *math_fabs(char * stream);
+//!char *math_fpatan(char * stream);
+ENCODER_DECLARE_EXPORT char * fprem(char * stream);
+ENCODER_DECLARE_EXPORT char * fprem1(char * stream);
+//!char *math_frndint(char * stream);
+//!char *math_fptan(char * stream);
+
+//
+// Add 1-7 bytes padding, with as few instructions as possible,
+// with no effect on the processor state (e.g., registers, flags)
+//
+//!char *padding(char * stream, unsigned num);
+
+// prolog and epilog code generation
+//- char *prolog(char * stream,unsigned frame_size,unsigned reg_save_mask);
+//- char *epilog(char * stream,unsigned reg_save_mask);
+
+//!extern R_Opnd reg_operand_array[];
+
+// fsave and frstor
+//!char *fsave(char * stream);
+//!char *frstor(char * stream);
+
+// lahf : Load Status Flags into AH Register
+//!char *lahf(char * stream);
+
+// mfence : Memory Fence
+//!char *mfence(char * stream);
+
+#endif // _VM_ENCODER_H_
diff --git a/vm/compiler/codegen/x86/libenc/encoder.inl b/vm/compiler/codegen/x86/libenc/encoder.inl
new file mode 100644
index 0000000..663d492
--- /dev/null
+++ b/vm/compiler/codegen/x86/libenc/encoder.inl
@@ -0,0 +1,850 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ */
+/**
+ * @author Alexander V. Astapchuk
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+extern const RegName map_of_regno_2_regname[];
+extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[];
+extern const Mnemonic map_of_alu_opcode_2_mnemonic[];
+extern const Mnemonic map_of_shift_opcode_2_mnemonic[];
+
+// S_ stands for 'Signed'
+extern const Mnemonic S_map_of_condition_code_2_branch_mnemonic[];
+// U_ stands for 'Unsigned'
+extern const Mnemonic U_map_of_condition_code_2_branch_mnemonic[];
+
+inline static RegName map_reg(Reg_No r) {
+    assert(r >= 0 && r <= n_reg);
+    return map_of_regno_2_regname[r];
+}
+
+inline static OpndSize map_size(Opnd_Size o_size) {
+    assert(o_size >= 0 && o_size <= n_size);
+    return map_of_EncoderOpndSize_2_RealOpndSize[o_size];
+}
+
+inline static Mnemonic map_alu(ALU_Opcode alu) {
+    assert(alu >= 0 && alu < n_alu);
+    return map_of_alu_opcode_2_mnemonic[alu];
+}
+
+inline static Mnemonic map_shift(Shift_Opcode shc) {
+    assert(shc >= 0 && shc < n_shift);
+    return map_of_shift_opcode_2_mnemonic[shc];
+}
+
+inline bool fit8(int64 val) {
+    return (CHAR_MIN <= val) && (val <= CHAR_MAX);
+}
+
+inline bool fit32(int64 val) {
+    return (INT_MIN <= val) && (val <= INT_MAX);
+}
+
+inline static void add_r(EncoderBase::Operands & args, const R_Opnd & r, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+    RegName reg = map_reg(r.reg_no());
+    if (sz != n_size) {
+        OpndSize size = map_size(sz);
+        if (size != getRegSize(reg)) {
+            reg = getAliasReg(reg, size);
+        }
+    }
+    args.add(EncoderBase::Operand(reg, ext));
+}
+
+inline static void add_m(EncoderBase::Operands & args, const M_Opnd & m, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+        assert(n_size != sz);
+        args.add(EncoderBase::Operand(map_size(sz),
+            map_reg(m.base().reg_no()), map_reg(m.index().reg_no()),
+            (unsigned)m.scale().get_value(), (int)m.disp().get_value(), ext));
+}
+
+inline static void add_rm(EncoderBase::Operands & args, const RM_Opnd & rm, Opnd_Size sz, OpndExt ext = OpndExt_None) {
+    rm.is_reg() ? add_r(args, (R_Opnd &)rm, sz, ext) : add_m(args, (M_Opnd &)rm, sz, ext);
+}
+
+inline static void add_xmm(EncoderBase::Operands & args, const XMM_Opnd & xmm, bool dbl) {
+    // Gregory -
+    // XMM registers indexes in Reg_No enum are shifted by xmm0_reg, their indexes
+    // don't start with 0, so it is necessary to subtract xmm0_reg index from
+    // xmm.get_idx() value
+    assert(xmm.get_idx() >= xmm0_reg);
+    return args.add((RegName)( (dbl ? RegName_XMM0D : RegName_XMM0S) + xmm.get_idx() -
+            xmm0_reg));
+}
+
+inline static void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) {
+    return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i));
+}
+
+inline static void add_imm(EncoderBase::Operands & args, const Imm_Opnd & imm) {
+    assert(n_size != imm.get_size());
+    args.add(EncoderBase::Operand(map_size(imm.get_size()), imm.get_value(),
+        imm.is_signed() ? OpndExt_Signed : OpndExt_Zero));
+}
+
+ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p) {
+    *stream = (char)p;
+    return stream + 1;
+}
+
+// stack push and pop instructions
+ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_PUSH, args);
+}
+
+ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+#ifdef _EM64T_
+    add_imm(args, imm);
+#else
+    // we need this workaround to be compatible with the former ia32 encoder implementation
+    add_imm(args, Imm_Opnd(size_32, imm.get_value()));
+#endif
+    return EncoderBase::encode(stream, Mnemonic_PUSH, args);
+}
+
+ENCODER_DECLARE_EXPORT char * pop(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_POP, args);
+}
+
+// cmpxchg or xchg
+ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    RegName implicitReg = getAliasReg(RegName_EAX, map_size(sz));
+    args.add(implicitReg);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CMPXCHG, args);
+}
+
+ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_XCHG, args);
+}
+
+// inc(rement), dec(rement), not, neg(ate) instructions
+ENCODER_DECLARE_EXPORT char * inc(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_INC, args);
+}
+
+ENCODER_DECLARE_EXPORT char * dec(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_DEC, args);
+}
+
+ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_NOT, args);
+}
+
+ENCODER_DECLARE_EXPORT char * neg(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_NEG, args);
+}
+
+ENCODER_DECLARE_EXPORT char * nop(char * stream) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_NOP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * int3(char * stream) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_INT3, args);
+}
+
+// alu instructions: add, or, adc, sbb, and, sub, xor, cmp
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+};
+
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, m, sz);
+    add_rm(args, r, sz);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, map_alu(opc), args);
+}
+
+// test instruction
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    assert(imm.get_size() <= sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args);
+}
+
+ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args);
+}
+
+// shift instructions: shl, shr, sar, shld, shrd
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    args.add(RegName_CL);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm,
+                            const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    assert(shc == shld_opc || shc == shrd_opc);
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm,
+                            const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    assert(shc == shld_opc || shc == shrd_opc);
+    add_rm(args, rm, sz);
+    add_r(args, r, sz);
+    args.add(RegName_CL);
+    return (char*)EncoderBase::encode(stream, map_shift(shc), args);
+}
+
+// multiply instructions: mul, imul
+ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    args.add(RegName_EDX);
+    args.add(RegName_EAX);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm,
+                           const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args);
+}
+
+// divide instructions: div, idiv
+ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+#ifdef _EM64T_
+    add_r(args, rdx_opnd, sz);
+    add_r(args, rax_opnd, sz);
+#else
+    add_r(args, edx_opnd, sz);
+    add_r(args, eax_opnd, sz);
+#endif
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_IDIV, args);
+}
+
+// data movement: mov
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_m(args, m, sz);
+    add_r(args, r, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, size_32);
+    add_xmm(args, xmm, false);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, false);
+    add_rm(args, rm, size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, size_64);
+    add_xmm(args, xmm, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, true);
+    add_rm(args, rm, size_64);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movsx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, n_size);
+    add_rm(args, rm, sz, OpndExt_Signed);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVSX, args);
+}
+
+ENCODER_DECLARE_EXPORT char * movzx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, n_size);
+    // movzx r64, r/m32 is not available on em64t
+    // mov r32, r/m32 should zero out upper bytes
+    assert(sz <= size_16);
+    add_rm(args, rm, sz, OpndExt_Zero);
+    return (char*)EncoderBase::encode(stream, Mnemonic_MOVZX, args);
+}
+
+// sse mov
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd &  mem, const XMM_Opnd & xmm, bool dbl) {
+    EncoderBase::Operands args;
+    add_m(args, mem, dbl ? size_64 : size_32);
+    add_xmm(args, xmm, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args );
+}
+
+// sse add, sub, mul, div
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mul( char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd& xmm0, const XMM_Opnd& xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args,  xmm0, dbl);
+    add_xmm(args,  xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_PXOR, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args);
+}
+
+// sse conversions
+ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm, dbl);
+    add_m(args, mem, size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTSI2SD : Mnemonic_CVTSI2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl) {
+    EncoderBase::Operands args;
+    add_rm(args, reg, size_32);
+    add_m(args, mem, dbl ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl) {
+    EncoderBase::Operands args;
+    add_rm(args, reg, size_32);
+    add_xmm(args, xmm, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ?  Mnemonic_CVTTPD2DQ : Mnemonic_CVTTPS2DQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, dbl);
+    add_xmm(args, xmm1, dbl);
+    return (char*)EncoderBase::encode(stream, dbl ?  Mnemonic_CVTDQ2PD : Mnemonic_CVTDQ2PS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, false);
+    add_m(args, mem64, size_64);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, false);
+    add_xmm(args, xmm1, true);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_m(args, mem32, size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) {
+    EncoderBase::Operands args;
+    add_xmm(args, xmm0, true);
+    add_xmm(args, xmm1, false);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args);
+}
+
+// condition operations
+ENCODER_DECLARE_EXPORT char *cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_CMOVcc + cc), args);
+}
+
+ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8) {
+    EncoderBase::Operands args;
+    add_rm(args, rm8, size_8);
+    return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_SETcc + cc), args);
+}
+
+// load effective address: lea
+ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_r(args, r, sz);
+    add_m(args, m, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_LEA, args);
+}
+
+ENCODER_DECLARE_EXPORT char * cdq(char * stream) {
+    EncoderBase::Operands args;
+    args.add(RegName_EDX);
+    args.add(RegName_EAX);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CDQ, args);
+}
+
+ENCODER_DECLARE_EXPORT char * wait(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_WAIT, EncoderBase::Operands());
+}
+
+// control-flow instructions
+
+// loop
+ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    args.add(RegName_ECX);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_LOOP, args);
+}
+
+// jump
+ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm) {
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_32);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz) {
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args);
+}
+
+/**
+ * @note On EM64T: if target lies beyond 2G (does not fit into 32 bit
+ *       offset) then generates indirect jump using RAX (whose content is
+ *       destroyed).
+ */
+ENCODER_DECLARE_EXPORT char * jump(char * stream, char * target) {
+#ifdef _EM64T_
+    int64 offset = target - stream;
+    // sub 2 bytes for the short version
+    offset -= 2;
+    if (fit8(offset)) {
+        // use 8-bit signed relative form
+        return jump8(stream, Imm_Opnd(size_8, offset));
+    } else if (fit32(offset)) {
+        // sub 5 (3 + 2)bytes for the long version
+        offset -= 3;
+        // use 32-bit signed relative form
+        return jump32(stream, Imm_Opnd(size_32, offset));
+    }
+    // need to use absolute indirect jump
+    stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64);
+    return jump(stream, rax_opnd, size_64);
+#else
+    I_32 offset = target - stream;
+    // sub 2 bytes for the short version
+    offset -= 2;
+    if (fit8(offset)) {
+        // use 8-bit signed relative form
+        return jump8(stream, Imm_Opnd(size_8, offset));
+    }
+    // sub 5 (3 + 2) bytes for the long version
+    offset -= 3;
+    // use 32-bit signed relative form
+    return jump32(stream, Imm_Opnd(size_32, offset));
+#endif
+}
+
+// branch
+ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cond,
+                                      const Imm_Opnd & imm,
+                                      InstrPrefix pref)
+{
+    if (pref != no_prefix) {
+        assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix);
+        stream = prefix(stream, pref);
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond);
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_8);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, m, args);
+}
+
+ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cond,
+                                       const Imm_Opnd & imm,
+                                       InstrPrefix pref)
+{
+    if (pref != no_prefix) {
+        assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix);
+        stream = prefix(stream, pref);
+    }
+    Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond);
+    EncoderBase::Operands args;
+    assert(imm.get_size() == size_32);
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, m, args);
+}
+
+/*
+ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix) {
+// sub 2 bytes for the short version
+int64 offset = stream-target-2;
+if( fit8(offset) ) {
+return branch8(stream, cc, Imm_Opnd(size_8, (char)offset), is_signed);
+}
+return branch32(stream, cc, Imm_Opnd(size_32, (int)offset), is_signed);
+}
+*/
+
+// call
+ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm)
+{
+    EncoderBase::Operands args;
+    add_imm(args, imm);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args);
+}
+
+ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm,
+                                   Opnd_Size sz)
+{
+    EncoderBase::Operands args;
+    add_rm(args, rm, sz);
+    return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args);
+}
+
+/**
+* @note On EM64T: if target lies beyond 2G (does not fit into 32 bit
+*       offset) then generates indirect jump using RAX (whose content is
+*       destroyed).
+*/
+ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target)
+{
+#ifdef _EM64T_
+    int64 offset = target - stream;
+    if (fit32(offset)) {
+        offset -= 5; // sub 5 bytes for this instruction
+        Imm_Opnd imm(size_32, offset);
+        return call(stream, imm);
+    }
+    // need to use absolute indirect call
+    stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64);
+    return call(stream, rax_opnd, size_64);
+#else
+    I_32 offset = target - stream;
+    offset -= 5; // sub 5 bytes for this instruction
+    Imm_Opnd imm(size_32, offset);
+    return call(stream, imm);
+#endif
+}
+
+// return instruction
+ENCODER_DECLARE_EXPORT char * ret(char * stream)
+{
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm)
+{
+    EncoderBase::Operands args;
+    // TheManual says imm can be 16-bit only
+    //assert(imm.get_size() <= size_16);
+    args.add(EncoderBase::Operand(map_size(size_16), imm.get_value()));
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop)
+{
+    // TheManual says it can only be imm16
+    EncoderBase::Operands args(EncoderBase::Operand(OpndSize_16, pop, OpndExt_Zero));
+    return (char*)EncoderBase::encode(stream, Mnemonic_RET, args);
+}
+
+// floating-point instructions
+ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m,
+                                  bool is_double) {
+    EncoderBase::Operands args;
+    // a fake FP register as operand
+    add_fp(args, 0, is_double);
+    add_m(args, m, is_double ? size_64 : size_32);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FLD, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem,
+                                   bool is_long, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    if (pop_stk) {
+        add_m(args, mem, is_long ? size_64 : size_32);
+        // a fake FP register as operand
+        add_fp(args, 0, is_long);
+        return (char*)EncoderBase::encode(stream,  Mnemonic_FISTP, args);
+    }
+    // only 32-bit operands are supported
+    assert(is_long == false);
+    add_m(args, mem, size_32);
+    add_fp(args, 0, false);
+    return (char*)EncoderBase::encode(stream,  Mnemonic_FIST, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m,
+                                  bool is_double, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    add_m(args, m, is_double ? size_64 : size_32);
+    // a fake FP register as operand
+    add_fp(args, 0, is_double);
+    return (char*)EncoderBase::encode(stream,
+                                    pop_stk ? Mnemonic_FSTP : Mnemonic_FST,
+                                    args);
+}
+
+ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk)
+{
+    EncoderBase::Operands args;
+    add_fp(args, i, true);
+    return (char*)EncoderBase::encode(stream,
+                                    pop_stk ? Mnemonic_FSTP : Mnemonic_FST,
+                                    args);
+}
+
+ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem) {
+    EncoderBase::Operands args;
+    add_m(args, mem, size_16);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FLDCW, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem) {
+    EncoderBase::Operands args;
+    add_m(args, mem, size_16);
+    return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW, args);
+}
+
+ENCODER_DECLARE_EXPORT char * fnstsw(char * stream)
+{
+    return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW,
+                                      EncoderBase::Operands());
+}
+
+// string operations
+ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set) {
+    EncoderBase::Operands args;
+    return (char*)EncoderBase::encode(stream,
+                                      set ? Mnemonic_STD : Mnemonic_CLD,
+                                      args);
+}
+
+ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix)
+{
+	EncoderBase::Operands args;
+    if (prefix != no_prefix) {
+        assert(prefix == prefix_repnz || prefix == prefix_repz);
+        *stream = prefix;
+        ++stream;
+    }
+    return (char*)EncoderBase::encode(stream, Mnemonic_SCAS, args);
+}
+
+ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix)
+{
+    if (prefix != no_prefix) {
+        assert(prefix == prefix_rep);
+        *stream = prefix;
+        ++stream;
+    }
+
+	EncoderBase::Operands args;
+	return (char*)EncoderBase::encode(stream, Mnemonic_STOS, args);
+}
+
+// Intrinsic FP math functions
+
+ENCODER_DECLARE_EXPORT char * fprem(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_FPREM,
+                                      EncoderBase::Operands());
+}
+
+ENCODER_DECLARE_EXPORT char * fprem1(char * stream) {
+    return (char*)EncoderBase::encode(stream, Mnemonic_FPREM1,
+                                      EncoderBase::Operands());
+}
diff --git a/vm/compiler/template/Makefile-template b/vm/compiler/template/Makefile-template
new file mode 100644
index 0000000..9203183
--- /dev/null
+++ b/vm/compiler/template/Makefile-template
@@ -0,0 +1,49 @@
+# Copyright (C) 2008 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.
+
+#
+# Makefile for the Dalvik modular interpreter.  This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion.  If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+	$(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print)
+
+# Source files generated by the script.  There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+	$(OUTPUT_DIR)/CompilerTemplateAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+	@mkdir -p out
+	./gen-template.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/compiler/template/README.txt b/vm/compiler/template/README.txt
new file mode 100644
index 0000000..fced412
--- /dev/null
+++ b/vm/compiler/template/README.txt
@@ -0,0 +1 @@
+See README.txt under dalvik/vm/mterp for details.
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
new file mode 100644
index 0000000..51693fa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
new file mode 100644
index 0000000..ad1e122
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
new file mode 100644
index 0000000..992c894
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
@@ -0,0 +1,33 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     *
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmpd  d0, d1                       @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
new file mode 100644
index 0000000..0510ef6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
new file mode 100644
index 0000000..7241af1
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
new file mode 100644
index 0000000..bdb42d6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa58b8
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
new file mode 100644
index 0000000..fc125ce
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..dba3b08
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"fcvtsd  s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
new file mode 100644
index 0000000..4d910aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"ftosizd  s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..a5157dd
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fcvtds  d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
new file mode 100644
index 0000000..90900aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..c9f4fd6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fsitod  d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..a8f57b5
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"fsitos  s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..8bee853
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,19 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    vpush   {d0-d15}                    @ save out all fp registers
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
new file mode 100644
index 0000000..459e796
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
new file mode 100644
index 0000000..301fa84
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..196d082
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,11 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    r0 - offset from rSELF to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace.coreRegs[0]
+    add     r0, #64                     @ pointer to heapArgSpace.fpRegs[0]
+    vldmia  r0, {d0-d15}
+    sub     r0, #64                     @ pointer to heapArgSpace.coreRegs[0]
+    ldmia   r0, {r0-r12}
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..11f62b7
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,23 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: r7 value to save
+     *    Top of stack + 0: r0 value to save
+     *    r0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    r7 - the value of regMap
+     *
+     * The handler must save regMap, r0-r12 and then return with r0-r12
+     * with their original values (note that this means r0 and r7 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace
+    stmia   r0!, {r7}                   @ save regMap
+    ldr     r7, [r13, #0]               @ recover r0 value
+    stmia   r0!, {r7}                   @ save r0
+    ldr     r7, [r13, #4]               @ recover r7 value
+    stmia   r0!, {r1-r12}
+    add     r0, #12                     @ move to start of FP save regio
+    vstmia  r0, {d0-d15}
+    pop     {r0, r7}                    @ recover r0, r7
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
new file mode 100644
index 0000000..1c6bb46
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
@@ -0,0 +1,23 @@
+%verify "executed"
+    /*
+     * 64-bit floating point vfp sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     r2 src addr of op1
+     * On exit:
+     *     r0,r1 = res
+     */
+    fldd    d0, [r2]
+    fsqrtd  d1, d0
+    fcmpd   d1, d1
+    fmstat
+    fmrrd   r0, r1, d1
+    bxeq    lr   @ Result OK - return
+    ldr     r2, .Lsqrt
+    fmrrd   r0, r1, d0   @ reload orig operand
+    bx      r2   @ tail call to sqrt library routine
+
+.Lsqrt:
+    .word   sqrt
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa20a0
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
new file mode 100644
index 0000000..5e17e51
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TemplateOpList.h b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
new file mode 100644
index 0000000..0365ba4
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv5te-vfp/fbinop.S b/vm/compiler/template/armv5te-vfp/fbinop.S
new file mode 100644
index 0000000..3bc4b52
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinop.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     $instr
+     fsts    s2,[r0]
+     bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/fbinopWide.S b/vm/compiler/template/armv5te-vfp/fbinopWide.S
new file mode 100644
index 0000000..3774646
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinopWide.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     $instr
+     fstd    d2,[r0]
+     bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funop.S b/vm/compiler/template/armv5te-vfp/funop.S
new file mode 100644
index 0000000..8409c28
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funop.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 32bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s1 = op s0".
+     *
+     * For: float-to-int, int-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    $instr                              @ s1<- op s0
+    fsts    s1, [r0]                    @ vA<- s1
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funopNarrower.S b/vm/compiler/template/armv5te-vfp/funopNarrower.S
new file mode 100644
index 0000000..8566fca
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopNarrower.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 64bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    fldd    d0, [r1]                    @ d0<- vB
+    $instr                              @ s0<- op d0
+    fsts    s0, [r0]                    @ vA<- s0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/funopWider.S b/vm/compiler/template/armv5te-vfp/funopWider.S
new file mode 100644
index 0000000..dbe745c
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopWider.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    $instr                              @ d0<- op s0
+    fstd    d0, [r0]                    @ vA<- d0
+    bx      lr
diff --git a/vm/compiler/template/armv5te-vfp/platform.S b/vm/compiler/template/armv5te-vfp/platform.S
new file mode 100644
index 0000000..e0666a5
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/platform.S
@@ -0,0 +1,5 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
new file mode 100644
index 0000000..f18f6d3
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_DOUBLE.S" { "naninst":"mov     r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
new file mode 100644
index 0000000..02887e5
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_FLOAT.S" { "naninst":"mov     r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
new file mode 100644
index 0000000..23614e9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
@@ -0,0 +1,38 @@
+%default { "naninst":"mvn     r0, #0" }
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    $naninst                            @ r1<- 1 or -1 for NaN
+    bx      r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
new file mode 100644
index 0000000..f9293e6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
@@ -0,0 +1,56 @@
+%default { "naninst":"mvn     r0, #0" }
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    $naninst                            @ r1<- 1 or -1 for NaN
+    bx      r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
new file mode 100644
index 0000000..e5e8196
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
@@ -0,0 +1,33 @@
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .L${opcode}_less            @ signed compare on high part
+    bgt     .L${opcode}_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .L${opcode}_greater         @ unsigned compare on low part
+.L${opcode}_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.L${opcode}_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..9f24887
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
@@ -0,0 +1,30 @@
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
new file mode 100644
index 0000000..99a17ab
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,49 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+$chaintgt:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
new file mode 100644
index 0000000..d1be4fd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
new file mode 100644
index 0000000..d8661d9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,83 @@
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
new file mode 100644
index 0000000..816277a
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..b7015eb
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,60 @@
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
new file mode 100644
index 0000000..bfea7d9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
new file mode 100644
index 0000000..9dd4ff8
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,60 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     $chaintgt   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
new file mode 100644
index 0000000..6ca5bdd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..03926b6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,17 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    bx      lr                          @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
new file mode 100644
index 0000000..1ed3fb1
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
@@ -0,0 +1,21 @@
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
new file mode 100644
index 0000000..2695483
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,28 @@
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
new file mode 100644
index 0000000..6652b71
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
@@ -0,0 +1,27 @@
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, r9
+    add     r1, r2, r10                 @  r1<- r10 + low(ZxW + (YxX))
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_PERIODIC_PROFILING.S b/vm/compiler/template/armv5te/TEMPLATE_PERIODIC_PROFILING.S
new file mode 100644
index 0000000..c0f7d6e
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_PERIODIC_PROFILING.S
@@ -0,0 +1,26 @@
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .L${opcode}_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.L${opcode}_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..25b4ffa
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,8 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    r0 - offset from rSELF to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace.coreRegs[0]
+    ldmia   r0, {r0-r12}
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
new file mode 100644
index 0000000..e8e2d52
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
@@ -0,0 +1,57 @@
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S
new file mode 100644
index 0000000..d7af0bd
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "armv5te/TEMPLATE_RETURN.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..1c3aa4d
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,21 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: r7 value to save
+     *    Top of stack + 0: r0 value to save
+     *    r0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    r7 - the value of regMap
+     *
+     * The handler must save regMap, r0-r12 and then return with r0-r12
+     * with their original values (note that this means r0 and r7 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     */
+    add     r0, r0, rSELF               @ pointer to heapArgSpace
+    stmia   r0!, {r7}                   @ save regMap
+    ldr     r7, [r13, #0]               @ recover r0 value
+    stmia   r0!, {r7}                   @ save r0
+    ldr     r7, [r13, #4]               @ recover r7 value
+    stmia   r0!, {r1-r12}
+    pop     {r0, r7}                    @ recover r0, r7
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
new file mode 100644
index 0000000..532f8a4
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
@@ -0,0 +1,15 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
new file mode 100644
index 0000000..c737840
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
@@ -0,0 +1,15 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
new file mode 100644
index 0000000..54bde47
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
@@ -0,0 +1,133 @@
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
new file mode 100644
index 0000000..d970372
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
@@ -0,0 +1,113 @@
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    r3, [r0, #STRING_FIELDOFF_VALUE]
+    ldr    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+
+
+    /*
+     * At this point, we have:
+     *    r1: char to match
+     *    r2: starting offset
+     *    r3: object pointer (final result -> r0)
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r3, #16
+     add   r0, r3, r7, lsl #1
+
+     /* Save a copy of starting data in r7 */
+     mov   r7, r0
+
+     /* Clamp start to [0..count] */
+     cmp   r2, #0
+     movlt r2, #0
+     cmp   r2, r8
+     movgt r2, r8
+
+     /* Build pointer to start of data to compare and pre-bias */
+     add   r0, r0, r2, lsl #1
+     sub   r0, #2
+
+     /* Compute iteration count */
+     sub   r8, r2
+
+     /*
+      * At this point we have:
+      *   r0: start of data to test
+      *   r1: chat to compare
+      *   r8: iteration count
+      *   r7: original start of string
+      *   r3, r4, r9, r10, r11, r12 available for loading string data
+      */
+
+    subs  r8, #4
+    blt   indexof_remainder
+
+indexof_loop4:
+    ldrh  r3, [r0, #2]!
+    ldrh  r4, [r0, #2]!
+    ldrh  r10, [r0, #2]!
+    ldrh  r11, [r0, #2]!
+    cmp   r3, r1
+    beq   match_0
+    cmp   r4, r1
+    beq   match_1
+    cmp   r10, r1
+    beq   match_2
+    cmp   r11, r1
+    beq   match_3
+    subs  r8, #4
+    bge   indexof_loop4
+
+indexof_remainder:
+    adds    r8, #4
+    beq     indexof_nomatch
+
+indexof_loop1:
+    ldrh  r3, [r0, #2]!
+    cmp   r3, r1
+    beq   match_3
+    subs  r8, #1
+    bne   indexof_loop1
+
+indexof_nomatch:
+    mov   r0, #-1
+    bx    lr
+
+match_0:
+    sub   r0, #6
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_1:
+    sub   r0, #4
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_2:
+    sub   r0, #2
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_3:
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S b/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S
new file mode 100644
index 0000000..b737798
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S
@@ -0,0 +1,6 @@
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    r0    Dalvik PC that raises the exception
+     */
+    b       .LhandleException
diff --git a/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S
new file mode 100644
index 0000000..8a48df2
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S
@@ -0,0 +1,15 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    bx      lr
diff --git a/vm/compiler/template/armv5te/TemplateOpList.h b/vm/compiler/template/armv5te/TemplateOpList.h
new file mode 100644
index 0000000..abfec4b
--- /dev/null
+++ b/vm/compiler/template/armv5te/TemplateOpList.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(CMPG_DOUBLE)
+JIT_TEMPLATE(CMPL_DOUBLE)
+JIT_TEMPLATE(CMPG_FLOAT)
+JIT_TEMPLATE(CMPL_FLOAT)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
new file mode 100644
index 0000000..e6740d2
--- /dev/null
+++ b/vm/compiler/template/armv5te/footer.S
@@ -0,0 +1,129 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/armv5te/header.S b/vm/compiler/template/armv5te/header.S
new file mode 100644
index 0000000..6dcf5b9
--- /dev/null
+++ b/vm/compiler/template/armv5te/header.S
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
diff --git a/vm/compiler/template/armv5te/platform.S b/vm/compiler/template/armv5te/platform.S
new file mode 100644
index 0000000..e0666a5
--- /dev/null
+++ b/vm/compiler/template/armv5te/platform.S
@@ -0,0 +1,5 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
diff --git a/vm/compiler/template/armv7-a-neon/TemplateOpList.h b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
new file mode 100644
index 0000000..8d0f8d6
--- /dev/null
+++ b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/armv7-a/TemplateOpList.h b/vm/compiler/template/armv7-a/TemplateOpList.h
new file mode 100644
index 0000000..8d0f8d6
--- /dev/null
+++ b/vm/compiler/template/armv7-a/TemplateOpList.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/config-armv5te b/vm/compiler/template/config-armv5te
new file mode 100644
index 0000000..668df1b
--- /dev/null
+++ b/vm/compiler/template/config-armv5te
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv5te-vfp b/vm/compiler/template/config-armv5te-vfp
new file mode 100644
index 0000000..774bd96
--- /dev/null
+++ b/vm/compiler/template/config-armv5te-vfp
@@ -0,0 +1,68 @@
+
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te-vfp
+    op TEMPLATE_CMP_LONG armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+    op TEMPLATE_MUL_LONG armv5te
+    op TEMPLATE_RETURN armv5te
+    op TEMPLATE_SHL_LONG armv5te
+    op TEMPLATE_SHR_LONG armv5te
+    op TEMPLATE_USHR_LONG armv5te
+    op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+    op TEMPLATE_STRING_COMPARETO armv5te
+    op TEMPLATE_STRING_INDEXOF armv5te
+    op TEMPLATE_INTERPRET armv5te
+    op TEMPLATE_MONITOR_ENTER armv5te
+    op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+    op TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a b/vm/compiler/template/config-armv7-a
new file mode 100644
index 0000000..6bc2e6d
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a
@@ -0,0 +1,68 @@
+
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv7-a architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv7-a
+    op TEMPLATE_CMP_LONG armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+    op TEMPLATE_MUL_LONG armv5te
+    op TEMPLATE_RETURN armv5te
+    op TEMPLATE_SHL_LONG armv5te
+    op TEMPLATE_SHR_LONG armv5te
+    op TEMPLATE_USHR_LONG armv5te
+    op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+    op TEMPLATE_STRING_COMPARETO armv5te
+    op TEMPLATE_STRING_INDEXOF armv5te
+    op TEMPLATE_INTERPRET armv5te
+    op TEMPLATE_MONITOR_ENTER armv5te
+    op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+    op TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+    op TEMPLATE_MEM_OP_DECODE armv5te-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a-neon b/vm/compiler/template/config-armv7-a-neon
new file mode 100644
index 0000000..72bdf86
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a-neon
@@ -0,0 +1,68 @@
+
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv7-a architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv7-a-neon
+    op TEMPLATE_CMP_LONG armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+    op TEMPLATE_MUL_LONG armv5te
+    op TEMPLATE_RETURN armv5te
+    op TEMPLATE_SHL_LONG armv5te
+    op TEMPLATE_SHR_LONG armv5te
+    op TEMPLATE_USHR_LONG armv5te
+    op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+    op TEMPLATE_STRING_COMPARETO armv5te
+    op TEMPLATE_STRING_INDEXOF armv5te
+    op TEMPLATE_INTERPRET armv5te
+    op TEMPLATE_MONITOR_ENTER armv5te
+    op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+    op TEMPLATE_PERIODIC_PROFILING armv5te
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF armv5te
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF armv5te
+    op TEMPLATE_RETURN_PROF armv5te
+    op TEMPLATE_MEM_OP_DECODE armv5te-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-ia32 b/vm/compiler/template/config-ia32
new file mode 100644
index 0000000..5709017
--- /dev/null
+++ b/vm/compiler/template/config-ia32
@@ -0,0 +1,45 @@
+# 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.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import ia32/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import ia32/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start ia32
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import ia32/footer.S
diff --git a/vm/compiler/template/config-mips b/vm/compiler/template/config-mips
new file mode 100644
index 0000000..f212150
--- /dev/null
+++ b/vm/compiler/template/config-mips
@@ -0,0 +1,93 @@
+
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import mips/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import mips/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start mips
+
+    op TEMPLATE_SHL_LONG mips
+    op TEMPLATE_SHR_LONG mips
+    op TEMPLATE_USHR_LONG mips
+    op TEMPLATE_INT_TO_DOUBLE_VFP mips
+    op TEMPLATE_FLOAT_TO_DOUBLE_VFP mips
+    op TEMPLATE_ADD_DOUBLE_VFP mips
+    op TEMPLATE_DIV_DOUBLE_VFP mips
+    op TEMPLATE_MUL_DOUBLE_VFP mips
+    op TEMPLATE_SUB_DOUBLE_VFP mips
+    op TEMPLATE_ADD_FLOAT_VFP mips
+    op TEMPLATE_DIV_FLOAT_VFP mips
+    op TEMPLATE_MUL_FLOAT_VFP mips
+    op TEMPLATE_SUB_FLOAT_VFP mips
+    op TEMPLATE_FLOAT_TO_INT_VFP mips
+    op TEMPLATE_INT_TO_FLOAT_VFP mips
+    op TEMPLATE_DOUBLE_TO_FLOAT_VFP mips
+    op TEMPLATE_DOUBLE_TO_INT_VFP mips
+    op TEMPLATE_CMP_LONG mips
+    op TEMPLATE_CMPL_FLOAT_VFP mips
+    op TEMPLATE_CMPL_DOUBLE_VFP mips
+    op TEMPLATE_CMPG_FLOAT_VFP mips
+    op TEMPLATE_CMPG_DOUBLE_VFP mips
+    op TEMPLATE_MUL_LONG mips
+    op TEMPLATE_INTERPRET mips
+    op TEMPLATE_THROW_EXCEPTION_COMMON mips
+    op TEMPLATE_SQRT_DOUBLE_VFP mips
+    op TEMPLATE_SAVE_STATE mips
+    op TEMPLATE_RESTORE_STATE mips
+    op TEMPLATE_RETURN mips
+    op TEMPLATE_STRING_COMPARETO mips
+    op TEMPLATE_STRING_INDEXOF mips
+    op TEMPLATE_MEM_OP_DECODE mips
+    op TEMPLATE_MONITOR_ENTER mips
+    op TEMPLATE_MONITOR_ENTER_DEBUG mips
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN mips
+    op TEMPLATE_INVOKE_METHOD_CHAIN mips
+    op TEMPLATE_INVOKE_METHOD_NATIVE mips
+    op TEMPLATE_INVOKE_METHOD_NO_OPT mips
+
+    # New templates for ICS
+    op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF mips
+    op TEMPLATE_INVOKE_METHOD_CHAIN_PROF mips
+    op TEMPLATE_INVOKE_METHOD_NATIVE_PROF mips
+    op TEMPLATE_INVOKE_METHOD_NO_OPT_PROF mips
+    op TEMPLATE_PERIODIC_PROFILING mips
+    op TEMPLATE_RETURN_PROF mips
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import mips/footer.S
diff --git a/vm/compiler/template/gen-template.py b/vm/compiler/template/gen-template.py
new file mode 100755
index 0000000..9122fd5
--- /dev/null
+++ b/vm/compiler/template/gen-template.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 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.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik JIT.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "TemplateOpList.h" # need opcode list
+
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0             # 0=not started, 1=started, 2=ended
+default_op_dir = None
+opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L"         # use ".L" to hide labels from gdb
+
+
+# Exception class.
+class DataParseError(SyntaxError):
+    "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+    return { "handler_size_bits":handler_size_bits,
+             "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes).  Throws an exception if "bytes" is not a power
+# of two.
+#
+def setHandlerSize(tokens):
+    global handler_size_bits, handler_size_bytes
+    if len(tokens) != 2:
+        raise DataParseError("handler-size requires one argument")
+    if handler_size_bits != -1000:
+        raise DataParseError("handler-size may only be set once")
+
+    # compute log2(n), and make sure n is a power of 2
+    handler_size_bytes = bytes = int(tokens[1])
+    bits = -1
+    while bytes > 0:
+        bytes //= 2     # halve with truncating division
+        bits += 1
+
+    if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+        raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
+                % orig_bytes)
+    handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    source = tokens[1]
+    if source.endswith(".S"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+    else:
+        raise DataParseError("don't know how to import %s (expecting .c/.S)"
+                % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+    global asm_stub_text
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    try:
+        stub_fp = open(tokens[1])
+        asm_stub_text = stub_fp.readlines()
+    except IOError, err:
+        stub_fp.close()
+        raise DataParseError("unable to load asm-stub: %s" % str(err))
+    stub_fp.close()
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+    global in_op_start
+    global default_op_dir
+    if len(tokens) != 2:
+        raise DataParseError("opStart takes a directory name argument")
+    if in_op_start != 0:
+        raise DataParseError("opStart can only be specified once")
+    default_op_dir = tokens[1]
+    in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+    #global opcode_locations
+    if len(tokens) != 3:
+        raise DataParseError("op requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("op statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+    global in_op_start
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if in_op_start != 1:
+        raise DataParseError("opEnd must follow opStart, and only appear once")
+    in_op_start = 2
+
+    loadAndEmitOpcodes()
+
+
+#
+# Extract an ordered list of instructions from the VM sources.  We use the
+# "goto table" definition macro, which has exactly kNumPackedOpcodes
+# entries.
+#
+def getOpcodeList():
+    opcodes = []
+    opcode_fp = open("%s/%s" % (target_arch, interp_defs_file))
+    opcode_re = re.compile(r"^JIT_TEMPLATE\((\w+)\)", re.DOTALL)
+    for line in opcode_fp:
+        match = opcode_re.match(line)
+        if not match:
+            continue
+        opcodes.append("TEMPLATE_" + match.group(1))
+    opcode_fp.close()
+
+    return opcodes
+
+
+#
+# Load and emit opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitOpcodes():
+    sister_list = []
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global dvmCompilerTemplateStart\n")
+    asm_fp.write("    .type   dvmCompilerTemplateStart, %function\n")
+    asm_fp.write("    .section .data.rel.ro\n\n")
+    asm_fp.write("dvmCompilerTemplateStart:\n\n")
+
+    for i in xrange(len(opcodes)):
+        op = opcodes[i]
+
+        if opcode_locations.has_key(op):
+            location = opcode_locations[op]
+        else:
+            location = default_op_dir
+
+        loadAndEmitAsm(location, i, sister_list)
+
+    # Use variable sized handlers now
+    # asm_fp.write("\n    .balign %d\n" % handler_size_bytes)
+    asm_fp.write("    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart\n")
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+    op = opcodes[opindex]
+    source = "%s/%s.S" % (location, op)
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+    print " emit %s --> asm" % source
+
+    emitAsmHeader(asm_fp, dict)
+    appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict):
+    outfp.write("/* ------------------------------ */\n")
+    # The alignment directive ensures that the handler occupies
+    # at least the correct amount of space.  We don't try to deal
+    # with overflow here.
+    outfp.write("    .balign 4\n")
+    # Emit a label so that gdb will say the right thing.  We prepend an
+    # underscore so the symbol name doesn't clash with the Opcode enum.
+    template_name = "dvmCompiler_%(opcode)s" % dict
+    outfp.write("    .global %s\n" % template_name);
+    outfp.write("%s:\n" % template_name);
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+    emitAsmHeader(outfp, dict)
+    for line in asm_stub_text:
+        templ = Template(line)
+        outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp".  Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings.  (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+    outfp.write("/* File: %s */\n" % source)
+    infp = open(source, "r")
+    in_sister = False
+    for line in infp:
+        if line.startswith("%include"):
+            # Parse the "include" line
+            tokens = line.strip().split(' ', 2)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%include in %s" % source)
+
+            alt_source = tokens[1].strip("\"")
+            if alt_source == source:
+                raise DataParseError("self-referential %%include in %s"
+                        % source)
+
+            new_dict = dict.copy()
+            if len(tokens) == 3:
+                new_dict.update(eval(tokens[2]))
+            #print " including src=%s dict=%s" % (alt_source, new_dict)
+            appendSourceFile(alt_source, new_dict, outfp, sister_list)
+            continue
+
+        elif line.startswith("%default"):
+            # copy keywords into dictionary
+            tokens = line.strip().split(' ', 1)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%default in %s" % source)
+            defaultValues = eval(tokens[1])
+            for entry in defaultValues:
+                dict.setdefault(entry, defaultValues[entry])
+            continue
+
+        elif line.startswith("%verify"):
+            # more to come, someday
+            continue
+
+        elif line.startswith("%break") and sister_list != None:
+            # allow more than one %break, ignoring all following the first
+            if not in_sister:
+                in_sister = True
+                sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+            continue
+
+        # perform keyword substitution if a dictionary was provided
+        if dict != None:
+            templ = Template(line)
+            try:
+                subline = templ.substitute(dict)
+            except KeyError, err:
+                raise DataParseError("keyword substitution failed in %s: %s"
+                        % (source, str(err)))
+            except:
+                print "ERROR: substitution failed: " + line
+                raise
+        else:
+            subline = line
+
+        # write output to appropriate file
+        if in_sister:
+            sister_list.append(subline)
+        else:
+            outfp.write(subline)
+    outfp.write("\n")
+    infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+    equals = "========================================" \
+             "==================================="
+
+    fp.write("\n/*\n * %s\n *  %s\n * %s\n */\n" %
+        (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+    print "Usage: %s target-arch output-dir" % sys.argv[0]
+    sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+#    print "  %s" % op
+
+#
+# Open config file.
+#
+try:
+    config_fp = open("config-%s" % target_arch)
+except:
+    print "Unable to open config file 'config-%s'" % target_arch
+    sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+    asm_fp = open("%s/CompilerTemplateAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+    print "Unable to open output files"
+    print "Make sure directory '%s' exists and existing files are writable" \
+            % output_dir
+    # Ideally we'd remove the files to avoid confusing "make", but if they
+    # failed to open we probably won't be able to remove them either.
+    sys.exit(1)
+
+print "Generating %s" % (asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-template.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+    for line in config_fp:
+        line = line.strip()         # remove CRLF, leading spaces
+        tokens = line.split(' ')    # tokenize
+        #print "%d: %s" % (len(tokens), tokens)
+        if len(tokens[0]) == 0:
+            #print "  blank"
+            pass
+        elif tokens[0][0] == '#':
+            #print "  comment"
+            pass
+        else:
+            if tokens[0] == "handler-size":
+                setHandlerSize(tokens)
+            elif tokens[0] == "import":
+                importFile(tokens)
+            elif tokens[0] == "asm-stub":
+                setAsmStub(tokens)
+            elif tokens[0] == "op-start":
+                opStart(tokens)
+            elif tokens[0] == "op-end":
+                opEnd(tokens)
+            elif tokens[0] == "op":
+                opEntry(tokens)
+            else:
+                raise DataParseError, "unrecognized command '%s'" % tokens[0]
+except DataParseError, err:
+    print "Failed: " + str(err)
+    # TODO: remove output files so "make" doesn't get confused
+    failed = True
+    asm_fp.close()
+    c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if asm_fp:
+    asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S b/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..5c7bf7c
--- /dev/null
+++ b/vm/compiler/template/ia32/TEMPLATE_INTERPRET.S
@@ -0,0 +1,38 @@
+    /*
+     * This handler is a bit odd - it may be called via chaining or
+     * from static code and is expected to cause control to flow
+     * to the interpreter.  The problem is where to find the Dalvik
+     * PC of the next instruction.  When called via chaining, the dPC
+     * will be located at *rp.  When called from static code, rPC is
+     * valid and rp is a real return pointer (that should be ignored).
+     * The Arm target deals with this by using the link register as
+     * a flag.  If it is zero, we know we were called from static code.
+     * If non-zero, it points to the chain cell containing dPC.
+     * For x86, we'll infer the source by looking where rp points.
+     * If it points to anywhere within the code cache, we'll assume
+     * we got here via chaining.  Otherwise, we'll assume rPC is valid.
+     *
+     * On entry:
+     *    (TOS)<- return pointer or pointer to dPC
+     */
+
+/*
+ * FIXME - this won't work as-is.  The cache boundaries are not
+ * set up until later.  Perhaps rething this whole thing.  Do we
+ * really need an interpret teplate?
+ */
+
+
+     movl   rSELF,%ecx
+     movl   $$.LinterpPunt,%edx
+     pop    %eax
+     /*cmpl   %eax,offThread_jitCacheEnd(%ecx)*/
+     ja     1f
+     /*cmpl   %eax,offThread_jitCacheStart(%ecx)*/
+     jb     1f
+     movl   %eax,rPC
+1:
+     jmp    *(%edx)
+
+.LinterpPunt:
+    .long   dvmJitToInterpPunt
diff --git a/vm/compiler/template/ia32/TemplateOpList.h b/vm/compiler/template/ia32/TemplateOpList.h
new file mode 100644
index 0000000..a5000da
--- /dev/null
+++ b/vm/compiler/template/ia32/TemplateOpList.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(INTERPRET)
diff --git a/vm/compiler/template/ia32/footer.S b/vm/compiler/template/ia32/footer.S
new file mode 100644
index 0000000..23384a5
--- /dev/null
+++ b/vm/compiler/template/ia32/footer.S
@@ -0,0 +1,13 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/ia32/header.S b/vm/compiler/template/ia32/header.S
new file mode 100644
index 0000000..ea2cc0f
--- /dev/null
+++ b/vm/compiler/template/ia32/header.S
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#if defined(WITH_JIT)
+
+/* Subset of defines from mterp/x86/header.S */
+#define rSELF (%ebp)
+#define rPC   %esi
+#define rFP   %edi
+#define rINST %ebx
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
diff --git a/vm/compiler/template/ia32/platform.S b/vm/compiler/template/ia32/platform.S
new file mode 100644
index 0000000..a84e62d
--- /dev/null
+++ b/vm/compiler/template/ia32/platform.S
@@ -0,0 +1,7 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
diff --git a/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S
new file mode 100644
index 0000000..56a02e3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_ADD_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__adddf3)","instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S
new file mode 100644
index 0000000..b0cbb31
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_ADD_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S
new file mode 100644
index 0000000..f5fa114
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPG_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/TEMPLATE_CMPL_DOUBLE_VFP.S" { "naninst":"li            rTEMP, 1" }
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S
new file mode 100644
index 0000000..c239a75
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPG_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/TEMPLATE_CMPL_FLOAT_VFP.S" { "naninst":"li     rTEMP, 1" }
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S
new file mode 100644
index 0000000..ee8f99e
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPL_DOUBLE_VFP.S
@@ -0,0 +1,68 @@
+%default { "naninst":"li     rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, ${opcode}_finish
+#else
+    LOAD64_F(ft0, ft0f, a0)             # ft0<- vBB
+    LOAD64_F(ft1, ft1f, a1)             # ft1<- vCC
+    c.olt.d     fcc0, ft0, ft1          # Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, ${opcode}_finish
+    c.olt.d     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, ${opcode}_finish
+    c.eq.d      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, ${opcode}_finish
+#endif
+
+    $naninst
+
+${opcode}_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S
new file mode 100644
index 0000000..d509ea6
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMPL_FLOAT_VFP.S
@@ -0,0 +1,68 @@
+%default { "naninst":"li     rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, ${opcode}_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, ${opcode}_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, ${opcode}_finish
+#else
+    LOAD_F(ft0, a0)                     # ft0<- vBB
+    LOAD_F(ft1, a1)                     # ft1<- vCC
+    c.olt.s     fcc0, ft0, ft1          #Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, ${opcode}_finish
+    c.olt.s     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, ${opcode}_finish
+    c.eq.s      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, ${opcode}_finish
+#endif
+
+    $naninst
+
+${opcode}_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S b/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S
new file mode 100644
index 0000000..9ecb069
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_CMP_LONG.S
@@ -0,0 +1,27 @@
+%verify "endianess"
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     *
+     * This code assumes the register pair ordering will depend on endianess (a1:a0 or a0:a1).
+     *    a1:a0 => vBB
+     *    a3:a2 => vCC
+     */
+    /* cmp-long vAA, vBB, vCC */
+    slt    t0, rARG1, rARG3             # compare hi
+    sgt    t1, rARG1, rARG3
+    subu   v0, t1, t0                   # v0<- (-1,1,0)
+    bnez   v0, .L${opcode}_finish
+                                        # at this point x.hi==y.hi
+    sltu   t0, rARG0, rARG2             # compare lo
+    sgtu   t1, rARG0, rARG2
+    subu   v0, t1, t0                   # v0<- (-1,1,0) for [< > =]
+.L${opcode}_finish:
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S
new file mode 100644
index 0000000..a951f93
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DIV_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__divdf3)","instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S
new file mode 100644
index 0000000..11b3da6
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DIV_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..51b1e96
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopNarrower.S" {"instr":"JAL(__truncdfsf2)","instr_f":"cvt.s.d  fv0,fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S
new file mode 100644
index 0000000..4774bb1
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_DOUBLE_TO_INT_VFP.S
@@ -0,0 +1,79 @@
+%verify "executed"
+%include "mips/funopNarrower.S" {"instr":"b    d2i_doconv","instr_f":"b    d2i_doconv"}
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rOBJ as global to hold arguments (they are not bound to a global var)
+ */
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move        rBIX, rARG0                       # save a0
+    move        rOBJ, rARG1                       #  and a1
+    JAL(__gedf2)                               # is arg >= maxint?
+
+    move        t0, v0
+    li          v0, ~0x80000000                # return maxint (7fffffff)
+    bgez        t0, .L${opcode}_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                       # recover arg
+    move        rARG1, rOBJ
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                               # is arg <= minint?
+
+    move        t0, v0
+    li          v0, 0x80000000                 # return minint (80000000)
+    blez        t0, .L${opcode}_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    move        rARG2, rBIX                  # compare against self
+    move        rARG3, rOBJ
+    JAL(__nedf2)                        # is arg == self?
+
+    move        t0, v0                  # zero == no
+    li          v0, 0
+    bnez        t0, .L${opcode}_set_vreg        # return zero for NaN
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    JAL(__fixdfsi)                      # convert double to int
+    b           .L${opcode}_set_vreg
+#else
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa1, fa0
+    l.s         fv0, .LDOUBLE_TO_INT_maxret
+    bc1t        .L${opcode}_set_vreg_f
+
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa0, fa1
+    l.s         fv0, .LDOUBLE_TO_INT_minret
+    bc1t        .L${opcode}_set_vreg_f
+
+    mov.d       fa1, fa0
+    c.un.d      fcc0, fa0, fa1
+    li.s        fv0, 0
+    bc1t        .L${opcode}_set_vreg_f
+
+    trunc.w.d   fv0, fa0
+    b           .L${opcode}_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword   0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword   0xc1e0000000000000                  # minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word   0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word   0x80000000
diff --git a/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..66d14ce
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopWider.S" {"instr":"JAL(__extendsfdf2)","instr_f":"cvt.d.s fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S
new file mode 100644
index 0000000..6eaaab9
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_FLOAT_TO_INT_VFP.S
@@ -0,0 +1,62 @@
+%verify "executed"
+%include "mips/funop.S" {"instr":"b    f2i_doconv","instr_f":"b        f2i_doconv"}
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+        li      a1, 0x4f000000  # (float)maxint
+        move    rBIX, a0
+        JAL(__gesf2)            # is arg >= maxint?
+        move    t0, v0
+        li      v0, ~0x80000000 # return maxint (7fffffff)
+        bgez    t0, .L${opcode}_set_vreg
+
+        move    a0, rBIX                # recover arg
+        li      a1, 0xcf000000  # (float)minint
+        JAL(__lesf2)
+
+        move    t0, v0
+        li      v0, 0x80000000  # return minint (80000000)
+        blez    t0, .L${opcode}_set_vreg
+        move    a0, rBIX
+        move    a1, rBIX
+        JAL(__nesf2)
+
+        move    t0, v0
+        li      v0, 0           # return zero for NaN
+        bnez    t0, .L${opcode}_set_vreg
+
+        move    a0, rBIX
+        JAL(__fixsfsi)
+        b .L${opcode}_set_vreg
+#else
+        l.s             fa1, .LFLOAT_TO_INT_max
+        c.ole.s         fcc0, fa1, fa0
+        l.s             fv0, .LFLOAT_TO_INT_ret_max
+        bc1t            .L${opcode}_set_vreg_f
+
+        l.s             fa1, .LFLOAT_TO_INT_min
+        c.ole.s         fcc0, fa0, fa1
+        l.s             fv0, .LFLOAT_TO_INT_ret_min
+        bc1t            .L${opcode}_set_vreg_f
+
+        mov.s           fa1, fa0
+        c.un.s          fcc0, fa0, fa1
+        li.s            fv0, 0
+        bc1t            .L${opcode}_set_vreg_f
+
+        trunc.w.s       fv0, fa0
+        b .L${opcode}_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+        .word   0x4f000000
+.LFLOAT_TO_INT_min:
+        .word   0xcf000000
+.LFLOAT_TO_INT_ret_max:
+        .word   0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+        .word   0x80000000
+
diff --git a/vm/compiler/template/mips/TEMPLATE_INTERPRET.S b/vm/compiler/template/mips/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..1284621
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INTERPRET.S
@@ -0,0 +1,21 @@
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC.
+     * On entry:
+     *    ra - if NULL:
+     *        a1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [ra] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    la      t0, dvmJitToInterpPunt
+    move    a0, a1
+    beq     ra, zero, 1f
+    lw      a0, 0(ra)
+1:
+    jr      t0
+    # doesn't return
diff --git a/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..0dca600
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funopWider.S" {"instr":"JAL(__floatsidf)","instr_f":"cvt.d.w    fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..384c207
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INT_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/funop.S" {"instr":"JAL(__floatsisf)","instr_f":"cvt.s.w fv0, fa0"}
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S
new file mode 100644
index 0000000..c1e03ce
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,67 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+$chaintgt:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
new file mode 100644
index 0000000..797ff03
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S
new file mode 100644
index 0000000..7bba88c
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,106 @@
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
new file mode 100644
index 0000000..e167996
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_NATIVE.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..06935d7
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,80 @@
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
new file mode 100644
index 0000000..386ce63
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
new file mode 100644
index 0000000..e95ab32
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,59 @@
+%default { "chaintgt" : ".LinvokeChain" }
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, $chaintgt       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
new file mode 100644
index 0000000..39d6452
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S" { "chaintgt" : ".LinvokeChainProf" }
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..038ccfd
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,165 @@
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+#ifdef HARD_FLOAT
+    /* push f0-f31 onto stack */
+    sw      f0, fr0*-4(sp)              # push f0
+    sw      f1, fr1*-4(sp)              # push f1
+    sw      f2, fr2*-4(sp)              # push f2
+    sw      f3, fr3*-4(sp)              # push f3
+    sw      f4, fr4*-4(sp)              # push f4
+    sw      f5, fr5*-4(sp)              # push f5
+    sw      f6, fr6*-4(sp)              # push f6
+    sw      f7, fr7*-4(sp)              # push f7
+    sw      f8, fr8*-4(sp)              # push f8
+    sw      f9, fr9*-4(sp)              # push f9
+    sw      f10, fr10*-4(sp)            # push f10
+    sw      f11, fr11*-4(sp)            # push f11
+    sw      f12, fr12*-4(sp)            # push f12
+    sw      f13, fr13*-4(sp)            # push f13
+    sw      f14, fr14*-4(sp)            # push f14
+    sw      f15, fr15*-4(sp)            # push f15
+    sw      f16, fr16*-4(sp)            # push f16
+    sw      f17, fr17*-4(sp)            # push f17
+    sw      f18, fr18*-4(sp)            # push f18
+    sw      f19, fr19*-4(sp)            # push f19
+    sw      f20, fr20*-4(sp)            # push f20
+    sw      f21, fr21*-4(sp)            # push f21
+    sw      f22, fr22*-4(sp)            # push f22
+    sw      f23, fr23*-4(sp)            # push f23
+    sw      f24, fr24*-4(sp)            # push f24
+    sw      f25, fr25*-4(sp)            # push f25
+    sw      f26, fr26*-4(sp)            # push f26
+    sw      f27, fr27*-4(sp)            # push f27
+    sw      f28, fr28*-4(sp)            # push f28
+    sw      f29, fr29*-4(sp)            # push f29
+    sw      f30, fr30*-4(sp)            # push f30
+    sw      f31, fr31*-4(sp)            # push f31
+
+    sub     sp, (32-0)*4                # adjust stack pointer
+#endif
+
+    /* push gp registers (except zero, gp, sp, and fp) */
+    .set noat
+    sw      AT, r_AT*-4(sp)             # push at
+    .set at
+    sw      v0, r_V0*-4(sp)             # push v0
+    sw      v1, r_V1*-4(sp)             # push v1
+    sw      a0, r_A0*-4(sp)             # push a0
+    sw      a1, r_A1*-4(sp)             # push a1
+    sw      a2, r_A2*-4(sp)             # push a2
+    sw      a3, r_A3*-4(sp)             # push a3
+    sw      t0, r_T0*-4(sp)             # push t0
+    sw      t1, r_T1*-4(sp)             # push t1
+    sw      t2, r_T2*-4(sp)             # push t2
+    sw      t3, r_T3*-4(sp)             # push t3
+    sw      t4, r_T4*-4(sp)             # push t4
+    sw      t5, r_T5*-4(sp)             # push t5
+    sw      t6, r_T6*-4(sp)             # push t6
+    sw      t7, r_T7*-4(sp)             # push t7
+    sw      s0, r_S0*-4(sp)             # push s0
+    sw      s1, r_S1*-4(sp)             # push s1
+    sw      s2, r_S2*-4(sp)             # push s2
+    sw      s3, r_S3*-4(sp)             # push s3
+    sw      s4, r_S4*-4(sp)             # push s4
+    sw      s5, r_S5*-4(sp)             # push s5
+    sw      s6, r_S6*-4(sp)             # push s6
+    sw      s7, r_S7*-4(sp)             # push s7
+    sw      t8, r_T8*-4(sp)             # push t8
+    sw      t9, r_T9*-4(sp)             # push t9
+    sw      k0, r_K0*-4(sp)             # push k0
+    sw      k1, r_K1*-4(sp)             # push k1
+    sw      ra, r_RA*-4(sp)             # push RA
+
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    sub     sp, (32-0)*4                # adjust stack pointer
+
+    la     a2, .LdvmSelfVerificationMemOpDecode  # defined in footer.S
+    lw     a2, (a2)
+    move   a0, ra                       # a0<- link register
+    move   a1, sp                       # a1<- stack pointer
+    JALR(a2)
+
+    /* pop gp registers (except zero, gp, sp, and fp) */
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    add     sp, (32-0)*4                # adjust stack pointer
+    .set noat
+    lw      AT, r_AT*-4(sp)             # pop at
+    .set at
+    lw      v0, r_V0*-4(sp)             # pop v0
+    lw      v1, r_V1*-4(sp)             # pop v1
+    lw      a0, r_A0*-4(sp)             # pop a0
+    lw      a1, r_A1*-4(sp)             # pop a1
+    lw      a2, r_A2*-4(sp)             # pop a2
+    lw      a3, r_A3*-4(sp)             # pop a3
+    lw      t0, r_T0*-4(sp)             # pop t0
+    lw      t1, r_T1*-4(sp)             # pop t1
+    lw      t2, r_T2*-4(sp)             # pop t2
+    lw      t3, r_T3*-4(sp)             # pop t3
+    lw      t4, r_T4*-4(sp)             # pop t4
+    lw      t5, r_T5*-4(sp)             # pop t5
+    lw      t6, r_T6*-4(sp)             # pop t6
+    lw      t7, r_T7*-4(sp)             # pop t7
+    lw      s0, r_S0*-4(sp)             # pop s0
+    lw      s1, r_S1*-4(sp)             # pop s1
+    lw      s2, r_S2*-4(sp)             # pop s2
+    lw      s3, r_S3*-4(sp)             # pop s3
+    lw      s4, r_S4*-4(sp)             # pop s4
+    lw      s5, r_S5*-4(sp)             # pop s5
+    lw      s6, r_S6*-4(sp)             # pop s6
+    lw      s7, r_S7*-4(sp)             # pop s7
+    lw      t8, r_T8*-4(sp)             # pop t8
+    lw      t9, r_T9*-4(sp)             # pop t9
+    lw      k0, r_K0*-4(sp)             # pop k0
+    lw      k1, r_K1*-4(sp)             # pop k1
+    lw      ra, r_RA*-4(sp)             # pop RA
+
+#ifdef HARD_FLOAT
+    /* pop f0-f31 from stack */
+    add     sp, (32-0)*4                # adjust stack pointer
+    lw      f0, fr0*-4(sp)              # pop f0
+    lw      f1, fr1*-4(sp)              # pop f1
+    lw      f2, fr2*-4(sp)              # pop f2
+    lw      f3, fr3*-4(sp)              # pop f3
+    lw      f4, fr4*-4(sp)              # pop f4
+    lw      f5, fr5*-4(sp)              # pop f5
+    lw      f6, fr6*-4(sp)              # pop f6
+    lw      f7, fr7*-4(sp)              # pop f7
+    lw      f8, fr8*-4(sp)              # pop f8
+    lw      f9, fr9*-4(sp)              # pop f9
+    lw      f10, fr10*-4(sp)            # pop f10
+    lw      f11, fr11*-4(sp)            # pop f11
+    lw      f12, fr12*-4(sp)            # pop f12
+    lw      f13, fr13*-4(sp)            # pop f13
+    lw      f14, fr14*-4(sp)            # pop f14
+    lw      f15, fr15*-4(sp)            # pop f15
+    lw      f16, fr16*-4(sp)            # pop f16
+    lw      f17, fr17*-4(sp)            # pop f17
+    lw      f18, fr18*-4(sp)            # pop f18
+    lw      f19, fr19*-4(sp)            # pop f19
+    lw      f20, fr20*-4(sp)            # pop f20
+    lw      f21, fr21*-4(sp)            # pop f21
+    lw      f22, fr22*-4(sp)            # pop f22
+    lw      f23, fr23*-4(sp)            # pop f23
+    lw      f24, fr24*-4(sp)            # pop f24
+    lw      f25, fr25*-4(sp)            # pop f25
+    lw      f26, fr26*-4(sp)            # pop f26
+    lw      f27, fr27*-4(sp)            # pop f27
+    lw      f28, fr28*-4(sp)            # pop f28
+    lw      f29, fr29*-4(sp)            # pop f29
+    lw      f30, fr30*-4(sp)            # pop f30
+    lw      f31, fr31*-4(sp)            # pop f31
+#endif
+
+    RETURN
+#endif
diff --git a/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S
new file mode 100644
index 0000000..902cdb7
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER.S
@@ -0,0 +1,25 @@
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li      a0, kHeavyweightMonitor
+#endif
+    jr      a2
diff --git a/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S
new file mode 100644
index 0000000..23bf661
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,30 @@
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     *
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # test for exception
+    lw     a1, offThread_exception(rSELF)
+    beqz   a1, 1f
+    sub    a0, rPC, 2                           # roll dPC back to this monitor instruction
+    j      .LhandleException
+1:
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li     a0, kHeavyweightMonitor
+#endif
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+    jr     a2
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S
new file mode 100644
index 0000000..9254d76
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__muldf3)","instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S
new file mode 100644
index 0000000..c1517b3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__mulsf3)","instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S b/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S
new file mode 100644
index 0000000..d91dcb8
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_MUL_LONG.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in a0/a1, op2 in a2/a3, return in v0/v1
+     *
+     * Consider WXxYZ (a1a0 x a3a2) with a long multiply:
+     *
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     *
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     rRESULT1,rARG3,rARG0              #  v1= a3a0
+    multu   rARG2,rARG0
+    mfhi    t1
+    mflo    rRESULT0                          #  v0= a2a0
+    mul     t0,rARG2,rARG1                    #  t0= a2a1
+    addu    rRESULT1,rRESULT1,t1              #  v1= a3a0 + hi(a2a0)
+    addu    rRESULT1,rRESULT1,t0              #  v1= a3a0 + hi(a2a0) + a2a1;
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S b/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S
new file mode 100644
index 0000000..4df771d
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_PERIODIC_PROFILING.S
@@ -0,0 +1,28 @@
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (ra-16) is address of pointer to counter.  Note: the counter
+     *    actually exists 16 bytes before the return target for mips.
+     *     - 4 bytes for prof count addr.
+     *     - 4 bytes for chain cell offset (2bytes 32 bit aligned).
+     *     - 4 bytes for call TEMPLATE_PERIODIC_PROFILING.
+     *     - 4 bytes for call delay slot.
+     */
+     lw     a0, -16(ra)
+     lw     a1, offThread_pProfileCountdown(rSELF)
+     lw     a2, 0(a0)                   # get counter
+     lw     a3, 0(a1)                   # get countdown timer
+     addu   a2, 1
+     sub    a3, 1                       # FIXME - bug in ARM code???
+     bltz   a3, .L${opcode}_disable_profiling
+     sw     a2, 0(a0)
+     sw     a3, 0(a1)
+     RETURN
+.L${opcode}_disable_profiling:
+     la     a0, dvmJitTraceProfilingOff
+     JALR(a0)
+     # The ra register is preserved by the JALR macro.
+     jr     ra
diff --git a/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..a4c505b
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,91 @@
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    a0 - offset from rSELF to the 1st element of the coreRegs save array.
+     * Note: the following registers are not restored
+     *       zero, AT, gp, sp, fp, ra
+     */
+
+    add     a0, a0, rSELF               # pointer to heapArgSpace.coreRegs[0]
+#if 0
+    lw      zero, r_ZERO*4(a0)          # restore zero
+#endif
+    .set noat
+    lw      AT, r_AT*4(a0)              # restore at
+    .set at
+    lw      v0, r_V0*4(a0)              # restore v0
+    lw      v1, r_V1*4(a0)              # restore v1
+
+    lw      a1, r_A1*4(a0)              # restore a1
+    lw      a2, r_A2*4(a0)              # restore a2
+    lw      a3, r_A3*4(a0)              # restore a3
+
+    lw      t0, r_T0*4(a0)              # restore t0
+    lw      t1, r_T1*4(a0)              # restore t1
+    lw      t2, r_T2*4(a0)              # restore t2
+    lw      t3, r_T3*4(a0)              # restore t3
+    lw      t4, r_T4*4(a0)              # restore t4
+    lw      t5, r_T5*4(a0)              # restore t5
+    lw      t6, r_T6*4(a0)              # restore t6
+    lw      t7, r_T7*4(a0)              # restore t7
+
+    lw      s0, r_S0*4(a0)              # restore s0
+    lw      s1, r_S1*4(a0)              # restore s1
+    lw      s2, r_S2*4(a0)              # restore s2
+    lw      s3, r_S3*4(a0)              # restore s3
+    lw      s4, r_S4*4(a0)              # restore s4
+    lw      s5, r_S5*4(a0)              # restore s5
+    lw      s6, r_S6*4(a0)              # restore s6
+    lw      s7, r_S7*4(a0)              # restore s7
+
+    lw      t8, r_T8*4(a0)              # restore t8
+    lw      t9, r_T9*4(a0)              # restore t9
+
+    lw      k0, r_K0*4(a0)              # restore k0
+    lw      k1, r_K1*4(a0)              # restore k1
+
+#if 0
+    lw      gp, r_GP*4(a0)              # restore gp
+    lw      sp, r_SP*4(a0)              # restore sp
+    lw      fp, r_FP*4(a0)              # restore fp
+    lw      ra, r_RA*4(a0)              # restore ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    lw      f0, fr0*4(a0)               # restore f0
+    lw      f1, fr1*4(a0)               # restore f1
+    lw      f2, fr2*4(a0)               # restore f2
+    lw      f3, fr3*4(a0)               # restore f3
+    lw      f4, fr4*4(a0)               # restore f4
+    lw      f5, fr5*4(a0)               # restore f5
+    lw      f6, fr6*4(a0)               # restore f6
+    lw      f7, fr7*4(a0)               # restore f7
+    lw      f8, fr8*4(a0)               # restore f8
+    lw      f9, fr9*4(a0)               # restore f9
+    lw      f10, fr10*4(a0)             # restore f10
+    lw      f11, fr11*4(a0)             # restore f11
+    lw      f12, fr12*4(a0)             # restore f12
+    lw      f13, fr13*4(a0)             # restore f13
+    lw      f14, fr14*4(a0)             # restore f14
+    lw      f15, fr15*4(a0)             # restore f15
+    lw      f16, fr16*4(a0)             # restore f16
+    lw      f17, fr17*4(a0)             # restore f17
+    lw      f18, fr18*4(a0)             # restore f18
+    lw      f19, fr19*4(a0)             # restore f19
+    lw      f20, fr20*4(a0)             # restore f20
+    lw      f21, fr21*4(a0)             # restore f21
+    lw      f22, fr22*4(a0)             # restore f22
+    lw      f23, fr23*4(a0)             # restore f23
+    lw      f24, fr24*4(a0)             # restore f24
+    lw      f25, fr25*4(a0)             # restore f25
+    lw      f26, fr26*4(a0)             # restore f26
+    lw      f27, fr27*4(a0)             # restore f27
+    lw      f28, fr28*4(a0)             # restore f28
+    lw      f29, fr29*4(a0)             # restore f29
+    lw      f30, fr30*4(a0)             # restore f30
+    lw      f31, fr31*4(a0)             # restore f31
+#endif
+
+    lw      a0, r_A1*4(a0)              # restore a0
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_RETURN.S b/vm/compiler/template/mips/TEMPLATE_RETURN.S
new file mode 100644
index 0000000..e9cee05
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RETURN.S
@@ -0,0 +1,77 @@
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
diff --git a/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S b/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S
new file mode 100644
index 0000000..b4e0754
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_RETURN_PROF.S
@@ -0,0 +1,3 @@
+#define TEMPLATE_INLINE_PROFILING
+%include "mips/TEMPLATE_RETURN.S"
+#undef TEMPLATE_INLINE_PROFILING
diff --git a/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..2e74481
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,105 @@
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: a1 value to save
+     *    Top of stack + 0: a0 value to save
+     *    a0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    a1 - the value of regMap
+     *
+     * The handler must save regMap, r0-r31, f0-f31 if FPU, and then return with
+     * r0-r31 with their original values (note that this means a0 and a1 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     * Note: the following registers are not saved
+     *       zero, AT, gp, sp, fp, ra
+     */
+    add     a0, a0, rSELF               # pointer to heapArgSpace
+    sw      a1, 0(a0)                   # save regMap
+    add     a0, a0, 4                   # pointer to coreRegs
+#if 0
+    sw      zero, r_ZERO*4(a0)          # save zero
+#endif
+    .set noat
+    sw      AT, r_AT*4(a0)              # save at
+    .set at
+    sw      v0, r_V0*4(a0)              # save v0
+    sw      v1, r_V1*4(a0)              # save v1
+
+    lw      a1, 0(sp)                   # recover a0 value
+    sw      a1, r_A0*4(a0)              # save a0
+    lw      a1, 4(sp)                   # recover a1 value
+    sw      a1, r_A1*4(a0)              # save a1
+    sw      a2, r_A2*4(a0)              # save a2
+    sw      a3, r_A3*4(a0)              # save a3
+
+    sw      t0, r_T0*4(a0)              # save t0
+    sw      t1, r_T1*4(a0)              # save t1
+    sw      t2, r_T2*4(a0)              # save t2
+    sw      t3, r_T3*4(a0)              # save t3
+    sw      t4, r_T4*4(a0)              # save t4
+    sw      t5, r_T5*4(a0)              # save t5
+    sw      t6, r_T6*4(a0)              # save t6
+    sw      t7, r_T7*4(a0)              # save t7
+
+    sw      s0, r_S0*4(a0)              # save s0
+    sw      s1, r_S1*4(a0)              # save s1
+    sw      s2, r_S2*4(a0)              # save s2
+    sw      s3, r_S3*4(a0)              # save s3
+    sw      s4, r_S4*4(a0)              # save s4
+    sw      s5, r_S5*4(a0)              # save s5
+    sw      s6, r_S6*4(a0)              # save s6
+    sw      s7, r_S7*4(a0)              # save s7
+
+    sw      t8, r_T8*4(a0)              # save t8
+    sw      t9, r_T9*4(a0)              # save t9
+
+    sw      k0, r_K0*4(a0)              # save k0
+    sw      k1, r_K1*4(a0)              # save k1
+
+#if 0
+    sw      gp, r_GP*4(a0)              # save gp
+    sw      sp, r_SP*4(a0)              # save sp (need to adjust??? )
+    sw      fp, r_FP*4(a0)              # save fp
+    sw      ra, r_RA*4(a0)              # save ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    sw      f0, fr0*4(a0)               # save f0
+    sw      f1, fr1*4(a0)               # save f1
+    sw      f2, fr2*4(a0)               # save f2
+    sw      f3, fr3*4(a0)               # save f3
+    sw      f4, fr4*4(a0)               # save f4
+    sw      f5, fr5*4(a0)               # save f5
+    sw      f6, fr6*4(a0)               # save f6
+    sw      f7, fr7*4(a0)               # save f7
+    sw      f8, fr8*4(a0)               # save f8
+    sw      f9, fr9*4(a0)               # save f9
+    sw      f10, fr10*4(a0)             # save f10
+    sw      f11, fr11*4(a0)             # save f11
+    sw      f12, fr12*4(a0)             # save f12
+    sw      f13, fr13*4(a0)             # save f13
+    sw      f14, fr14*4(a0)             # save f14
+    sw      f15, fr15*4(a0)             # save f15
+    sw      f16, fr16*4(a0)             # save f16
+    sw      f17, fr17*4(a0)             # save f17
+    sw      f18, fr18*4(a0)             # save f18
+    sw      f19, fr19*4(a0)             # save f19
+    sw      f20, fr20*4(a0)             # save f20
+    sw      f21, fr21*4(a0)             # save f21
+    sw      f22, fr22*4(a0)             # save f22
+    sw      f23, fr23*4(a0)             # save f23
+    sw      f24, fr24*4(a0)             # save f24
+    sw      f25, fr25*4(a0)             # save f25
+    sw      f26, fr26*4(a0)             # save f26
+    sw      f27, fr27*4(a0)             # save f27
+    sw      f28, fr28*4(a0)             # save f28
+    sw      f29, fr29*4(a0)             # save f29
+    sw      f30, fr30*4(a0)             # save f30
+    sw      f31, fr31*4(a0)             # save f31
+#endif
+
+    lw      a1, 0(sp)                   # recover a0 value
+    lw      a1, 4(sp)                   # recover a1 value
+    sub     sp, sp, 8                   # adjust stack ptr
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S b/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S
new file mode 100644
index 0000000..b15f0c3
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SHL_LONG.S
@@ -0,0 +1,17 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sll     rRESULT0, rARG0, a2		#  rlo<- alo << (shift&31)
+    not     rRESULT1, a2		#  rhi<- 31-shift  (shift is 5b)
+    srl     rARG0, 1
+    srl     rARG0, rRESULT1		#  alo<- alo >> (32-(shift&31))
+    sll     rRESULT1, rARG1, a2		#  rhi<- ahi << (shift&31)
+    or      rRESULT1, rARG0		#  rhi<- rhi | alo
+    andi    a2, 0x20			#  shift< shift & 0x20
+    movn    rRESULT1, rRESULT0, a2	#  rhi<- rlo (if shift&0x20)
+    movn    rRESULT0, zero, a2		#  rlo<- 0  (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S b/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S
new file mode 100644
index 0000000..e59686d
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SHR_LONG.S
@@ -0,0 +1,18 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sra     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    sra     a3, rARG1, 31		#  a3<- sign(ah)
+    not     rARG0, a2			#  alo<- 31-shift (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, a3, a2		#  rhi<- sign(ahi) (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S
new file mode 100644
index 0000000..253a4a4
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SQRT_DOUBLE_VFP.S
@@ -0,0 +1,23 @@
+%verify "executed"
+
+    /*
+     * 64-bit floating point sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     a2 src addr of op1
+     * On exit:
+     *     v0,v1/fv0 = res
+     */
+#ifdef  SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)        # a0/a1<- vBB/vBB+1
+#else
+    LOAD64_F(fa0, fa0f, a2)         # fa0/fa0f<- vBB/vBB+1
+    sqrt.d	fv0, fa0
+    c.eq.d	fv0, fv0
+    bc1t	1f
+#endif
+    JAL(sqrt)
+1:
+    RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S
new file mode 100644
index 0000000..a514351
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_STRING_COMPARETO.S
@@ -0,0 +1,146 @@
+    /*
+     * String's compareTo.
+     *
+     * Requires a0/a1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   this object pointer
+     *    a1:   comp object pointer
+     *
+     */
+
+     subu  v0, a0, a1                # Same?
+     bnez  v0, 1f
+     RETURN
+1:
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_OFFSET(a1)
+     lw    t2, STRING_FIELDOFF_COUNT(a0)
+     lw    a2, STRING_FIELDOFF_COUNT(a1)
+     lw    a0, STRING_FIELDOFF_VALUE(a0)
+     lw    a1, STRING_FIELDOFF_VALUE(a1)
+
+    /*
+     * At this point, we have this/comp:
+     *    offset: t0/t1
+     *    count:  t2/a2
+     *    value:  a0/a1
+     * We're going to compute
+     *    a3 <- countDiff
+     *    a2 <- minCount
+     */
+     subu  a3, t2, a2                # a3<- countDiff
+     sleu  t7, t2, a2
+     movn  a2, t2, t7                # a2<- minCount
+
+     /*
+      * Note: data pointers point to first element.
+      */
+     addu  a0, 16                    # point to contents[0]
+     addu  a1, 16                    # point to contents[0]
+
+     /* Now, build pointers to the string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  a0, a0, t7
+     sll   t7, t1, 1                 # multiply offset by 2
+     addu  a1, a1, t7
+
+     /*
+      * At this point we have:
+      *   a0: *this string data
+      *   a1: *comp string data
+      *   a2: iteration count for comparison
+      *   a3: value to return if the first part of the string is equal
+      *   v0: reserved for result
+      *   t0-t5 available for loading string data
+      */
+
+     subu  a2, 2
+     bltz  a2, do_remainder2
+
+     /*
+      * Unroll the first two checks so we can quickly catch early mismatch
+      * on long strings (but preserve incoming alignment)
+      */
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     addu  a0, 4                     # offset to contents[2]
+     addu  a1, 4                     # offset to contents[2]
+     li    t7, 28
+     bgt   a2, t7, do_memcmp16
+     subu  a2, 3
+     bltz  a2, do_remainder
+
+loopback_triple:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     lhu   t4, 4(a0)
+     lhu   t5, 4(a1)
+     subu  v0, t4, t5
+     beqz  v0, 3f
+     RETURN
+3:
+     addu  a0, 6                     # offset to contents[i+3]
+     addu  a1, 6                     # offset to contents[i+3]
+     subu  a2, 3
+     bgez  a2, loopback_triple
+
+do_remainder:
+     addu  a2, 3
+     beqz  a2, returnDiff
+
+loopback_single:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     bnez  v0, 1f
+     addu  a0, 2                     # offset to contents[i+1]
+     addu  a1, 2                     # offset to contents[i+1]
+     subu  a2, 1
+     bnez  a2, loopback_single
+
+returnDiff:
+     move  v0, a3
+1:
+     RETURN
+
+do_remainder2:
+     addu  a2, 2
+     bnez  a2, loopback_single
+     move  v0, a3
+     RETURN
+
+    /* Long string case */
+do_memcmp16:
+     move  rOBJ, a3                  # save return value if strings are equal
+     JAL(__memcmp16)
+     seq   t0, v0, zero
+     movn  v0, rOBJ, t0              # overwrite return value if strings are equal
+     RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S b/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S
new file mode 100644
index 0000000..9d9cd60
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_STRING_INDEXOF.S
@@ -0,0 +1,111 @@
+    /*
+     * String's indexOf.
+     *
+     * Requires a0 to have been previously checked for null.  Will
+     * return index of match of a1 in v0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   string object pointer
+     *    a1:   char to match
+     *    a2:   Starting offset in string data
+     */
+
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_COUNT(a0)
+     lw    v0, STRING_FIELDOFF_VALUE(a0)
+
+    /*
+     * At this point, we have:
+     *    v0: object pointer
+     *    a1: char to match
+     *    a2: starting offset
+     *    t0: offset
+     *    t1: string length
+     */
+
+    /* Point to first element */
+     addu  v0, 16                    # point to contents[0]
+
+    /* Build pointer to start of string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Save a copy of starting data in v1 */
+     move  v1, v0
+
+    /* Clamp start to [0..count] */
+     slt   t7, a2, zero
+     movn  a2, zero, t7
+     sgt   t7, a2, t1
+     movn  a2, t1, t7
+
+    /* Build pointer to start of data to compare */
+     sll   t7, a2, 1                # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Compute iteration count */
+     subu  a3, t1, a2
+
+    /*
+     * At this point we have:
+     *   v0: start of data to test
+     *   a1: char to compare
+     *   a3: iteration count
+     *   v1: original start of string
+     *   t0-t7 available for loading string data
+     */
+     subu  a3, 4
+     bltz  a3, indexof_remainder
+
+indexof_loop4:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     lhu   t0, 2(v0)
+     beq   t0, a1, match_1
+     lhu   t0, 4(v0)
+     beq   t0, a1, match_2
+     lhu   t0, 6(v0)
+     beq   t0, a1, match_3
+     addu  v0, 8                     # offset to contents[i+4]
+     subu  a3, 4
+     bgez  a3, indexof_loop4
+
+indexof_remainder:
+     addu  a3, 4
+     beqz  a3, indexof_nomatch
+
+indexof_loop1:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     addu  v0, 2                     # offset to contents[i+1]
+     subu  a3, 1
+     bnez  a3, indexof_loop1
+
+indexof_nomatch:
+     li    v0, -1
+     RETURN
+
+match_0:
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_1:
+     addu  v0, 2
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_2:
+     addu  v0, 4
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_3:
+     addu  v0, 6
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
diff --git a/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S b/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S
new file mode 100644
index 0000000..b07bf44
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SUB_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinopWide.S" {"instr":"JAL(__subdf3)","instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S b/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S
new file mode 100644
index 0000000..b0333cd
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_SUB_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/fbinop.S" {"instr":"JAL(__subsf3)","instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S b/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S
new file mode 100644
index 0000000..d6e8b2e
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_THROW_EXCEPTION_COMMON.S
@@ -0,0 +1,6 @@
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    a0    Dalvik PC that raises the exception
+     */
+    j      .LhandleException
diff --git a/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S b/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S
new file mode 100644
index 0000000..f2a9ddb
--- /dev/null
+++ b/vm/compiler/template/mips/TEMPLATE_USHR_LONG.S
@@ -0,0 +1,17 @@
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    srl     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    not     rARG0, a2			#  alo<- 31-n  (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, zero, a2		#  rhi<- 0 (if shift&0x20)
+    RETURN
diff --git a/vm/compiler/template/mips/TemplateOpList.h b/vm/compiler/template/mips/TemplateOpList.h
new file mode 100644
index 0000000..6aabdba
--- /dev/null
+++ b/vm/compiler/template/mips/TemplateOpList.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(RESTORE_STATE)
+JIT_TEMPLATE(SAVE_STATE)
+JIT_TEMPLATE(PERIODIC_PROFILING)
+JIT_TEMPLATE(RETURN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN_PROF)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE_PROF)
diff --git a/vm/compiler/template/mips/fbinop.S b/vm/compiler/template/mips/fbinop.S
new file mode 100644
index 0000000..e9ccf0a
--- /dev/null
+++ b/vm/compiler/template/mips/fbinop.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if $chkzero
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+    $preinstr                           # optional op
+    $instr                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if $chkzero
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/fbinopWide.S b/vm/compiler/template/mips/fbinopWide.S
new file mode 100644
index 0000000..0e31b87
--- /dev/null
+++ b/vm/compiler/template/mips/fbinopWide.S
@@ -0,0 +1,45 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if $chkzero
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if $chkzero
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+    $preinstr                           # optional op
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/footer.S b/vm/compiler/template/mips/footer.S
new file mode 100644
index 0000000..436e76a
--- /dev/null
+++ b/vm/compiler/template/mips/footer.S
@@ -0,0 +1,140 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+.LinvokeNative:
+    # Prep for the native call
+    # a1 = newFP, a0 = methodToCall
+    lw     t9, offThread_jniLocal_topCookie(rSELF)  # t9<- thread->localRef->...
+    sw     zero, offThread_inJitCodeCache(rSELF)    # not in jit code cache
+    sw     a1, offThread_curFrame(rSELF)            # self->curFrame = newFp
+    sw     t9, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                 # newFp->localRefCookie=top
+    lhu     ra, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(rBIX, a1)                   # rBIX<- new stack save area
+
+    move    a2, a0                               # a2<- methodToCall
+    move    a0, a1                               # a0<- newFp
+    add     a1, rSELF, offThread_retval          # a1<- &retval
+    move    a3, rSELF                            # a3<- self
+    andi    ra, kSubModeMethodTrace
+    beqz    ra, 121f
+    # a2: methodToCall
+    # preserve a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                      # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a2 again
+    SCRATCH_LOAD(a2, 8)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+    b       212f
+
+121:
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                     # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+212:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, offStackSaveArea_savedPc(rBIX)        # reload rPC
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+
+/*
+ * On entry:
+ * a0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    la     t0, .LdeadFood
+    lw     t0, (t0)                  # should not see this under self-verification mode
+    jr     t0
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    sw     zero, offThread_inJitCodeCache(rSELF)  # in interpreter land
+    la     a1, .LdvmMterpCommonExceptionThrown  # PIC way of getting &func
+    lw     a1, (a1)
+    la     rIBASE, .LdvmAsmInstructionStart     # PIC way of getting &func
+    lw     rIBASE, (rIBASE)
+    move   rPC, a0                              # reload the faulting Dalvid address
+    jr     a1                                   # branch to dvmMterpCommonExeceptionThrown
+
+    .align  4
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/mips/funop.S b/vm/compiler/template/mips/funop.S
new file mode 100644
index 0000000..0a984d7
--- /dev/null
+++ b/vm/compiler/template/mips/funop.S
@@ -0,0 +1,30 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    $preinstr                           # optional op
+    $instr                              # v0<- op, a0-a3 changed
+.L${opcode}_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+.L${opcode}_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/funopNarrower.S b/vm/compiler/template/mips/funopNarrower.S
new file mode 100644
index 0000000..7fbaf7b
--- /dev/null
+++ b/vm/compiler/template/mips/funopNarrower.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "load":"LOAD64_F(fa0, fa0f, a1)"}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+    $preinstr                           # optional op
+    $instr                              # v0<- op, a0-a3 changed
+.L${opcode}_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    $load
+    $preinstr                           # optional op
+    $instr_f                            # fv0 = result
+.L${opcode}_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/funopWider.S b/vm/compiler/template/mips/funopWider.S
new file mode 100644
index 0000000..887e171
--- /dev/null
+++ b/vm/compiler/template/mips/funopWider.S
@@ -0,0 +1,29 @@
+%default {"preinstr":"", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+    $preinstr                           # optional op
+    $instr                              # result<- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+    $preinstr                           # optional op
+    $instr_f
+
+.L${opcode}_set_vreg:
+    $st_result                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
diff --git a/vm/compiler/template/mips/header.S b/vm/compiler/template/mips/header.S
new file mode 100644
index 0000000..39419f6
--- /dev/null
+++ b/vm/compiler/template/mips/header.S
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+#include "../../../mterp/common/mips-defines.h"
+#include "../../../mterp/common/jit-config.h"
+
+#ifdef	__mips_hard_float
+#define		HARD_FLOAT
+#else
+#define		SOFT_FLOAT
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		pointer to thread
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+/* register offsets */
+#define r_ZERO      0
+#define r_AT        1
+#define r_V0        2
+#define r_V1        3
+#define r_A0        4
+#define r_A1        5
+#define r_A2        6
+#define r_A3        7
+#define r_T0        8
+#define r_T1        9
+#define r_T2        10
+#define r_T3        11
+#define r_T4        12
+#define r_T5        13
+#define r_T6        14
+#define r_T7        15
+#define r_S0        16
+#define r_S1        17
+#define r_S2        18
+#define r_S3        19
+#define r_S4        20
+#define r_S5        21
+#define r_S6        22
+#define r_S7        23
+#define r_T8        24
+#define r_T9        25
+#define r_K0        26
+#define r_K1        27
+#define r_GP        28
+#define r_SP        29
+#define r_FP        30
+#define r_RA        31
+#define r_F0        32
+#define r_F1        33
+#define r_F2        34
+#define r_F3        35
+#define r_F4        36
+#define r_F5        37
+#define r_F6        38
+#define r_F7        39
+#define r_F8        40
+#define r_F9        41
+#define r_F10       42
+#define r_F11       43
+#define r_F12       44
+#define r_F13       45
+#define r_F14       46
+#define r_F15       47
+#define r_F16       48
+#define r_F17       49
+#define r_F18       50
+#define r_F19       51
+#define r_F20       52
+#define r_F21       53
+#define r_F22       54
+#define r_F23       55
+#define r_F24       56
+#define r_F25       57
+#define r_F26       58
+#define r_F27       59
+#define r_F28       60
+#define r_F29       61
+#define r_F30       62
+#define r_F31       63
+
+/* single-purpose registers, given names for clarity */
+#define rPC	s0
+#define rFP	s1
+#define rSELF	s2
+#define rIBASE	s3
+#define rINST	s4
+#define rOBJ	s5
+#define rBIX	s6
+#define rTEMP	s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0     a0
+#define rARG1     a1
+#define rARG2     a2
+#define rARG3     a3
+#define rRESULT0  v0
+#define rRESULT1  v1
+#else
+#define rARG0     a1
+#define rARG1     a0
+#define rARG2     a3
+#define rARG3     a2
+#define rRESULT0  v1
+#define rRESULT1  v0
+#endif
+
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()	lw	rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF()	sw	rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF()	lw	rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF()	sw	rFP, offThread_curFrame(rSELF)
+
+#define EXPORT_PC() \
+	sw	rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+	subu	rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST()			lhu	rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count)	lhu     rINST, (_count*2)(rPC); \
+					addu	rPC, rPC, (_count * 2)
+
+#define FETCH_ADVANCE_INST_RB(rd)	addu	rPC, rPC, rd;	\
+					lhu     rINST, (rPC)
+
+#define FETCH(rd, _count)		lhu	rd, (_count * 2)(rPC)
+#define FETCH_S(rd, _count)		lh	rd, (_count * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd)		and	rd, rINST, 0xFF
+
+#define GOTO_OPCODE(rd)			sll  rd, rd, ${handler_size_bits};	\
+					addu rd, rIBASE, rd;	\
+					jr  rd
+
+
+#define LOAD(rd, rbase)			lw  rd, 0(rbase)
+#define LOAD_F(rd, rbase)		l.s rd, (rbase)
+#define STORE(rd, rbase)		sw  rd, 0(rbase)
+#define STORE_F(rd, rbase)		s.s rd, (rbase)
+
+#define GET_VREG(rd, rix)		LOAD_eas2(rd,rFP,rix)
+
+#define GET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix)		STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst)	.set noreorder;		\
+					sll  dst, dst, ${handler_size_bits};	\
+					addu dst, rIBASE, dst;			\
+					sll  t8, rix, 2;	\
+					addu t8, t8, rFP;	\
+					jr  dst;		\
+					sw  rd, 0(t8);		\
+					.set reorder
+
+#define SET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  s.s	rd, (AT); .set at
+
+
+#define GET_OPA(rd)			srl     rd, rINST, 8
+#ifndef		MIPS32R2
+#define GET_OPA4(rd)			GET_OPA(rd);  and  rd, 0xf
+#else
+#define GET_OPA4(rd)			ext	rd, rd, 8, 4
+#endif
+#define GET_OPB(rd)			srl     rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd,off)		lw    rd, offThread_##off##(rSELF)
+
+#define LOAD_rSELF_method(rd)		LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd)	LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd)	LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd)		LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd)	LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd)		LOAD_rSELF_OFF(rd, bailPtr)
+
+#define GET_JIT_PROF_TABLE(rd)		LOAD_rSELF_OFF(rd,pJitProfTable)
+#define GET_JIT_THRESHOLD(rd)		LOAD_rSELF_OFF(rd,jitThreshold)
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd,rbase,roff,rshift)	.set noat;		\
+					sll  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define EAS1(rd,rbase,roff)		EASN(rd,rbase,roff,1)
+#define EAS2(rd,rbase,roff)		EASN(rd,rbase,roff,2)
+#define EAS3(rd,rbase,roff)		EASN(rd,rbase,roff,3)
+#define EAS4(rd,rbase,roff)		EASN(rd,rbase,roff,4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd,rbase,roff,rshift)	.set noat;		\
+					srl  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define LOAD_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  lw  rd, 0(AT); .set at
+
+#define STORE_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  sw  rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd,rbase,off)	lw	rd, off(rbase)
+#define LOADu2_RB_OFF(rd,rbase,off)	lhu	rd, off(rbase)
+#define STORE_RB_OFF(rd,rbase,off)	sw	rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, off(rbase);	\
+					        sw	rhi, (off+4)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, off(rbase);	\
+					        lw	rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, off(rbase);	\
+						s.s	rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, off(rbase);	\
+						l.s	rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, (off+4)(rbase);	\
+					        sw	rhi, (off)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, (off+4)(rbase);	\
+					        lw	rhi, (off)(rbase)
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, (off+4)(rbase);	\
+						s.s	rhi, (off)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, (off+4)(rbase);	\
+						l.s	rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo,rhi,rbase)		STORE64_off(rlo,rhi,rbase,0)
+#define LOAD64(rlo,rhi,rbase)		LOAD64_off(rlo,rhi,rbase,0)
+
+#define STORE64_F(rlo,rhi,rbase)	STORE64_off_F(rlo,rhi,rbase,0)
+#define LOAD64_F(rlo,rhi,rbase)		LOAD64_off_F(rlo,rhi,rbase,0)
+
+#define STORE64_lo(rd,rbase)		sw	rd, 0(rbase)
+#define STORE64_hi(rd,rbase)		sw	rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd,rbase)		LOAD_RB_OFF(rd,rbase,offThread_exception)
+#define LOAD_base_offArrayObject_length(rd,rbase)	LOAD_RB_OFF(rd,rbase,offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd,rbase)   LOAD_RB_OFF(rd,rbase,offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd,rbase)	LOAD_RB_OFF(rd,rbase,offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd,rbase)	LOAD_RB_OFF(rd,rbase,offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_name)
+#define LOAD_base_offObject_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd,rbase)		LOADu2_RB_OFF(rd,rbase,offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd,rbase)		STORE_RB_OFF(rd,rbase,offThread_exception)
+
+
+#define	STACK_STORE(rd,off)	sw   rd, off(sp)
+#define	STACK_LOAD(rd,off)	lw   rd, off(sp)
+#define CREATE_STACK(n)	 	subu sp, sp, n
+#define DELETE_STACK(n)	 	addu sp, sp, n
+
+#define SAVE_RA(offset)	 	STACK_STORE(ra, offset)
+#define LOAD_RA(offset)	 	STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest,addr)	la   dest, addr
+#define LOAD_IMM(dest, imm)	li   dest, imm
+#define MOVE_REG(dest,src)	move dest, src
+#define	RETURN			jr   ra
+#define	STACK_SIZE		128
+
+#define STACK_OFFSET_ARG04	16
+#define STACK_OFFSET_GP		84
+#define STACK_OFFSET_rFP	112
+
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+        .cprestore STACK_OFFSET_GP
+
+#define JAL(func)		move rTEMP, ra;				\
+				jal  func;				\
+				move ra, rTEMP
+
+#define JALR(reg)		move rTEMP, ra;				\
+				jalr ra, reg;				\
+				move ra, rTEMP
+
+#define BAL(n)			bal  n
+
+#define	STACK_STORE_RA()  	CREATE_STACK(STACK_SIZE);		\
+				STACK_STORE(gp, STACK_OFFSET_GP);	\
+				STACK_STORE(ra, 124)
+
+#define	STACK_STORE_S0()  	STACK_STORE_RA();			\
+				STACK_STORE(s0, 116)
+
+#define	STACK_STORE_S0S1()  	STACK_STORE_S0();			\
+				STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define	STACK_LOAD_RA()		STACK_LOAD(ra, 124);			\
+				STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				DELETE_STACK(STACK_SIZE)
+
+#define	STACK_LOAD_S0()  	STACK_LOAD(s0, 116);			\
+				STACK_LOAD_RA()
+
+#define	STACK_LOAD_S0S1()  	STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD_S0()
+
+#define STACK_STORE_FULL()	CREATE_STACK(STACK_SIZE);	\
+				STACK_STORE(ra, 124);		\
+				STACK_STORE(fp, 120);		\
+				STACK_STORE(s0, 116);		\
+				STACK_STORE(s1, STACK_OFFSET_rFP);	\
+				STACK_STORE(s2, 108);		\
+				STACK_STORE(s3, 104);		\
+				STACK_STORE(s4, 100);		\
+				STACK_STORE(s5, 96);		\
+				STACK_STORE(s6, 92);		\
+				STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL()	STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				STACK_LOAD(s7, 88);	\
+				STACK_LOAD(s6, 92);	\
+				STACK_LOAD(s5, 96);	\
+				STACK_LOAD(s4, 100);	\
+				STACK_LOAD(s3, 104);	\
+				STACK_LOAD(s2, 108);	\
+				STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD(s0, 116);	\
+				STACK_LOAD(fp, 120);	\
+				STACK_LOAD(ra, 124);	\
+				DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define STACK_OFFSET_SCR   32
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
diff --git a/vm/compiler/template/mips/platform.S b/vm/compiler/template/mips/platform.S
new file mode 100644
index 0000000..8b0d23e
--- /dev/null
+++ b/vm/compiler/template/mips/platform.S
@@ -0,0 +1,6 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
new file mode 100644
index 0000000..d4578bc
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -0,0 +1,1981 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, r9
+    add     r1, r2, r10                 @  r1<- r10 + low(ZxW + (YxX))
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     fadds   s2, s0, s1
+     fsts    s2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     fsubs   s2, s0, s1
+     fsts    s2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     fmuls   s2, s0, s1
+     fsts    s2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     flds    s0,[r1]
+     flds    s1,[r2]
+     fdivs   s2, s0, s1
+     fsts    s2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     faddd   d2, d0, d1
+     fstd    d2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     fsubd   d2, d0, d1
+     fstd    d2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     fmuld   d2, d0, d1
+     fstd    d2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit floating point operation.  Provide an "instr" line that
+     * specifies an instruction that performs s2 = s0 op s1.
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = op1 address
+     *     r2 = op2 address
+     */
+     fldd    d0,[r1]
+     fldd    d1,[r2]
+     fdivd   d2, d0, d1
+     fstd    d2,[r0]
+     bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    fldd    d0, [r1]                    @ d0<- vB
+    fcvtsd  s0, d0                              @ s0<- op d0
+    fsts    s0, [r0]                    @ vA<- s0
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    fldd    d0, [r1]                    @ d0<- vB
+    ftosizd  s0, d0                              @ s0<- op d0
+    fsts    s0, [r0]                    @ vA<- s0
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    fcvtds  d0, s0                              @ d0<- op s0
+    fstd    d0, [r0]                    @ vA<- d0
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+    /*
+     * Generic 32bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s1 = op s0".
+     *
+     * For: float-to-int, int-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    ftosizs s1, s0                              @ s1<- op s0
+    fsts    s1, [r0]                    @ vA<- s1
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    fsitod  d0, s0                              @ d0<- op s0
+    fstd    d0, [r0]                    @ vA<- d0
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+    /*
+     * Generic 32bit-to-32bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s1 = op s0".
+     *
+     * For: float-to-int, int-to-float
+     *
+     * On entry:
+     *     r0 = target dalvik register address
+     *     r1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    flds    s0, [r1]                    @ s0<- vB
+    fsitos  s1, s0                              @ s1<- op s0
+    fsts    s1, [r0]                    @ vA<- s1
+    bx      lr
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     *
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmpd  d0, d1                       @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    fldd    d0, [r0]                    @ d0<- vBB
+    fldd    d1, [r1]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mov     r0, #1                      @ r0<- 1 (default)
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     * On entry:
+     *    r0 = &op1 [vBB]
+     *    r1 = &op2 [vCC]
+     */
+    /* op vAA, vBB, vCC */
+    flds    s0, [r0]                    @ d0<- vBB
+    flds    s1, [r1]                    @ d1<- vCC
+    fcmps  s0, s1                      @ compare (vBB, vCC)
+    mvn     r0, #0                      @ r0<- -1 (default)
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r0<- 1
+    moveq   r0, #0                      @ (equal) r0<- 0
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S */
+    /*
+     * 64-bit floating point vfp sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     r2 src addr of op1
+     * On exit:
+     *     r0,r1 = res
+     */
+    fldd    d0, [r2]
+    fsqrtd  d1, d0
+    fcmpd   d1, d1
+    fmstat
+    fmrrd   r0, r1, d1
+    bxeq    lr   @ Result OK - return
+    ldr     r2, .Lsqrt
+    fmrrd   r0, r1, d0   @ reload orig operand
+    bx      r2   @ tail call to sqrt library routine
+
+.Lsqrt:
+    .word   sqrt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    r0    Dalvik PC that raises the exception
+     */
+    b       .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    vpush   {d0-d15}                    @ save out all fp registers
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    r3, [r0, #STRING_FIELDOFF_VALUE]
+    ldr    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+
+
+    /*
+     * At this point, we have:
+     *    r1: char to match
+     *    r2: starting offset
+     *    r3: object pointer (final result -> r0)
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r3, #16
+     add   r0, r3, r7, lsl #1
+
+     /* Save a copy of starting data in r7 */
+     mov   r7, r0
+
+     /* Clamp start to [0..count] */
+     cmp   r2, #0
+     movlt r2, #0
+     cmp   r2, r8
+     movgt r2, r8
+
+     /* Build pointer to start of data to compare and pre-bias */
+     add   r0, r0, r2, lsl #1
+     sub   r0, #2
+
+     /* Compute iteration count */
+     sub   r8, r2
+
+     /*
+      * At this point we have:
+      *   r0: start of data to test
+      *   r1: chat to compare
+      *   r8: iteration count
+      *   r7: original start of string
+      *   r3, r4, r9, r10, r11, r12 available for loading string data
+      */
+
+    subs  r8, #4
+    blt   indexof_remainder
+
+indexof_loop4:
+    ldrh  r3, [r0, #2]!
+    ldrh  r4, [r0, #2]!
+    ldrh  r10, [r0, #2]!
+    ldrh  r11, [r0, #2]!
+    cmp   r3, r1
+    beq   match_0
+    cmp   r4, r1
+    beq   match_1
+    cmp   r10, r1
+    beq   match_2
+    cmp   r11, r1
+    beq   match_3
+    subs  r8, #4
+    bge   indexof_loop4
+
+indexof_remainder:
+    adds    r8, #4
+    beq     indexof_nomatch
+
+indexof_loop1:
+    ldrh  r3, [r0, #2]!
+    cmp   r3, r1
+    beq   match_3
+    subs  r8, #1
+    bne   indexof_loop1
+
+indexof_nomatch:
+    mov   r0, #-1
+    bx    lr
+
+match_0:
+    sub   r0, #6
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_1:
+    sub   r0, #4
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_2:
+    sub   r0, #2
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_3:
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
new file mode 100644
index 0000000..3efcdd6
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -0,0 +1,1712 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_DOUBLE
+dvmCompiler_TEMPLATE_CMPG_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPG_DOUBLE.S */
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .LTEMPLATE_CMPG_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPG_DOUBLE_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mov     r0, #1                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_DOUBLE
+dvmCompiler_TEMPLATE_CMPL_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    push    {r0-r3}                     @ save operands
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cdcmple       @ PIC way of "bl __aeabi_cdcmple"
+    blx     ip
+    bhi     .LTEMPLATE_CMPL_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0, trumps less than
+    add     sp, #16                     @ drop unused operands
+    bx      r11
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPL_DOUBLE_gt_or_nan:
+    pop     {r2-r3}                     @ restore operands in reverse order
+    pop     {r0-r1}                     @ restore operands in reverse order
+    ldr     ip, .L__aeabi_cdcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mvn     r0, #0                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_FLOAT
+dvmCompiler_TEMPLATE_CMPG_FLOAT:
+/* File: armv5te/TEMPLATE_CMPG_FLOAT.S */
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .LTEMPLATE_CMPG_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPG_FLOAT_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mov     r0, #1                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_FLOAT
+dvmCompiler_TEMPLATE_CMPL_FLOAT:
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+    /*
+     * For the JIT: incoming arguments in r0-r1, r2-r3
+     *              result in r0
+     *
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    mov     r9, r0                      @ Save copies - we may need to redo
+    mov     r10, r1
+    mov     r11, lr                     @ save return address
+    ldr     ip, .L__aeabi_cfcmple       @ cmp <=: C clear if <, Z set if eq
+    blx     ip
+    bhi     .LTEMPLATE_CMPL_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r0, #0                      @ (less than) r0<- -1
+    moveq   r0, #0                      @ (equal) r0<- 0, trumps less than
+    bx      r11
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LTEMPLATE_CMPL_FLOAT_gt_or_nan:
+    mov     r0, r10                     @ restore in reverse order
+    mov     r1, r9
+    ldr     ip, .L__aeabi_cfcmple       @ r0<- Z set if eq, C clear if <
+    blx     ip
+    movcc   r0, #1                      @ (greater than) r1<- 1
+    bxcc    r11
+    mvn     r0, #0                            @ r1<- 1 or -1 for NaN
+    bx      r11
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, r9
+    add     r1, r2, r10                 @  r1<- r10 + low(ZxW + (YxX))
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    r0    Dalvik PC that raises the exception
+     */
+    b       .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    r3, [r0, #STRING_FIELDOFF_VALUE]
+    ldr    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+
+
+    /*
+     * At this point, we have:
+     *    r1: char to match
+     *    r2: starting offset
+     *    r3: object pointer (final result -> r0)
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r3, #16
+     add   r0, r3, r7, lsl #1
+
+     /* Save a copy of starting data in r7 */
+     mov   r7, r0
+
+     /* Clamp start to [0..count] */
+     cmp   r2, #0
+     movlt r2, #0
+     cmp   r2, r8
+     movgt r2, r8
+
+     /* Build pointer to start of data to compare and pre-bias */
+     add   r0, r0, r2, lsl #1
+     sub   r0, #2
+
+     /* Compute iteration count */
+     sub   r8, r2
+
+     /*
+      * At this point we have:
+      *   r0: start of data to test
+      *   r1: chat to compare
+      *   r8: iteration count
+      *   r7: original start of string
+      *   r3, r4, r9, r10, r11, r12 available for loading string data
+      */
+
+    subs  r8, #4
+    blt   indexof_remainder
+
+indexof_loop4:
+    ldrh  r3, [r0, #2]!
+    ldrh  r4, [r0, #2]!
+    ldrh  r10, [r0, #2]!
+    ldrh  r11, [r0, #2]!
+    cmp   r3, r1
+    beq   match_0
+    cmp   r4, r1
+    beq   match_1
+    cmp   r10, r1
+    beq   match_2
+    cmp   r11, r1
+    beq   match_3
+    subs  r8, #4
+    bge   indexof_loop4
+
+indexof_remainder:
+    adds    r8, #4
+    beq     indexof_nomatch
+
+indexof_loop1:
+    ldrh  r3, [r0, #2]!
+    cmp   r3, r1
+    beq   match_3
+    subs  r8, #1
+    bne   indexof_loop1
+
+indexof_nomatch:
+    mov   r0, #-1
+    bx    lr
+
+match_0:
+    sub   r0, #6
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_1:
+    sub   r0, #4
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_2:
+    sub   r0, #2
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_3:
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
new file mode 100644
index 0000000..4b2c15c
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
@@ -0,0 +1,1502 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, r9
+    add     r1, r2, r10                 @  r1<- r10 + low(ZxW + (YxX))
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    r0    Dalvik PC that raises the exception
+     */
+    b       .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    vpush   {d0-d15}                    @ save out all fp registers
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    r3, [r0, #STRING_FIELDOFF_VALUE]
+    ldr    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+
+
+    /*
+     * At this point, we have:
+     *    r1: char to match
+     *    r2: starting offset
+     *    r3: object pointer (final result -> r0)
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r3, #16
+     add   r0, r3, r7, lsl #1
+
+     /* Save a copy of starting data in r7 */
+     mov   r7, r0
+
+     /* Clamp start to [0..count] */
+     cmp   r2, #0
+     movlt r2, #0
+     cmp   r2, r8
+     movgt r2, r8
+
+     /* Build pointer to start of data to compare and pre-bias */
+     add   r0, r0, r2, lsl #1
+     sub   r0, #2
+
+     /* Compute iteration count */
+     sub   r8, r2
+
+     /*
+      * At this point we have:
+      *   r0: start of data to test
+      *   r1: chat to compare
+      *   r8: iteration count
+      *   r7: original start of string
+      *   r3, r4, r9, r10, r11, r12 available for loading string data
+      */
+
+    subs  r8, #4
+    blt   indexof_remainder
+
+indexof_loop4:
+    ldrh  r3, [r0, #2]!
+    ldrh  r4, [r0, #2]!
+    ldrh  r10, [r0, #2]!
+    ldrh  r11, [r0, #2]!
+    cmp   r3, r1
+    beq   match_0
+    cmp   r4, r1
+    beq   match_1
+    cmp   r10, r1
+    beq   match_2
+    cmp   r11, r1
+    beq   match_3
+    subs  r8, #4
+    bge   indexof_loop4
+
+indexof_remainder:
+    adds    r8, #4
+    beq     indexof_nomatch
+
+indexof_loop1:
+    ldrh  r3, [r0, #2]!
+    cmp   r3, r1
+    beq   match_3
+    subs  r8, #1
+    bne   indexof_loop1
+
+indexof_nomatch:
+    mov   r0, #-1
+    bx    lr
+
+match_0:
+    sub   r0, #6
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_1:
+    sub   r0, #4
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_2:
+    sub   r0, #2
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_3:
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
new file mode 100644
index 0000000..9f85e1f
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -0,0 +1,1502 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     thread pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LTEMPLATE_CMP_LONG_less            @ signed compare on high part
+    bgt     .LTEMPLATE_CMP_LONG_greater
+    subs    r0, r0, r2                  @ r0<- r0 - r2
+    bxeq     lr
+    bhi     .LTEMPLATE_CMP_LONG_greater         @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+    mvn     r0, #0                      @ r0<- -1
+    bx      lr
+.LTEMPLATE_CMP_LONG_greater:
+    mov     r0, #1                      @ r0<- 1
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChain   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, r9
+    add     r1, r2, r10                 @  r1<- r10 + low(ZxW + (YxX))
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    bx      lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    r0    Dalvik PC that raises the exception
+     */
+    b       .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+    vpush   {d0-d15}                    @ save out all fp registers
+    push    {r0-r12,lr}                 @ save out all registers
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    blx     r2                          @ decode and handle the mem op
+    pop     {r0-r12,lr}                 @ restore all registers
+    vpop    {d0-d15}                    @ restore all fp registers
+    bx      lr                          @ return to compiled code
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires r0/r1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   this object pointer
+     *    r1:   comp object pointer
+     *
+     */
+
+    mov    r2, r0         @ this to r2, opening up r0 for return value
+    subs   r0, r2, r1     @ Same?
+    bxeq   lr
+
+    ldr    r4, [r2, #STRING_FIELDOFF_OFFSET]
+    ldr    r9, [r1, #STRING_FIELDOFF_OFFSET]
+    ldr    r7, [r2, #STRING_FIELDOFF_COUNT]
+    ldr    r10, [r1, #STRING_FIELDOFF_COUNT]
+    ldr    r2, [r2, #STRING_FIELDOFF_VALUE]
+    ldr    r1, [r1, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    value:  r2/r1
+     *    offset: r4/r9
+     *    count:  r7/r10
+     * We're going to compute
+     *    r11 <- countDiff
+     *    r10 <- minCount
+     */
+     subs  r11, r7, r10
+     movls r10, r7
+
+     /* Now, build pointers to the string data */
+     add   r2, r2, r4, lsl #1
+     add   r1, r1, r9, lsl #1
+     /*
+      * Note: data pointers point to previous element so we can use pre-index
+      * mode with base writeback.
+      */
+     add   r2, #16-2   @ offset to contents[-1]
+     add   r1, #16-2   @ offset to contents[-1]
+
+     /*
+      * At this point we have:
+      *   r2: *this string data
+      *   r1: *comp string data
+      *   r10: iteration count for comparison
+      *   r11: value to return if the first part of the string is equal
+      *   r0: reserved for result
+      *   r3, r4, r7, r8, r9, r12 available for loading string data
+      */
+
+    subs  r10, #2
+    blt   do_remainder2
+
+      /*
+       * Unroll the first two checks so we can quickly catch early mismatch
+       * on long strings (but preserve incoming alignment)
+       */
+
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    bxne  lr
+    cmp   r10, #28
+    bgt   do_memcmp16
+    subs  r10, #3
+    blt   do_remainder
+
+loopback_triple:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    ldrh  r7, [r2, #2]!
+    ldrh  r8, [r1, #2]!
+    ldrh  r9, [r2, #2]!
+    ldrh  r12,[r1, #2]!
+    subs  r0, r3, r4
+    subeqs  r0, r7, r8
+    subeqs  r0, r9, r12
+    bxne  lr
+    subs  r10, #3
+    bge   loopback_triple
+
+do_remainder:
+    adds  r10, #3
+    beq   returnDiff
+
+loopback_single:
+    ldrh  r3, [r2, #2]!
+    ldrh  r4, [r1, #2]!
+    subs  r0, r3, r4
+    bxne  lr
+    subs  r10, #1
+    bne     loopback_single
+
+returnDiff:
+    mov   r0, r11
+    bx    lr
+
+do_remainder2:
+    adds  r10, #2
+    bne   loopback_single
+    mov   r0, r11
+    bx    lr
+
+    /* Long string case */
+do_memcmp16:
+    mov   r4, lr
+    ldr   lr, .Lmemcmp16
+    mov   r7, r11
+    add   r0, r2, #2
+    add   r1, r1, #2
+    mov   r2, r10
+    blx   lr
+    cmp   r0, #0
+    bxne  r4
+    mov   r0, r7
+    bx    r4
+
+.Lmemcmp16:
+    .word __memcmp16
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires r0 to have been previously checked for null.  Will
+     * return index of match of r1 in r0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    r0:   string object pointer
+     *    r1:   char to match
+     *    r2:   Starting offset in string data
+     */
+
+    ldr    r3, [r0, #STRING_FIELDOFF_VALUE]
+    ldr    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+
+
+    /*
+     * At this point, we have:
+     *    r1: char to match
+     *    r2: starting offset
+     *    r3: object pointer (final result -> r0)
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r3, #16
+     add   r0, r3, r7, lsl #1
+
+     /* Save a copy of starting data in r7 */
+     mov   r7, r0
+
+     /* Clamp start to [0..count] */
+     cmp   r2, #0
+     movlt r2, #0
+     cmp   r2, r8
+     movgt r2, r8
+
+     /* Build pointer to start of data to compare and pre-bias */
+     add   r0, r0, r2, lsl #1
+     sub   r0, #2
+
+     /* Compute iteration count */
+     sub   r8, r2
+
+     /*
+      * At this point we have:
+      *   r0: start of data to test
+      *   r1: chat to compare
+      *   r8: iteration count
+      *   r7: original start of string
+      *   r3, r4, r9, r10, r11, r12 available for loading string data
+      */
+
+    subs  r8, #4
+    blt   indexof_remainder
+
+indexof_loop4:
+    ldrh  r3, [r0, #2]!
+    ldrh  r4, [r0, #2]!
+    ldrh  r10, [r0, #2]!
+    ldrh  r11, [r0, #2]!
+    cmp   r3, r1
+    beq   match_0
+    cmp   r4, r1
+    beq   match_1
+    cmp   r10, r1
+    beq   match_2
+    cmp   r11, r1
+    beq   match_3
+    subs  r8, #4
+    bge   indexof_loop4
+
+indexof_remainder:
+    adds    r8, #4
+    beq     indexof_nomatch
+
+indexof_loop1:
+    ldrh  r3, [r0, #2]!
+    cmp   r3, r1
+    beq   match_3
+    subs  r8, #1
+    bne   indexof_loop1
+
+indexof_nomatch:
+    mov   r0, #-1
+    bx    lr
+
+match_0:
+    sub   r0, #6
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_1:
+    sub   r0, #4
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_2:
+    sub   r0, #2
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+match_3:
+    sub   r0, r7
+    asr   r0, r0, #1
+    bx    lr
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC -3.
+     * On entry:
+     *    lr - if NULL:
+     *        r1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [lr, #3] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     101f
+    ldr     r1,[lr, #3]
+101:
+#else
+    ldrne   r1,[lr, #3]
+#endif
+    ldr     r2, .LinterpPunt
+    mov     r0, r1                       @ set Dalvik PC
+    bx      r2
+    @ doesn't return
+
+.LinterpPunt:
+    .word   dvmJitToInterpPunt
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2                           @ dvmLockObject(self, obj)
+    ldr     r2, .LdvmJitToInterpNoChain
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    bx      r2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    r0 - self pointer
+     *    r1 - the object (which has already been null-checked by the caller
+     *    r4 - the Dalvik PC of the following instruction.
+     *
+     */
+    ldr     r2, .LdvmLockObject
+    mov     r3, #0                       @ Record that we're not returning
+    str     r3, [r0, #offThread_inJitCodeCache]
+    blx     r2             @ dvmLockObject(self, obj)
+    @ test for exception
+    ldr     r1, [rSELF, #offThread_exception]
+    cmp     r1, #0
+    beq     1f
+    ldr     r2, .LhandleException
+    sub     r0, r4, #2     @ roll dPC back to this monitor instruction
+    bx      r2
+1:
+    @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kHeavyweightMonitor
+#endif
+    ldr     pc, .LdvmJitToInterpNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (lr-11) is address of pointer to counter.  Note: the counter
+     *    actually exists 10 bytes before the return target, but because
+     *    we are arriving from thumb mode, lr will have its low bit set.
+     */
+     ldr    r0, [lr,#-11]
+     ldr    r1, [rSELF, #offThread_pProfileCountdown]
+     ldr    r2, [r0]                    @ get counter
+     ldr    r3, [r1]                    @ get countdown timer
+     add    r2, #1
+     subs   r2, #1
+     blt    .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     str    r2, [r0]
+     str    r3, [r1]
+     bx     lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     mov    r4, lr                     @ preserve lr
+     ldr    r0, .LdvmJitTraceProfilingOff
+     blx    r0
+     bx     r4
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: armv5te/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve live registers
+    mov     r0, r6
+    @ r0=rSELF
+    ldr     ip, .LdvmFastMethodTraceExit
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore live registers
+#endif
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     1f                          @ bail to interpreter
+#else
+    blxeq   lr                          @ punt to interpreter and compare state
+#endif
+    ldr     r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+    mov     rFP, r10                    @ publish new FP
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rSELF, #offThread_methodClassDex]
+    cmp     r8, #0                      @ check the break flags
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [rSELF, #offThread_inJitCodeCache] @ in code cache or not
+    cmp     r9, #0                      @ chaining cell exists?
+    blxne   r9                          @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1                      @ callsite is interpreted
+1:
+    mov     r0, #0
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ reset inJitCodeCache
+    stmia   rSELF, {rPC, rFP}           @ SAVE_PC_FP_TO_SELF()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r0, rSELF                   @ Expecting rSELF in r0
+    blx     r2                          @ exit the interpreter
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags] @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    ldr     r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    lr                          @ bail to the interpreter
+    tst     r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     .LinvokeNative
+#else
+    bxne    lr                          @ bail to the interpreter
+#endif
+
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r3}                    @ preserve r0-r3
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                    @ restore r0-r3
+#endif
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    bx      r10                         @ dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    @ r0 = methodToCall, r1 = returnCell, r2 = methodToCall->outsSize
+    @ rPC = dalvikCallsite, r7 = methodToCall->registersSize
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    add     r12, lr, #2                 @ setup the punt-to-interp address
+    sub     r10, r10, r2, lsl #2        @ r10<- bottom (newsave - outsSize)
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    r12                         @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    ldr     r9, [r0, #offMethod_clazz]      @ r9<- method->clazz
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+
+    @ Update "thread" values for the new method
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    stmfd   sp!, {r0-r2,lr}             @ preserve clobbered live registers
+    mov     r1, r6
+    @ r0=methodToCall, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r2,lr}             @ restore registers
+#endif
+
+    bx      lr                              @ return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr  : to branch to the chaining cell
+     *    - lr+2: to punt to the interpreter
+     *    - lr+4: to fully resolve the callee and may rechain.
+     *            r3 <- class
+     *            r9 <- counter
+     */
+    @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+    ldr     r3, [r0, #offObject_clazz]  @ r3 <- this->class
+    ldr     r8, [r2, #4]    @ r8 <- predictedChainCell->clazz
+    ldr     r0, [r2, #8]    @ r0 <- predictedChainCell->method
+    ldr     r9, [rSELF, #offThread_icRechainCount] @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    bne     101f
+    ldr     r10, [r7, #0]
+101:
+#else
+    ldreq   r10, [r7, #0]
+#endif
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    ldreqh  r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldreqh  r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    beq     .LinvokeChainProf   @ predicted chain is valid
+    ldr     r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+    cmp     r8, #0          @ initialized class or not
+    moveq   r1, #0
+    subne   r1, r9, #1      @ count--
+    strne   r1, [rSELF, #offThread_icRechainCount]  @ write back to thread
+    add     lr, lr, #4      @ return to fully-resolve landing pad
+    /*
+     * r1 <- count
+     * r2 <- &predictedChainCell
+     * r3 <- this->class
+     * r4 <- dPC
+     * r7 <- this->class->vtable
+     */
+    bx      lr
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    @ r7 = methodToCall->registersSize
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    ldrb    r8, [rSELF, #offThread_breakFlags]        @ r8<- breakFlags
+    add     r3, r1, #1  @ Thumb addr is odd
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r7, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- stack save area
+    cmp     r10, r9                     @ bottom < interpStackEnd?
+    bxlo    lr                          @ return to raise stack overflow excep.
+    @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+    str     rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+    str     rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ breakFlags != 0
+    ldr     r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    lr                          @ bail to the interpreter
+#else
+    bx      lr                          @ bail to interpreter unconditionally
+#endif
+
+    @ go ahead and transfer control to the native code
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    mov     r2, #0
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                        @ arg2<- methodToCall
+    mov     r0, r1                        @ arg0<- newFP
+    add     r1, rSELF, #offThread_retval  @ arg1<- &retval
+    mov     r3, rSELF                     @ arg3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    @ r2=methodToCall, r6=rSELF
+    stmfd   sp!, {r2,r6}                @ to be consumed after JNI return
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    mov     r0, r2
+    mov     r1, r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+#endif
+
+    blx     r8                          @ off to the native code
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    ldmfd   sp!, {r0-r1}                @ restore r2 and r6
+    @ r0=JNIMethod, r1=rSELF
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+#endif
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the mode properly
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    mov     pc, r1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    mov     r2, #0
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+    ands    lr, #kSubModeMethodTrace
+    beq     121f                        @ hop if not profiling
+    @ r2: methodToCall, r6: rSELF
+    stmfd   sp!, {r2,r6}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r2
+    mov     r1, r6
+    ldr     ip, .LdvmFastMethodTraceEnter
+    blx     ip
+    ldmfd   sp!, {r0-r3}
+
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+
+    ldmfd   sp!, {r0-r1}
+    ldr     ip, .LdvmFastNativeMethodTraceExit
+    blx     ip
+    b       212f
+121:
+    ldr     ip, [r2, #offMethod_nativeFunc]
+    blx     ip
+212:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ set the new mode
+    cmp     r2, #0                      @ return chaining cell still exists?
+    bxne    r2                          @ yes - go ahead
+
+    @ continue executing the next instruction through the interpreter
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+    add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kCallsiteInterpreted
+#endif
+    bx      r1
+
+/*
+ * On entry:
+ * r0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    ldr     pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    mov     r2, #0
+    str     r2, [rSELF, #offThread_inJitCodeCache] @ in interpreter land
+    ldr     r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+    ldr     rIBASE, .LdvmAsmInstructionStart    @ same as above
+    mov     rPC, r0                 @ reload the faulting Dalvik address
+    bx      r1                  @ branch to dvmMterpCommonExceptionThrown
+
+    .align  2
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+.LdvmJitTraceProfilingOff:
+    .word   dvmJitTraceProfilingOff
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.LdvmFastMethodTraceEnter:
+    .word   dvmFastMethodTraceEnter
+.LdvmFastNativeMethodTraceExit:
+    .word   dvmFastNativeMethodTraceExit
+.LdvmFastMethodTraceExit:
+    .word   dvmFastMethodTraceExit
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-ia32.S b/vm/compiler/template/out/CompilerTemplateAsm-ia32.S
new file mode 100644
index 0000000..267b2a7
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-ia32.S
@@ -0,0 +1,113 @@
+/*
+ * This file was generated automatically by gen-template.py for 'ia32'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: ia32/header.S */
+/*
+ * 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.
+ */
+
+#if defined(WITH_JIT)
+
+/* Subset of defines from mterp/x86/header.S */
+#define rSELF (%ebp)
+#define rPC   %esi
+#define rFP   %edi
+#define rINST %ebx
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: ia32/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: ia32/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler is a bit odd - it may be called via chaining or
+     * from static code and is expected to cause control to flow
+     * to the interpreter.  The problem is where to find the Dalvik
+     * PC of the next instruction.  When called via chaining, the dPC
+     * will be located at *rp.  When called from static code, rPC is
+     * valid and rp is a real return pointer (that should be ignored).
+     * The Arm target deals with this by using the link register as
+     * a flag.  If it is zero, we know we were called from static code.
+     * If non-zero, it points to the chain cell containing dPC.
+     * For x86, we'll infer the source by looking where rp points.
+     * If it points to anywhere within the code cache, we'll assume
+     * we got here via chaining.  Otherwise, we'll assume rPC is valid.
+     *
+     * On entry:
+     *    (TOS)<- return pointer or pointer to dPC
+     */
+
+/*
+ * FIXME - this won't work as-is.  The cache boundaries are not
+ * set up until later.  Perhaps rething this whole thing.  Do we
+ * really need an interpret teplate?
+ */
+
+
+     movl   rSELF,%ecx
+     movl   $.LinterpPunt,%edx
+     pop    %eax
+     /*cmpl   %eax,offThread_jitCacheEnd(%ecx)*/
+     ja     1f
+     /*cmpl   %eax,offThread_jitCacheStart(%ecx)*/
+     jb     1f
+     movl   %eax,rPC
+1:
+     jmp    *(%edx)
+
+.LinterpPunt:
+    .long   dvmJitToInterpPunt
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: ia32/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-mips.S b/vm/compiler/template/out/CompilerTemplateAsm-mips.S
new file mode 100644
index 0000000..4daf5ce
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-mips.S
@@ -0,0 +1,3405 @@
+/*
+ * This file was generated automatically by gen-template.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: mips/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+#include "../../../mterp/common/mips-defines.h"
+#include "../../../mterp/common/jit-config.h"
+
+#ifdef	__mips_hard_float
+#define		HARD_FLOAT
+#else
+#define		SOFT_FLOAT
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		pointer to thread
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+/* register offsets */
+#define r_ZERO      0
+#define r_AT        1
+#define r_V0        2
+#define r_V1        3
+#define r_A0        4
+#define r_A1        5
+#define r_A2        6
+#define r_A3        7
+#define r_T0        8
+#define r_T1        9
+#define r_T2        10
+#define r_T3        11
+#define r_T4        12
+#define r_T5        13
+#define r_T6        14
+#define r_T7        15
+#define r_S0        16
+#define r_S1        17
+#define r_S2        18
+#define r_S3        19
+#define r_S4        20
+#define r_S5        21
+#define r_S6        22
+#define r_S7        23
+#define r_T8        24
+#define r_T9        25
+#define r_K0        26
+#define r_K1        27
+#define r_GP        28
+#define r_SP        29
+#define r_FP        30
+#define r_RA        31
+#define r_F0        32
+#define r_F1        33
+#define r_F2        34
+#define r_F3        35
+#define r_F4        36
+#define r_F5        37
+#define r_F6        38
+#define r_F7        39
+#define r_F8        40
+#define r_F9        41
+#define r_F10       42
+#define r_F11       43
+#define r_F12       44
+#define r_F13       45
+#define r_F14       46
+#define r_F15       47
+#define r_F16       48
+#define r_F17       49
+#define r_F18       50
+#define r_F19       51
+#define r_F20       52
+#define r_F21       53
+#define r_F22       54
+#define r_F23       55
+#define r_F24       56
+#define r_F25       57
+#define r_F26       58
+#define r_F27       59
+#define r_F28       60
+#define r_F29       61
+#define r_F30       62
+#define r_F31       63
+
+/* single-purpose registers, given names for clarity */
+#define rPC	s0
+#define rFP	s1
+#define rSELF	s2
+#define rIBASE	s3
+#define rINST	s4
+#define rOBJ	s5
+#define rBIX	s6
+#define rTEMP	s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0     a0
+#define rARG1     a1
+#define rARG2     a2
+#define rARG3     a3
+#define rRESULT0  v0
+#define rRESULT1  v1
+#else
+#define rARG0     a1
+#define rARG1     a0
+#define rARG2     a3
+#define rARG3     a2
+#define rRESULT0  v1
+#define rRESULT1  v0
+#endif
+
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()	lw	rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF()	sw	rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF()	lw	rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF()	sw	rFP, offThread_curFrame(rSELF)
+
+#define EXPORT_PC() \
+	sw	rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+	subu	rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST()			lhu	rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count)	lhu     rINST, (_count*2)(rPC); \
+					addu	rPC, rPC, (_count * 2)
+
+#define FETCH_ADVANCE_INST_RB(rd)	addu	rPC, rPC, rd;	\
+					lhu     rINST, (rPC)
+
+#define FETCH(rd, _count)		lhu	rd, (_count * 2)(rPC)
+#define FETCH_S(rd, _count)		lh	rd, (_count * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count)            lbu     rd, (_count * 2 + 1)(rPC)
+#define FETCH_C(rd, _count)            lbu     rd, (_count * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd)		and	rd, rINST, 0xFF
+
+#define GOTO_OPCODE(rd)			sll  rd, rd, -1000;	\
+					addu rd, rIBASE, rd;	\
+					jr  rd
+
+
+#define LOAD(rd, rbase)			lw  rd, 0(rbase)
+#define LOAD_F(rd, rbase)		l.s rd, (rbase)
+#define STORE(rd, rbase)		sw  rd, 0(rbase)
+#define STORE_F(rd, rbase)		s.s rd, (rbase)
+
+#define GET_VREG(rd, rix)		LOAD_eas2(rd,rFP,rix)
+
+#define GET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix)		STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst)	.set noreorder;		\
+					sll  dst, dst, -1000;	\
+					addu dst, rIBASE, dst;			\
+					sll  t8, rix, 2;	\
+					addu t8, t8, rFP;	\
+					jr  dst;		\
+					sw  rd, 0(t8);		\
+					.set reorder
+
+#define SET_VREG_F(rd, rix)		EAS2(AT, rFP, rix);		\
+					.set noat;  s.s	rd, (AT); .set at
+
+
+#define GET_OPA(rd)			srl     rd, rINST, 8
+#ifndef		MIPS32R2
+#define GET_OPA4(rd)			GET_OPA(rd);  and  rd, 0xf
+#else
+#define GET_OPA4(rd)			ext	rd, rd, 8, 4
+#endif
+#define GET_OPB(rd)			srl     rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd,off)		lw    rd, offThread_##off##(rSELF)
+
+#define LOAD_rSELF_method(rd)		LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd)	LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd)	LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd)		LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd)	LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd)		LOAD_rSELF_OFF(rd, bailPtr)
+
+#define GET_JIT_PROF_TABLE(rd)		LOAD_rSELF_OFF(rd,pJitProfTable)
+#define GET_JIT_THRESHOLD(rd)		LOAD_rSELF_OFF(rd,jitThreshold)
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd,rbase,roff,rshift)	.set noat;		\
+					sll  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define EAS1(rd,rbase,roff)		EASN(rd,rbase,roff,1)
+#define EAS2(rd,rbase,roff)		EASN(rd,rbase,roff,2)
+#define EAS3(rd,rbase,roff)		EASN(rd,rbase,roff,3)
+#define EAS4(rd,rbase,roff)		EASN(rd,rbase,roff,4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd,rbase,roff,rshift)	.set noat;		\
+					srl  AT, roff, rshift;	\
+					addu rd, rbase, AT;	\
+					.set at
+
+#define LOAD_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  lw  rd, 0(AT); .set at
+
+#define STORE_eas2(rd,rbase,roff)	EAS2(AT, rbase, roff);  \
+					.set noat;  sw  rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd,rbase,off)	lw	rd, off(rbase)
+#define LOADu2_RB_OFF(rd,rbase,off)	lhu	rd, off(rbase)
+#define STORE_RB_OFF(rd,rbase,off)	sw	rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, off(rbase);	\
+					        sw	rhi, (off+4)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, off(rbase);	\
+					        lw	rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, off(rbase);	\
+						s.s	rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, off(rbase);	\
+						l.s	rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo,rhi,rbase,off)	        sw	rlo, (off+4)(rbase);	\
+					        sw	rhi, (off)(rbase)
+#define LOAD64_off(rlo,rhi,rbase,off)	        lw	rlo, (off+4)(rbase);	\
+					        lw	rhi, (off)(rbase)
+#define STORE64_off_F(rlo,rhi,rbase,off)	s.s	rlo, (off+4)(rbase);	\
+						s.s	rhi, (off)(rbase)
+#define LOAD64_off_F(rlo,rhi,rbase,off)		l.s	rlo, (off+4)(rbase);	\
+						l.s	rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo,rhi,rbase)		STORE64_off(rlo,rhi,rbase,0)
+#define LOAD64(rlo,rhi,rbase)		LOAD64_off(rlo,rhi,rbase,0)
+
+#define STORE64_F(rlo,rhi,rbase)	STORE64_off_F(rlo,rhi,rbase,0)
+#define LOAD64_F(rlo,rhi,rbase)		LOAD64_off_F(rlo,rhi,rbase,0)
+
+#define STORE64_lo(rd,rbase)		sw	rd, 0(rbase)
+#define STORE64_hi(rd,rbase)		sw	rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd,rbase)		LOAD_RB_OFF(rd,rbase,offThread_exception)
+#define LOAD_base_offArrayObject_length(rd,rbase)	LOAD_RB_OFF(rd,rbase,offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd,rbase)   LOAD_RB_OFF(rd,rbase,offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd,rbase)	LOAD_RB_OFF(rd,rbase,offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd,rbase)	LOAD_RB_OFF(rd,rbase,offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd,rbase)	LOAD_RB_OFF(rd,rbase,offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd,rbase)	LOAD_RB_OFF(rd,rbase,offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd,rbase)		LOAD_RB_OFF(rd,rbase,offMethod_name)
+#define LOAD_base_offObject_clazz(rd,rbase)		LOAD_RB_OFF(rd,rbase,offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd,rbase)		LOADu2_RB_OFF(rd,rbase,offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd,rbase)		STORE_RB_OFF(rd,rbase,offThread_exception)
+
+
+#define	STACK_STORE(rd,off)	sw   rd, off(sp)
+#define	STACK_LOAD(rd,off)	lw   rd, off(sp)
+#define CREATE_STACK(n)	 	subu sp, sp, n
+#define DELETE_STACK(n)	 	addu sp, sp, n
+
+#define SAVE_RA(offset)	 	STACK_STORE(ra, offset)
+#define LOAD_RA(offset)	 	STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest,addr)	la   dest, addr
+#define LOAD_IMM(dest, imm)	li   dest, imm
+#define MOVE_REG(dest,src)	move dest, src
+#define	RETURN			jr   ra
+#define	STACK_SIZE		128
+
+#define STACK_OFFSET_ARG04	16
+#define STACK_OFFSET_GP		84
+#define STACK_OFFSET_rFP	112
+
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+        .cprestore STACK_OFFSET_GP
+
+#define JAL(func)		move rTEMP, ra;				\
+				jal  func;				\
+				move ra, rTEMP
+
+#define JALR(reg)		move rTEMP, ra;				\
+				jalr ra, reg;				\
+				move ra, rTEMP
+
+#define BAL(n)			bal  n
+
+#define	STACK_STORE_RA()  	CREATE_STACK(STACK_SIZE);		\
+				STACK_STORE(gp, STACK_OFFSET_GP);	\
+				STACK_STORE(ra, 124)
+
+#define	STACK_STORE_S0()  	STACK_STORE_RA();			\
+				STACK_STORE(s0, 116)
+
+#define	STACK_STORE_S0S1()  	STACK_STORE_S0();			\
+				STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define	STACK_LOAD_RA()		STACK_LOAD(ra, 124);			\
+				STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				DELETE_STACK(STACK_SIZE)
+
+#define	STACK_LOAD_S0()  	STACK_LOAD(s0, 116);			\
+				STACK_LOAD_RA()
+
+#define	STACK_LOAD_S0S1()  	STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD_S0()
+
+#define STACK_STORE_FULL()	CREATE_STACK(STACK_SIZE);	\
+				STACK_STORE(ra, 124);		\
+				STACK_STORE(fp, 120);		\
+				STACK_STORE(s0, 116);		\
+				STACK_STORE(s1, STACK_OFFSET_rFP);	\
+				STACK_STORE(s2, 108);		\
+				STACK_STORE(s3, 104);		\
+				STACK_STORE(s4, 100);		\
+				STACK_STORE(s5, 96);		\
+				STACK_STORE(s6, 92);		\
+				STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL()	STACK_LOAD(gp, STACK_OFFSET_GP);	\
+				STACK_LOAD(s7, 88);	\
+				STACK_LOAD(s6, 92);	\
+				STACK_LOAD(s5, 96);	\
+				STACK_LOAD(s4, 100);	\
+				STACK_LOAD(s3, 104);	\
+				STACK_LOAD(s2, 108);	\
+				STACK_LOAD(s1, STACK_OFFSET_rFP);	\
+				STACK_LOAD(s0, 116);	\
+				STACK_LOAD(fp, 120);	\
+				STACK_LOAD(ra, 124);	\
+				DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define STACK_OFFSET_SCR   32
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+/* File: mips/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .section .data.rel.ro
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: mips/TEMPLATE_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;        # (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;        # (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     *
+     * This code assumes the register pair ordering will depend on endianess (a1:a0 or a0:a1).
+     *    a1:a0 => vBB
+     *    a3:a2 => vCC
+     */
+    /* cmp-long vAA, vBB, vCC */
+    slt    t0, rARG1, rARG3             # compare hi
+    sgt    t1, rARG1, rARG3
+    subu   v0, t1, t0                   # v0<- (-1,1,0)
+    bnez   v0, .LTEMPLATE_CMP_LONG_finish
+                                        # at this point x.hi==y.hi
+    sltu   t0, rARG0, rARG2             # compare lo
+    sgtu   t1, rARG0, rARG2
+    subu   v0, t1, t0                   # v0<- (-1,1,0) for [< > =]
+.LTEMPLATE_CMP_LONG_finish:
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: mips/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, .LinvokeChain       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: mips/TEMPLATE_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * For JIT: op1 in a0/a1, op2 in a2/a3, return in v0/v1
+     *
+     * Consider WXxYZ (a1a0 x a3a2) with a long multiply:
+     *
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     *
+     */
+    /* mul-long vAA, vBB, vCC */
+    mul     rRESULT1,rARG3,rARG0              #  v1= a3a0
+    multu   rARG2,rARG0
+    mfhi    t1
+    mflo    rRESULT0                          #  v0= a2a0
+    mul     t0,rARG2,rARG1                    #  t0= a2a1
+    addu    rRESULT1,rRESULT1,t1              #  v1= a3a0 + hi(a2a0)
+    addu    rRESULT1,rRESULT1,t0              #  v1= a3a0 + hi(a2a0) + a2a1;
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: mips/TEMPLATE_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shl-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sll     rRESULT0, rARG0, a2		#  rlo<- alo << (shift&31)
+    not     rRESULT1, a2		#  rhi<- 31-shift  (shift is 5b)
+    srl     rARG0, 1
+    srl     rARG0, rRESULT1		#  alo<- alo >> (32-(shift&31))
+    sll     rRESULT1, rARG1, a2		#  rhi<- ahi << (shift&31)
+    or      rRESULT1, rARG0		#  rhi<- rhi | alo
+    andi    a2, 0x20			#  shift< shift & 0x20
+    movn    rRESULT1, rRESULT0, a2	#  rhi<- rlo (if shift&0x20)
+    movn    rRESULT0, zero, a2		#  rlo<- 0  (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: mips/TEMPLATE_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* shr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    sra     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    sra     a3, rARG1, 31		#  a3<- sign(ah)
+    not     rARG0, a2			#  alo<- 31-shift (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, a3, a2		#  rhi<- sign(ahi) (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: mips/TEMPLATE_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to ignore all but the low
+     * 6 bits.
+     */
+    /* ushr-long vAA:vBB(rARG1:rARG0), vCC(a2) - result in (rRESULT1:rRESULT0) */
+    srl     rRESULT1, rARG1, a2		#  rhi<- ahi >> (shift&31)
+    srl     rRESULT0, rARG0, a2		#  rlo<- alo >> (shift&31)
+    not     rARG0, a2			#  alo<- 31-n  (shift is 5b)
+    sll     rARG1, 1
+    sll     rARG1, rARG0		#  ahi<- ahi << (32-(shift&31))
+    or      rRESULT0, rARG1		#  rlo<- rlo | ahi
+    andi    a2, 0x20			#  shift & 0x20
+    movn    rRESULT0, rRESULT1, a2	#  rlo<- rhi (if shift&0x20)
+    movn    rRESULT1, zero, a2		#  rhi<- 0 (if shift&0x20)
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: mips/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__addsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    add.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: mips/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__subsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    sub.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: mips/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__mulsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    mul.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: mips/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: mips/fbinop.S */
+    /*
+     * Generic 32-bit binary float operation. a0 = a1 op a2.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+    LOAD(a1, a2)                        # a1<- vCC
+    .if 0
+    beqz    a1, common_errDivideByZero  # is second operand zero?
+    .endif
+                               # optional op
+    JAL(__divsf3)                              # v0 = result
+    STORE(v0, rOBJ)                     # vAA <- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+    LOAD_F(fa1, a2)                     # fa1<- vCC
+    .if 0
+    # is second operand zero?
+    li.s        ft0, 0
+    c.eq.s      fcc0, ft0, fa1          # condition bit and comparision with 0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    div.s fv0, fa0, fa1                            # fv0 = result
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: mips/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__adddf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: mips/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__subdf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: mips/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__muldf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: mips/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: mips/fbinopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = op1 address
+     *     a2 = op2 address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    move t1, a2                         # save a2
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)            # a2/a3<- vCC/vCC+1
+    .if 0
+    or          t0, rARG2, rARG3        # second arg (a2-a3) is zero?
+    beqz        t0, common_errDivideByZero
+    .endif
+                               # optional op
+    JAL(__divdf3)                              # result<- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    LOAD64_F(fa0, fa0f, a1)
+    LOAD64_F(fa1, fa1f, a2)
+    .if 0
+    li.d        ft0, 0
+    c.eq.d      fcc0, fa1, ft0
+    bc1t        fcc0, common_errDivideByZero
+    .endif
+                               # optional op
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: mips/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: mips/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+                               # optional op
+    JAL(__truncdfsf2)                              # v0<- op, a0-a3 changed
+.LTEMPLATE_DOUBLE_TO_FLOAT_VFP_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    LOAD64_F(fa0, fa0f, a1)
+                               # optional op
+    cvt.s.d  fv0,fa0                            # fv0 = result
+.LTEMPLATE_DOUBLE_TO_FLOAT_VFP_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: mips/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: mips/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     */
+    move rINST, a0                      # save a0
+#ifdef  SOFT_FLOAT
+    move t0, a1                         # save a1
+    LOAD64(rARG0, rARG1, t0)            # a0/a1<- vB/vB+1
+                               # optional op
+    b    d2i_doconv                              # v0<- op, a0-a3 changed
+.LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg:
+    STORE(v0, rINST)                    # vA<- v0
+#else
+    LOAD64_F(fa0, fa0f, a1)
+                               # optional op
+    b    d2i_doconv                            # fv0 = result
+.LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f:
+    STORE_F(fv0, rINST)                 # vA<- fv0
+#endif
+    RETURN
+
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rOBJ as global to hold arguments (they are not bound to a global var)
+ */
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move        rBIX, rARG0                       # save a0
+    move        rOBJ, rARG1                       #  and a1
+    JAL(__gedf2)                               # is arg >= maxint?
+
+    move        t0, v0
+    li          v0, ~0x80000000                # return maxint (7fffffff)
+    bgez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                       # recover arg
+    move        rARG1, rOBJ
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                               # is arg <= minint?
+
+    move        t0, v0
+    li          v0, 0x80000000                 # return minint (80000000)
+    blez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg       # nonzero == yes
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    move        rARG2, rBIX                  # compare against self
+    move        rARG3, rOBJ
+    JAL(__nedf2)                        # is arg == self?
+
+    move        t0, v0                  # zero == no
+    li          v0, 0
+    bnez        t0, .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg        # return zero for NaN
+
+    move        rARG0, rBIX                  # recover arg
+    move        rARG1, rOBJ
+    JAL(__fixdfsi)                      # convert double to int
+    b           .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg
+#else
+    la          t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa1, fa0
+    l.s         fv0, .LDOUBLE_TO_INT_maxret
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    la          t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d     fcc0, fa0, fa1
+    l.s         fv0, .LDOUBLE_TO_INT_minret
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    mov.d       fa1, fa0
+    c.un.d      fcc0, fa0, fa1
+    li.s        fv0, 0
+    bc1t        .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+
+    trunc.w.d   fv0, fa0
+    b           .LTEMPLATE_DOUBLE_TO_INT_VFP_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword   0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword   0xc1e0000000000000                  # minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word   0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word   0x80000000
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: mips/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: mips/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+                               # optional op
+    JAL(__extendsfdf2)                              # result<- op, a0-a3 changed
+
+.LTEMPLATE_FLOAT_TO_DOUBLE_VFP_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+                               # optional op
+    cvt.d.s fv0, fa0
+
+.LTEMPLATE_FLOAT_TO_DOUBLE_VFP_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: mips/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: mips/funop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+                               # optional op
+    b    f2i_doconv                              # v0<- op, a0-a3 changed
+.LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+                               # optional op
+    b        f2i_doconv                            # fv0 = result
+.LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+        li      a1, 0x4f000000  # (float)maxint
+        move    rBIX, a0
+        JAL(__gesf2)            # is arg >= maxint?
+        move    t0, v0
+        li      v0, ~0x80000000 # return maxint (7fffffff)
+        bgez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+
+        move    a0, rBIX                # recover arg
+        li      a1, 0xcf000000  # (float)minint
+        JAL(__lesf2)
+
+        move    t0, v0
+        li      v0, 0x80000000  # return minint (80000000)
+        blez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+        move    a0, rBIX
+        move    a1, rBIX
+        JAL(__nesf2)
+
+        move    t0, v0
+        li      v0, 0           # return zero for NaN
+        bnez    t0, .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+
+        move    a0, rBIX
+        JAL(__fixsfsi)
+        b .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg
+#else
+        l.s             fa1, .LFLOAT_TO_INT_max
+        c.ole.s         fcc0, fa1, fa0
+        l.s             fv0, .LFLOAT_TO_INT_ret_max
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        l.s             fa1, .LFLOAT_TO_INT_min
+        c.ole.s         fcc0, fa0, fa1
+        l.s             fv0, .LFLOAT_TO_INT_ret_min
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        mov.s           fa1, fa0
+        c.un.s          fcc0, fa0, fa1
+        li.s            fv0, 0
+        bc1t            .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+
+        trunc.w.s       fv0, fa0
+        b .LTEMPLATE_FLOAT_TO_INT_VFP_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+        .word   0x4f000000
+.LFLOAT_TO_INT_min:
+        .word   0xcf000000
+.LFLOAT_TO_INT_ret_max:
+        .word   0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+        .word   0x80000000
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: mips/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: mips/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     */
+    /* unop vA, vB */
+    move rOBJ, a0                       # save a0
+#ifdef  SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vB
+                               # optional op
+    JAL(__floatsidf)                              # result<- op, a0-a3 changed
+
+.LTEMPLATE_INT_TO_DOUBLE_VFP_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)   # vA/vA+1<- v0/v1
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vB
+                               # optional op
+    cvt.d.w    fv0, fa0
+
+.LTEMPLATE_INT_TO_DOUBLE_VFP_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                          # vA/vA+1<- fv0/fv0f
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: mips/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: mips/funop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     *
+     * On entry:
+     *     a0 = target dalvik register address
+     *     a1 = src dalvik register address
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     *
+     */
+    move rOBJ, a0                       # save a0
+#ifdef SOFT_FLOAT
+    LOAD(a0, a1)                        # a0<- vBB
+                               # optional op
+    JAL(__floatsisf)                              # v0<- op, a0-a3 changed
+.LTEMPLATE_INT_TO_FLOAT_VFP_set_vreg:
+    STORE(v0, rOBJ)                     # vAA<- v0
+#else
+    LOAD_F(fa0, a1)                     # fa0<- vBB
+                               # optional op
+    cvt.s.w fv0, fa0                            # fv0 = result
+.LTEMPLATE_INT_TO_FLOAT_VFP_set_vreg_f:
+    STORE_F(fv0, rOBJ)                  # vAA <- fv0
+#endif
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: mips/TEMPLATE_CMPG_DOUBLE_VFP.S */
+/* File: mips/TEMPLATE_CMPL_DOUBLE_VFP.S */
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+#else
+    LOAD64_F(ft0, ft0f, a0)             # ft0<- vBB
+    LOAD64_F(ft1, ft1f, a1)             # ft1<- vCC
+    c.olt.d     fcc0, ft0, ft1          # Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    c.olt.d     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+    c.eq.d      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPG_DOUBLE_VFP_finish
+#endif
+
+    li            rTEMP, 1
+
+TEMPLATE_CMPG_DOUBLE_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: mips/TEMPLATE_CMPL_DOUBLE_VFP.S */
+    /*
+     * Compare two double precision floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    move rOBJ, a0                       # save a0
+    move rBIX, a1                       # save a1
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__eqdf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__ltdf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    LOAD64(rARG0, rARG1, rOBJ)          # a0/a1<- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)          # a2/a3<- vCC/vCC+1
+    JAL(__gtdf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+#else
+    LOAD64_F(ft0, ft0f, a0)             # ft0<- vBB
+    LOAD64_F(ft1, ft1f, a1)             # ft1<- vCC
+    c.olt.d     fcc0, ft0, ft1          # Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    c.olt.d     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+    c.eq.d      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPL_DOUBLE_VFP_finish
+#endif
+
+    li     rTEMP, -1
+
+TEMPLATE_CMPL_DOUBLE_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: mips/TEMPLATE_CMPG_FLOAT_VFP.S */
+/* File: mips/TEMPLATE_CMPL_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPG_FLOAT_VFP_finish
+#else
+    LOAD_F(ft0, a0)                     # ft0<- vBB
+    LOAD_F(ft1, a1)                     # ft1<- vCC
+    c.olt.s     fcc0, ft0, ft1          #Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    c.olt.s     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+    c.eq.s      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPG_FLOAT_VFP_finish
+#endif
+
+    li     rTEMP, 1
+
+TEMPLATE_CMPG_FLOAT_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: mips/TEMPLATE_CMPL_FLOAT_VFP.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * On entry:
+     *    a0 = &op1 [vBB]
+     *    a1 = &op2 [vCC]
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+#ifdef  SOFT_FLOAT
+    LOAD(rOBJ, a0)                      # rOBJ<- vBB
+    LOAD(rBIX, a1)                      # rBIX<- vCC
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__eqsf2)                        # v0<- (vBB == vCC)
+    li       rTEMP, 0                   # vAA<- 0
+    beqz     v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__ltsf2)                        # a0<- (vBB < vCC)
+    li       rTEMP, -1                  # vAA<- -1
+    bltz     v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    move     a0, rOBJ                   # a0<- vBB
+    move     a1, rBIX                   # a1<- vCC
+    JAL(__gtsf2)                        # v0<- (vBB > vCC)
+    li      rTEMP, 1                    # vAA<- 1
+    bgtz    v0, TEMPLATE_CMPL_FLOAT_VFP_finish
+#else
+    LOAD_F(ft0, a0)                     # ft0<- vBB
+    LOAD_F(ft1, a1)                     # ft1<- vCC
+    c.olt.s     fcc0, ft0, ft1          #Is ft0 < ft1
+    li          rTEMP, -1
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    c.olt.s     fcc0, ft1, ft0
+    li          rTEMP, 1
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+    c.eq.s      fcc0, ft0, ft1
+    li          rTEMP, 0
+    bc1t        fcc0, TEMPLATE_CMPL_FLOAT_VFP_finish
+#endif
+
+    li     rTEMP, -1
+
+TEMPLATE_CMPL_FLOAT_VFP_finish:
+    move     v0, rTEMP                  # v0<- vAA
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: mips/TEMPLATE_SQRT_DOUBLE_VFP.S */
+
+    /*
+     * 64-bit floating point sqrt operation.
+     * If the result is a NaN, bail out to library code to do
+     * the right thing.
+     *
+     * On entry:
+     *     a2 src addr of op1
+     * On exit:
+     *     v0,v1/fv0 = res
+     */
+#ifdef  SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)        # a0/a1<- vBB/vBB+1
+#else
+    LOAD64_F(fa0, fa0f, a2)         # fa0/fa0f<- vBB/vBB+1
+    sqrt.d	fv0, fa0
+    c.eq.d	fv0, fv0
+    bc1t	1f
+#endif
+    JAL(sqrt)
+1:
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: mips/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+    /*
+     * Throw an exception from JIT'ed code.
+     * On entry:
+     *    a0    Dalvik PC that raises the exception
+     */
+    j      .LhandleException
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: mips/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+    /*
+     * This handler encapsulates heap memory ops for selfVerification mode.
+     *
+     * The call to the handler is inserted prior to a heap memory operation.
+     * This handler then calls a function to decode the memory op, and process
+     * it accordingly. Afterwards, the handler changes the return address to
+     * skip the memory op so it never gets executed.
+     */
+#ifdef HARD_FLOAT
+    /* push f0-f31 onto stack */
+    sw      f0, fr0*-4(sp)              # push f0
+    sw      f1, fr1*-4(sp)              # push f1
+    sw      f2, fr2*-4(sp)              # push f2
+    sw      f3, fr3*-4(sp)              # push f3
+    sw      f4, fr4*-4(sp)              # push f4
+    sw      f5, fr5*-4(sp)              # push f5
+    sw      f6, fr6*-4(sp)              # push f6
+    sw      f7, fr7*-4(sp)              # push f7
+    sw      f8, fr8*-4(sp)              # push f8
+    sw      f9, fr9*-4(sp)              # push f9
+    sw      f10, fr10*-4(sp)            # push f10
+    sw      f11, fr11*-4(sp)            # push f11
+    sw      f12, fr12*-4(sp)            # push f12
+    sw      f13, fr13*-4(sp)            # push f13
+    sw      f14, fr14*-4(sp)            # push f14
+    sw      f15, fr15*-4(sp)            # push f15
+    sw      f16, fr16*-4(sp)            # push f16
+    sw      f17, fr17*-4(sp)            # push f17
+    sw      f18, fr18*-4(sp)            # push f18
+    sw      f19, fr19*-4(sp)            # push f19
+    sw      f20, fr20*-4(sp)            # push f20
+    sw      f21, fr21*-4(sp)            # push f21
+    sw      f22, fr22*-4(sp)            # push f22
+    sw      f23, fr23*-4(sp)            # push f23
+    sw      f24, fr24*-4(sp)            # push f24
+    sw      f25, fr25*-4(sp)            # push f25
+    sw      f26, fr26*-4(sp)            # push f26
+    sw      f27, fr27*-4(sp)            # push f27
+    sw      f28, fr28*-4(sp)            # push f28
+    sw      f29, fr29*-4(sp)            # push f29
+    sw      f30, fr30*-4(sp)            # push f30
+    sw      f31, fr31*-4(sp)            # push f31
+
+    sub     sp, (32-0)*4                # adjust stack pointer
+#endif
+
+    /* push gp registers (except zero, gp, sp, and fp) */
+    .set noat
+    sw      AT, r_AT*-4(sp)             # push at
+    .set at
+    sw      v0, r_V0*-4(sp)             # push v0
+    sw      v1, r_V1*-4(sp)             # push v1
+    sw      a0, r_A0*-4(sp)             # push a0
+    sw      a1, r_A1*-4(sp)             # push a1
+    sw      a2, r_A2*-4(sp)             # push a2
+    sw      a3, r_A3*-4(sp)             # push a3
+    sw      t0, r_T0*-4(sp)             # push t0
+    sw      t1, r_T1*-4(sp)             # push t1
+    sw      t2, r_T2*-4(sp)             # push t2
+    sw      t3, r_T3*-4(sp)             # push t3
+    sw      t4, r_T4*-4(sp)             # push t4
+    sw      t5, r_T5*-4(sp)             # push t5
+    sw      t6, r_T6*-4(sp)             # push t6
+    sw      t7, r_T7*-4(sp)             # push t7
+    sw      s0, r_S0*-4(sp)             # push s0
+    sw      s1, r_S1*-4(sp)             # push s1
+    sw      s2, r_S2*-4(sp)             # push s2
+    sw      s3, r_S3*-4(sp)             # push s3
+    sw      s4, r_S4*-4(sp)             # push s4
+    sw      s5, r_S5*-4(sp)             # push s5
+    sw      s6, r_S6*-4(sp)             # push s6
+    sw      s7, r_S7*-4(sp)             # push s7
+    sw      t8, r_T8*-4(sp)             # push t8
+    sw      t9, r_T9*-4(sp)             # push t9
+    sw      k0, r_K0*-4(sp)             # push k0
+    sw      k1, r_K1*-4(sp)             # push k1
+    sw      ra, r_RA*-4(sp)             # push RA
+
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    sub     sp, (32-0)*4                # adjust stack pointer
+
+    la     a2, .LdvmSelfVerificationMemOpDecode  # defined in footer.S
+    lw     a2, (a2)
+    move   a0, ra                       # a0<- link register
+    move   a1, sp                       # a1<- stack pointer
+    JALR(a2)
+
+    /* pop gp registers (except zero, gp, sp, and fp) */
+    # Note: even if we don't save all 32 registers, we still need to
+    #       adjust SP by 32 registers due to the way we are storing
+    #       the registers on the stack.
+    add     sp, (32-0)*4                # adjust stack pointer
+    .set noat
+    lw      AT, r_AT*-4(sp)             # pop at
+    .set at
+    lw      v0, r_V0*-4(sp)             # pop v0
+    lw      v1, r_V1*-4(sp)             # pop v1
+    lw      a0, r_A0*-4(sp)             # pop a0
+    lw      a1, r_A1*-4(sp)             # pop a1
+    lw      a2, r_A2*-4(sp)             # pop a2
+    lw      a3, r_A3*-4(sp)             # pop a3
+    lw      t0, r_T0*-4(sp)             # pop t0
+    lw      t1, r_T1*-4(sp)             # pop t1
+    lw      t2, r_T2*-4(sp)             # pop t2
+    lw      t3, r_T3*-4(sp)             # pop t3
+    lw      t4, r_T4*-4(sp)             # pop t4
+    lw      t5, r_T5*-4(sp)             # pop t5
+    lw      t6, r_T6*-4(sp)             # pop t6
+    lw      t7, r_T7*-4(sp)             # pop t7
+    lw      s0, r_S0*-4(sp)             # pop s0
+    lw      s1, r_S1*-4(sp)             # pop s1
+    lw      s2, r_S2*-4(sp)             # pop s2
+    lw      s3, r_S3*-4(sp)             # pop s3
+    lw      s4, r_S4*-4(sp)             # pop s4
+    lw      s5, r_S5*-4(sp)             # pop s5
+    lw      s6, r_S6*-4(sp)             # pop s6
+    lw      s7, r_S7*-4(sp)             # pop s7
+    lw      t8, r_T8*-4(sp)             # pop t8
+    lw      t9, r_T9*-4(sp)             # pop t9
+    lw      k0, r_K0*-4(sp)             # pop k0
+    lw      k1, r_K1*-4(sp)             # pop k1
+    lw      ra, r_RA*-4(sp)             # pop RA
+
+#ifdef HARD_FLOAT
+    /* pop f0-f31 from stack */
+    add     sp, (32-0)*4                # adjust stack pointer
+    lw      f0, fr0*-4(sp)              # pop f0
+    lw      f1, fr1*-4(sp)              # pop f1
+    lw      f2, fr2*-4(sp)              # pop f2
+    lw      f3, fr3*-4(sp)              # pop f3
+    lw      f4, fr4*-4(sp)              # pop f4
+    lw      f5, fr5*-4(sp)              # pop f5
+    lw      f6, fr6*-4(sp)              # pop f6
+    lw      f7, fr7*-4(sp)              # pop f7
+    lw      f8, fr8*-4(sp)              # pop f8
+    lw      f9, fr9*-4(sp)              # pop f9
+    lw      f10, fr10*-4(sp)            # pop f10
+    lw      f11, fr11*-4(sp)            # pop f11
+    lw      f12, fr12*-4(sp)            # pop f12
+    lw      f13, fr13*-4(sp)            # pop f13
+    lw      f14, fr14*-4(sp)            # pop f14
+    lw      f15, fr15*-4(sp)            # pop f15
+    lw      f16, fr16*-4(sp)            # pop f16
+    lw      f17, fr17*-4(sp)            # pop f17
+    lw      f18, fr18*-4(sp)            # pop f18
+    lw      f19, fr19*-4(sp)            # pop f19
+    lw      f20, fr20*-4(sp)            # pop f20
+    lw      f21, fr21*-4(sp)            # pop f21
+    lw      f22, fr22*-4(sp)            # pop f22
+    lw      f23, fr23*-4(sp)            # pop f23
+    lw      f24, fr24*-4(sp)            # pop f24
+    lw      f25, fr25*-4(sp)            # pop f25
+    lw      f26, fr26*-4(sp)            # pop f26
+    lw      f27, fr27*-4(sp)            # pop f27
+    lw      f28, fr28*-4(sp)            # pop f28
+    lw      f29, fr29*-4(sp)            # pop f29
+    lw      f30, fr30*-4(sp)            # pop f30
+    lw      f31, fr31*-4(sp)            # pop f31
+#endif
+
+    RETURN
+#endif
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: mips/TEMPLATE_STRING_COMPARETO.S */
+    /*
+     * String's compareTo.
+     *
+     * Requires a0/a1 to have been previously checked for null.  Will
+     * return negative if this's string is < comp, 0 if they are the
+     * same and positive if >.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync with definitions in UtfString.h.  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   this object pointer
+     *    a1:   comp object pointer
+     *
+     */
+
+     subu  v0, a0, a1                # Same?
+     bnez  v0, 1f
+     RETURN
+1:
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_OFFSET(a1)
+     lw    t2, STRING_FIELDOFF_COUNT(a0)
+     lw    a2, STRING_FIELDOFF_COUNT(a1)
+     lw    a0, STRING_FIELDOFF_VALUE(a0)
+     lw    a1, STRING_FIELDOFF_VALUE(a1)
+
+    /*
+     * At this point, we have this/comp:
+     *    offset: t0/t1
+     *    count:  t2/a2
+     *    value:  a0/a1
+     * We're going to compute
+     *    a3 <- countDiff
+     *    a2 <- minCount
+     */
+     subu  a3, t2, a2                # a3<- countDiff
+     sleu  t7, t2, a2
+     movn  a2, t2, t7                # a2<- minCount
+
+     /*
+      * Note: data pointers point to first element.
+      */
+     addu  a0, 16                    # point to contents[0]
+     addu  a1, 16                    # point to contents[0]
+
+     /* Now, build pointers to the string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  a0, a0, t7
+     sll   t7, t1, 1                 # multiply offset by 2
+     addu  a1, a1, t7
+
+     /*
+      * At this point we have:
+      *   a0: *this string data
+      *   a1: *comp string data
+      *   a2: iteration count for comparison
+      *   a3: value to return if the first part of the string is equal
+      *   v0: reserved for result
+      *   t0-t5 available for loading string data
+      */
+
+     subu  a2, 2
+     bltz  a2, do_remainder2
+
+     /*
+      * Unroll the first two checks so we can quickly catch early mismatch
+      * on long strings (but preserve incoming alignment)
+      */
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     addu  a0, 4                     # offset to contents[2]
+     addu  a1, 4                     # offset to contents[2]
+     li    t7, 28
+     bgt   a2, t7, do_memcmp16
+     subu  a2, 3
+     bltz  a2, do_remainder
+
+loopback_triple:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     beqz  v0, 1f
+     RETURN
+1:
+     lhu   t2, 2(a0)
+     lhu   t3, 2(a1)
+     subu  v0, t2, t3
+     beqz  v0, 2f
+     RETURN
+2:
+     lhu   t4, 4(a0)
+     lhu   t5, 4(a1)
+     subu  v0, t4, t5
+     beqz  v0, 3f
+     RETURN
+3:
+     addu  a0, 6                     # offset to contents[i+3]
+     addu  a1, 6                     # offset to contents[i+3]
+     subu  a2, 3
+     bgez  a2, loopback_triple
+
+do_remainder:
+     addu  a2, 3
+     beqz  a2, returnDiff
+
+loopback_single:
+     lhu   t0, 0(a0)
+     lhu   t1, 0(a1)
+     subu  v0, t0, t1
+     bnez  v0, 1f
+     addu  a0, 2                     # offset to contents[i+1]
+     addu  a1, 2                     # offset to contents[i+1]
+     subu  a2, 1
+     bnez  a2, loopback_single
+
+returnDiff:
+     move  v0, a3
+1:
+     RETURN
+
+do_remainder2:
+     addu  a2, 2
+     bnez  a2, loopback_single
+     move  v0, a3
+     RETURN
+
+    /* Long string case */
+do_memcmp16:
+     move  rOBJ, a3                  # save return value if strings are equal
+     JAL(__memcmp16)
+     seq   t0, v0, zero
+     movn  v0, rOBJ, t0              # overwrite return value if strings are equal
+     RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: mips/TEMPLATE_STRING_INDEXOF.S */
+    /*
+     * String's indexOf.
+     *
+     * Requires a0 to have been previously checked for null.  Will
+     * return index of match of a1 in v0.
+     *
+     * IMPORTANT NOTE:
+     *
+     * This code relies on hard-coded offsets for string objects, and must be
+     * kept in sync wth definitions in UtfString.h  See asm-constants.h
+     *
+     * On entry:
+     *    a0:   string object pointer
+     *    a1:   char to match
+     *    a2:   Starting offset in string data
+     */
+
+     lw    t0, STRING_FIELDOFF_OFFSET(a0)
+     lw    t1, STRING_FIELDOFF_COUNT(a0)
+     lw    v0, STRING_FIELDOFF_VALUE(a0)
+
+    /*
+     * At this point, we have:
+     *    v0: object pointer
+     *    a1: char to match
+     *    a2: starting offset
+     *    t0: offset
+     *    t1: string length
+     */
+
+    /* Point to first element */
+     addu  v0, 16                    # point to contents[0]
+
+    /* Build pointer to start of string data */
+     sll   t7, t0, 1                 # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Save a copy of starting data in v1 */
+     move  v1, v0
+
+    /* Clamp start to [0..count] */
+     slt   t7, a2, zero
+     movn  a2, zero, t7
+     sgt   t7, a2, t1
+     movn  a2, t1, t7
+
+    /* Build pointer to start of data to compare */
+     sll   t7, a2, 1                # multiply offset by 2
+     addu  v0, v0, t7
+
+    /* Compute iteration count */
+     subu  a3, t1, a2
+
+    /*
+     * At this point we have:
+     *   v0: start of data to test
+     *   a1: char to compare
+     *   a3: iteration count
+     *   v1: original start of string
+     *   t0-t7 available for loading string data
+     */
+     subu  a3, 4
+     bltz  a3, indexof_remainder
+
+indexof_loop4:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     lhu   t0, 2(v0)
+     beq   t0, a1, match_1
+     lhu   t0, 4(v0)
+     beq   t0, a1, match_2
+     lhu   t0, 6(v0)
+     beq   t0, a1, match_3
+     addu  v0, 8                     # offset to contents[i+4]
+     subu  a3, 4
+     bgez  a3, indexof_loop4
+
+indexof_remainder:
+     addu  a3, 4
+     beqz  a3, indexof_nomatch
+
+indexof_loop1:
+     lhu   t0, 0(v0)
+     beq   t0, a1, match_0
+     addu  v0, 2                     # offset to contents[i+1]
+     subu  a3, 1
+     bnez  a3, indexof_loop1
+
+indexof_nomatch:
+     li    v0, -1
+     RETURN
+
+match_0:
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_1:
+     addu  v0, 2
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_2:
+     addu  v0, 4
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+match_3:
+     addu  v0, 6
+     subu  v0, v1
+     sra   v0, v0, 1                 # divide by 2
+     RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: mips/TEMPLATE_INTERPRET.S */
+    /*
+     * This handler transfers control to the interpeter without performing
+     * any lookups.  It may be called either as part of a normal chaining
+     * operation, or from the transition code in header.S.  We distinquish
+     * the two cases by looking at the link register.  If called from a
+     * translation chain, it will point to the chaining Dalvik PC.
+     * On entry:
+     *    ra - if NULL:
+     *        a1 - the Dalvik PC to begin interpretation.
+     *    else
+     *        [ra] contains Dalvik PC to begin interpretation
+     *    rSELF - pointer to thread
+     *    rFP - Dalvik frame pointer
+     */
+    la      t0, dvmJitToInterpPunt
+    move    a0, a1
+    beq     ra, zero, 1f
+    lw      a0, 0(ra)
+1:
+    jr      t0
+    # doesn't return
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: mips/TEMPLATE_MONITOR_ENTER.S */
+    /*
+     * Call out to the runtime to lock an object.  Because this thread
+     * may have been suspended in THREAD_MONITOR state and the Jit's
+     * translation cache subsequently cleared, we cannot return directly.
+     * Instead, unconditionally transition to the interpreter to resume.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li      a0, kHeavyweightMonitor
+#endif
+    jr      a2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: mips/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+    /*
+     * To support deadlock prediction, this version of MONITOR_ENTER
+     * will always call the heavyweight dvmLockObject, check for an
+     * exception and then bail out to the interpreter.
+     *
+     * On entry:
+     *    a0 - self pointer
+     *    a1 - the object (which has already been null-checked by the caller
+     *    rPC - the Dalvik PC of the following instruction.
+     *
+     */
+    la     a2, .LdvmLockObject
+    lw     t9, (a2)
+    sw     zero, offThread_inJitCodeCache(a0)   # record that we're not returning
+    JALR(t9)                                    # dvmLockObject(self, obj)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # test for exception
+    lw     a1, offThread_exception(rSELF)
+    beqz   a1, 1f
+    sub    a0, rPC, 2                           # roll dPC back to this monitor instruction
+    j      .LhandleException
+1:
+    # Bail to interpreter - no chain [note - rPC still contains dPC]
+#if defined(WITH_JIT_TUNING)
+    li     a0, kHeavyweightMonitor
+#endif
+    la     a2, .LdvmJitToInterpNoChain
+    lw     a2, (a2)
+    jr     a2
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RESTORE_STATE
+dvmCompiler_TEMPLATE_RESTORE_STATE:
+/* File: mips/TEMPLATE_RESTORE_STATE.S */
+    /*
+     * This handler restores state following a selfVerification memory access.
+     * On entry:
+     *    a0 - offset from rSELF to the 1st element of the coreRegs save array.
+     * Note: the following registers are not restored
+     *       zero, AT, gp, sp, fp, ra
+     */
+
+    add     a0, a0, rSELF               # pointer to heapArgSpace.coreRegs[0]
+#if 0
+    lw      zero, r_ZERO*4(a0)          # restore zero
+#endif
+    .set noat
+    lw      AT, r_AT*4(a0)              # restore at
+    .set at
+    lw      v0, r_V0*4(a0)              # restore v0
+    lw      v1, r_V1*4(a0)              # restore v1
+
+    lw      a1, r_A1*4(a0)              # restore a1
+    lw      a2, r_A2*4(a0)              # restore a2
+    lw      a3, r_A3*4(a0)              # restore a3
+
+    lw      t0, r_T0*4(a0)              # restore t0
+    lw      t1, r_T1*4(a0)              # restore t1
+    lw      t2, r_T2*4(a0)              # restore t2
+    lw      t3, r_T3*4(a0)              # restore t3
+    lw      t4, r_T4*4(a0)              # restore t4
+    lw      t5, r_T5*4(a0)              # restore t5
+    lw      t6, r_T6*4(a0)              # restore t6
+    lw      t7, r_T7*4(a0)              # restore t7
+
+    lw      s0, r_S0*4(a0)              # restore s0
+    lw      s1, r_S1*4(a0)              # restore s1
+    lw      s2, r_S2*4(a0)              # restore s2
+    lw      s3, r_S3*4(a0)              # restore s3
+    lw      s4, r_S4*4(a0)              # restore s4
+    lw      s5, r_S5*4(a0)              # restore s5
+    lw      s6, r_S6*4(a0)              # restore s6
+    lw      s7, r_S7*4(a0)              # restore s7
+
+    lw      t8, r_T8*4(a0)              # restore t8
+    lw      t9, r_T9*4(a0)              # restore t9
+
+    lw      k0, r_K0*4(a0)              # restore k0
+    lw      k1, r_K1*4(a0)              # restore k1
+
+#if 0
+    lw      gp, r_GP*4(a0)              # restore gp
+    lw      sp, r_SP*4(a0)              # restore sp
+    lw      fp, r_FP*4(a0)              # restore fp
+    lw      ra, r_RA*4(a0)              # restore ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    lw      f0, fr0*4(a0)               # restore f0
+    lw      f1, fr1*4(a0)               # restore f1
+    lw      f2, fr2*4(a0)               # restore f2
+    lw      f3, fr3*4(a0)               # restore f3
+    lw      f4, fr4*4(a0)               # restore f4
+    lw      f5, fr5*4(a0)               # restore f5
+    lw      f6, fr6*4(a0)               # restore f6
+    lw      f7, fr7*4(a0)               # restore f7
+    lw      f8, fr8*4(a0)               # restore f8
+    lw      f9, fr9*4(a0)               # restore f9
+    lw      f10, fr10*4(a0)             # restore f10
+    lw      f11, fr11*4(a0)             # restore f11
+    lw      f12, fr12*4(a0)             # restore f12
+    lw      f13, fr13*4(a0)             # restore f13
+    lw      f14, fr14*4(a0)             # restore f14
+    lw      f15, fr15*4(a0)             # restore f15
+    lw      f16, fr16*4(a0)             # restore f16
+    lw      f17, fr17*4(a0)             # restore f17
+    lw      f18, fr18*4(a0)             # restore f18
+    lw      f19, fr19*4(a0)             # restore f19
+    lw      f20, fr20*4(a0)             # restore f20
+    lw      f21, fr21*4(a0)             # restore f21
+    lw      f22, fr22*4(a0)             # restore f22
+    lw      f23, fr23*4(a0)             # restore f23
+    lw      f24, fr24*4(a0)             # restore f24
+    lw      f25, fr25*4(a0)             # restore f25
+    lw      f26, fr26*4(a0)             # restore f26
+    lw      f27, fr27*4(a0)             # restore f27
+    lw      f28, fr28*4(a0)             # restore f28
+    lw      f29, fr29*4(a0)             # restore f29
+    lw      f30, fr30*4(a0)             # restore f30
+    lw      f31, fr31*4(a0)             # restore f31
+#endif
+
+    lw      a0, r_A1*4(a0)              # restore a0
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_SAVE_STATE
+dvmCompiler_TEMPLATE_SAVE_STATE:
+/* File: mips/TEMPLATE_SAVE_STATE.S */
+    /*
+     * This handler performs a register save for selfVerification mode.
+     * On entry:
+     *    Top of stack + 4: a1 value to save
+     *    Top of stack + 0: a0 value to save
+     *    a0 - offset from rSELF to the beginning of the heapArgSpace record
+     *    a1 - the value of regMap
+     *
+     * The handler must save regMap, r0-r31, f0-f31 if FPU, and then return with
+     * r0-r31 with their original values (note that this means a0 and a1 must take
+     * the values on the stack - not the ones in those registers on entry.
+     * Finally, the two registers previously pushed must be popped.
+     * Note: the following registers are not saved
+     *       zero, AT, gp, sp, fp, ra
+     */
+    add     a0, a0, rSELF               # pointer to heapArgSpace
+    sw      a1, 0(a0)                   # save regMap
+    add     a0, a0, 4                   # pointer to coreRegs
+#if 0
+    sw      zero, r_ZERO*4(a0)          # save zero
+#endif
+    .set noat
+    sw      AT, r_AT*4(a0)              # save at
+    .set at
+    sw      v0, r_V0*4(a0)              # save v0
+    sw      v1, r_V1*4(a0)              # save v1
+
+    lw      a1, 0(sp)                   # recover a0 value
+    sw      a1, r_A0*4(a0)              # save a0
+    lw      a1, 4(sp)                   # recover a1 value
+    sw      a1, r_A1*4(a0)              # save a1
+    sw      a2, r_A2*4(a0)              # save a2
+    sw      a3, r_A3*4(a0)              # save a3
+
+    sw      t0, r_T0*4(a0)              # save t0
+    sw      t1, r_T1*4(a0)              # save t1
+    sw      t2, r_T2*4(a0)              # save t2
+    sw      t3, r_T3*4(a0)              # save t3
+    sw      t4, r_T4*4(a0)              # save t4
+    sw      t5, r_T5*4(a0)              # save t5
+    sw      t6, r_T6*4(a0)              # save t6
+    sw      t7, r_T7*4(a0)              # save t7
+
+    sw      s0, r_S0*4(a0)              # save s0
+    sw      s1, r_S1*4(a0)              # save s1
+    sw      s2, r_S2*4(a0)              # save s2
+    sw      s3, r_S3*4(a0)              # save s3
+    sw      s4, r_S4*4(a0)              # save s4
+    sw      s5, r_S5*4(a0)              # save s5
+    sw      s6, r_S6*4(a0)              # save s6
+    sw      s7, r_S7*4(a0)              # save s7
+
+    sw      t8, r_T8*4(a0)              # save t8
+    sw      t9, r_T9*4(a0)              # save t9
+
+    sw      k0, r_K0*4(a0)              # save k0
+    sw      k1, r_K1*4(a0)              # save k1
+
+#if 0
+    sw      gp, r_GP*4(a0)              # save gp
+    sw      sp, r_SP*4(a0)              # save sp (need to adjust??? )
+    sw      fp, r_FP*4(a0)              # save fp
+    sw      ra, r_RA*4(a0)              # save ra
+#endif
+
+/* #ifdef HARD_FLOAT */
+#if 0
+    sw      f0, fr0*4(a0)               # save f0
+    sw      f1, fr1*4(a0)               # save f1
+    sw      f2, fr2*4(a0)               # save f2
+    sw      f3, fr3*4(a0)               # save f3
+    sw      f4, fr4*4(a0)               # save f4
+    sw      f5, fr5*4(a0)               # save f5
+    sw      f6, fr6*4(a0)               # save f6
+    sw      f7, fr7*4(a0)               # save f7
+    sw      f8, fr8*4(a0)               # save f8
+    sw      f9, fr9*4(a0)               # save f9
+    sw      f10, fr10*4(a0)             # save f10
+    sw      f11, fr11*4(a0)             # save f11
+    sw      f12, fr12*4(a0)             # save f12
+    sw      f13, fr13*4(a0)             # save f13
+    sw      f14, fr14*4(a0)             # save f14
+    sw      f15, fr15*4(a0)             # save f15
+    sw      f16, fr16*4(a0)             # save f16
+    sw      f17, fr17*4(a0)             # save f17
+    sw      f18, fr18*4(a0)             # save f18
+    sw      f19, fr19*4(a0)             # save f19
+    sw      f20, fr20*4(a0)             # save f20
+    sw      f21, fr21*4(a0)             # save f21
+    sw      f22, fr22*4(a0)             # save f22
+    sw      f23, fr23*4(a0)             # save f23
+    sw      f24, fr24*4(a0)             # save f24
+    sw      f25, fr25*4(a0)             # save f25
+    sw      f26, fr26*4(a0)             # save f26
+    sw      f27, fr27*4(a0)             # save f27
+    sw      f28, fr28*4(a0)             # save f28
+    sw      f29, fr29*4(a0)             # save f29
+    sw      f30, fr30*4(a0)             # save f30
+    sw      f31, fr31*4(a0)             # save f31
+#endif
+
+    lw      a1, 0(sp)                   # recover a0 value
+    lw      a1, 4(sp)                   # recover a1 value
+    sub     sp, sp, 8                   # adjust stack ptr
+    RETURN
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: mips/TEMPLATE_PERIODIC_PROFILING.S */
+    /*
+     * Increment profile counter for this trace, and decrement
+     * sample counter.  If sample counter goes below zero, turn
+     * off profiling.
+     *
+     * On entry
+     * (ra-16) is address of pointer to counter.  Note: the counter
+     *    actually exists 16 bytes before the return target for mips.
+     *     - 4 bytes for prof count addr.
+     *     - 4 bytes for chain cell offset (2bytes 32 bit aligned).
+     *     - 4 bytes for call TEMPLATE_PERIODIC_PROFILING.
+     *     - 4 bytes for call delay slot.
+     */
+     lw     a0, -16(ra)
+     lw     a1, offThread_pProfileCountdown(rSELF)
+     lw     a2, 0(a0)                   # get counter
+     lw     a3, 0(a1)                   # get countdown timer
+     addu   a2, 1
+     sub    a3, 1                       # FIXME - bug in ARM code???
+     bltz   a3, .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+     sw     a2, 0(a0)
+     sw     a3, 0(a1)
+     RETURN
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+     la     a0, dvmJitTraceProfilingOff
+     JALR(a0)
+     # The ra register is preserved by the JALR macro.
+     jr     ra
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_RETURN_PROF
+dvmCompiler_TEMPLATE_RETURN_PROF:
+/* File: mips/TEMPLATE_RETURN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_RETURN.S */
+    /*
+     * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+     * If the stored value in returnAddr
+     * is non-zero, the caller is compiled by the JIT thus return to the
+     * address in the code cache following the invoke instruction. Otherwise
+     * return to the special dvmJitToInterpNoChain entry point.
+     */
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    # a0=rSELF
+    move    a0, rSELF
+    la      t9, dvmFastMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    SAVEAREA_FROM_FP(a0, rFP)           # a0<- saveArea (old)
+    lw      t0, offStackSaveArea_prevFrame(a0)     # t0<- saveArea->prevFrame
+    lbu     t1, offThread_breakFlags(rSELF)        # t1<- breakFlags
+    lw      rPC, offStackSaveArea_savedPc(a0)      # rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    lw      t2,  offStackSaveArea_returnAddr(a0)   # t2<- chaining cell ret
+#else
+    move    t2, zero                               # disable chaining
+#endif
+    lw      a2, offStackSaveArea_method - sizeofStackSaveArea(t0)
+                                                   # a2<- method we're returning to
+#if !defined(WITH_SELF_VERIFICATION)
+    beq     a2, zero, 1f                           # bail to interpreter
+#else
+    bne     a2, zero, 2f
+    JALR(ra)                                       # punt to interpreter and compare state
+    # DOUG: assume this does not return ???
+2:
+#endif
+    la      t4, .LdvmJitToInterpNoChainNoProfile   # defined in footer.S
+    lw      a1, (t4)
+    move    rFP, t0                                # publish new FP
+    beq     a2, zero, 4f
+    lw      t0, offMethod_clazz(a2)                # t0<- method->clazz
+4:
+
+    sw      a2, offThread_method(rSELF)            # self->method = newSave->method
+    lw      a0, offClassObject_pDvmDex(t0)         # a0<- method->clazz->pDvmDex
+    sw      rFP, offThread_curFrame(rSELF)         # self->curFrame = fp
+    add     rPC, rPC, 3*2                          # publish new rPC
+    sw      a0, offThread_methodClassDex(rSELF)
+    movn    t2, zero, t1                           # check the breadFlags and
+                                                   # clear the chaining cell address
+    sw      t2, offThread_inJitCodeCache(rSELF)    # in code cache or not
+    beq     t2, zero, 3f                           # chaining cell exists?
+    JALR(t2)                                       # jump to the chaining cell
+    # DOUG: assume this does not return ???
+3:
+#if defined(WITH_JIT_TUNING)
+    li      a0, kCallsiteInterpreted
+#endif
+    j       a1                                     # callsite is interpreted
+1:
+    sw      zero, offThread_inJitCodeCache(rSELF)  # reset inJitCodeCache
+    SAVE_PC_TO_SELF()                              # SAVE_PC_FP_TO_SELF()
+    SAVE_FP_TO_SELF()
+    la      t4, .LdvmMterpStdBail                  # defined in footer.S
+    lw      a2, (t4)
+    move    a0, rSELF                              # Expecting rSELF in a0
+    JALR(a2)                                       # exit the interpreter
+    # DOUG: assume this does not return ???
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+    /*
+     * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+     * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+     * runtime-resolved callee.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    lw     t0, offMethod_accessFlags(a0)          # t0<- methodToCall->accessFlags
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+
+2:
+    and    t6, t0, ACC_NATIVE
+    beqz   t6, 3f
+#if !defined(WITH_SELF_VERIFICATION)
+    j      .LinvokeNative
+#else
+    RETURN                                        # bail to the interpreter
+#endif
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     t0, .LdvmJitToInterpTraceSelectNoChain # defined in footer.S
+    lw     rTEMP, (t0)
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- method->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve rTEMP,a1-a3
+    SCRATCH_STORE(rTEMP, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    # a0=methodToCall, a1=rSELF
+    move   a1, rSELF
+    la     t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a1-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(rTEMP, 0)
+#endif
+
+    # Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    li     a0, kInlineCacheMiss
+#endif
+    jr     rTEMP                                  # dvmJitToInterpTraceSelectNoChain
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+    /*
+     * For monomorphic callsite, setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    # methodToCall is guaranteed to be non-native
+.LinvokeChainProf:
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lh     a2, offMethod_outsSize(a0)             # a2<- methodToCall->outsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    add    t2, ra, 8                              # setup the punt-to-interp address
+                                                  # 8 bytes skips branch and delay slot
+    sll    t6, a2, 2                              # multiply outsSize by 4 (4 bytes per reg)
+    sub    t0, t0, t6                             # t0<- bottom (newsave-outsSize)
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    jr     t2                                     # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    lw     t9, offMethod_clazz(a0)                # t9<- methodToCall->clazz
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    beqz   t8, 2f                                 # breakFlags != 0
+    jr     t2                                     # bail to the interpreter
+
+2:
+    lw     a3, offClassObject_pDvmDex(t9)         # a3<- methodToCall->clazz->pDvmDex
+
+    # Update "thread" values for the new method
+    sw     a0, offThread_method(rSELF)            # self->method = methodToCall
+    sw     a3, offThread_methodClassDex(rSELF)    # self->methodClassDex = ...
+    move   rFP, a1                                # fp = newFp
+    sw     rFP, offThread_curFrame(rSELF)         # self->curFrame = newFp
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # preserve a0-a2 and ra
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(ra, 12)
+
+    move   a1, rSELF
+    # a0=methodToCall, a1=rSELF
+    la     t9, dvmFastMethodTraceEnter
+    jalr   t9
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a2 and ra
+    SCRATCH_LOAD(ra, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+#endif
+    RETURN                                        # return to the callee-chaining cell
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+    /*
+     * For polymorphic callsite, check whether the cached class pointer matches
+     * the current one. If so setup the Dalvik frame and return to the
+     * Thumb code through the link register to transfer control to the callee
+     * method through a dedicated chaining cell.
+     *
+     * The predicted chaining cell is declared in ArmLIR.h with the
+     * following layout:
+     *
+     *  typedef struct PredictedChainingCell {
+     *      u4 branch;
+     *      u4 delay_slot;
+     *      const ClassObject *clazz;
+     *      const Method *method;
+     *      u4 counter;
+     *  } PredictedChainingCell;
+     *
+     * Upon returning to the callsite:
+     *    - lr   : to branch to the chaining cell
+     *    - lr+8 : to punt to the interpreter
+     *    - lr+16: to fully resolve the callee and may rechain.
+     *             a3 <- class
+     */
+    # a0 = this, a1 = returnCell, a2 = predictedChainCell, rPC = dalvikCallsite
+    lw      a3, offObject_clazz(a0)     # a3 <- this->class
+    lw      rIBASE, 8(a2)                   # t0 <- predictedChainCell->clazz
+    lw      a0, 12(a2)                  # a0 <- predictedChainCell->method
+    lw      t1, offThread_icRechainCount(rSELF)    # t1 <- shared rechainCount
+
+#if defined(WITH_JIT_TUNING)
+    la      rINST, .LdvmICHitCount
+    #add     t2, t2, 1
+    bne    a3, rIBASE, 1f
+    nop
+    lw      t2, 0(rINST)
+    add     t2, t2, 1
+    sw      t2, 0(rINST)
+1:
+    #add     t2, t2, 1
+#endif
+    beq     a3, rIBASE, .LinvokeChainProf       # branch if predicted chain is valid
+    lw      rINST, offClassObject_vtable(a3)     # rINST <- this->class->vtable
+    beqz    rIBASE, 2f                      # initialized class or not
+    sub     a1, t1, 1                   # count--
+    sw      a1, offThread_icRechainCount(rSELF)   # write back to InterpState
+    b       3f
+2:
+    move    a1, zero
+3:
+    add     ra, ra, 16                  # return to fully-resolve landing pad
+    /*
+     * a1 <- count
+     * a2 <- &predictedChainCell
+     * a3 <- this->class
+     * rPC <- dPC
+     * rINST <- this->class->vtable
+     */
+    RETURN
+
+#undef TEMPLATE_INLINE_PROFILING
+
+/* ------------------------------ */
+    .balign 4
+    .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE_PROF:
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE_PROF.S */
+#define TEMPLATE_INLINE_PROFILING
+/* File: mips/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+    # a0 = methodToCall, a1 = returnCell, rPC = dalvikCallsite
+    lh     t7, offMethod_registersSize(a0)        # t7<- methodToCall->regsSize
+    lw     t9, offThread_interpStackEnd(rSELF)    # t9<- interpStackEnd
+    lbu    t8, offThread_breakFlags(rSELF)        # t8<- breakFlags
+    move   a3, a1                                 # a3<- returnCell
+    SAVEAREA_FROM_FP(a1, rFP)                     # a1<- stack save area
+    sll    t6, t7, 2                              # multiply regsSize by 4 (4 bytes per reg)
+    sub    a1, a1, t6                             # a1<- newFp(old savearea-regsSize)
+    SAVEAREA_FROM_FP(t0, a1)                      # t0<- stack save area
+    bgeu   t0, t9, 1f                             # bottom < interpStackEnd?
+    RETURN                                        # return to raise stack overflow excep.
+
+1:
+    # a1 = newFP, a0 = methodToCall, a3 = returnCell, rPC = dalvikCallsite
+    sw     rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+    sw     rPC, (offStackSaveArea_savedPc - sizeofStackSaveArea)(a1)
+    lw     rPC, offMethod_insns(a0)               # rPC<- methodToCall->insns
+
+    # set up newSaveArea
+    sw     rFP, (offStackSaveArea_prevFrame - sizeofStackSaveArea)(a1)
+    sw     a3, (offStackSaveArea_returnAddr - sizeofStackSaveArea)(a1)
+    sw     a0, (offStackSaveArea_method - sizeofStackSaveArea)(a1)
+    lw     rTEMP, offMethod_nativeFunc(a0)        # t9<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+    beqz   t8, 2f                                 # breakFlags != 0
+    RETURN                                        # bail to the interpreter
+2:
+#else
+    RETURN                                        # bail to the interpreter unconditionally
+#endif
+
+    # go ahead and transfer control to the native code
+    lw     t6, offThread_jniLocal_topCookie(rSELF)  # t6<- thread->localRef->...
+    sw     a1, offThread_curFrame(rSELF)          # self->curFrame = newFp
+    sw     zero, offThread_inJitCodeCache(rSELF)  # not in the jit code cache
+    sw     t6, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                  # newFp->localRefCookie=top
+    SAVEAREA_FROM_FP(rBIX, a1)                    # rBIX<- new stack save area
+    move   a2, a0                                 # a2<- methodToCall
+    move   a0, a1                                 # a0<- newFp
+    add    a1, rSELF, offThread_retval            # a1<- &retval
+    move   a3, rSELF                              # a3<- self
+#if defined(TEMPLATE_INLINE_PROFILING)
+    # a2: methodToCall
+    # preserve rTEMP,a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(rTEMP, 16)
+
+    move   a0, a2
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)                                      # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+    # restore rTEMP,a0-a3
+    SCRATCH_LOAD(rTEMP, 16)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    move   rOBJ, a2                               # save a2
+#endif
+    move   t9, rTEMP
+    JALR(t9)                                   # off to the native code
+    lw     gp, STACK_OFFSET_GP(sp)
+
+#if defined(TEMPLATE_INLINE_PROFILING)
+    move   a0, rOBJ
+    move   a1, rSELF
+    # a0=JNIMethod, a1=rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw     gp, STACK_OFFSET_GP(sp)
+#endif
+
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC (advance 3 dalvik instr)
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+#undef TEMPLATE_INLINE_PROFILING
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: mips/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .section .data.rel.ro
+    .align  4
+.LinvokeNative:
+    # Prep for the native call
+    # a1 = newFP, a0 = methodToCall
+    lw     t9, offThread_jniLocal_topCookie(rSELF)  # t9<- thread->localRef->...
+    sw     zero, offThread_inJitCodeCache(rSELF)    # not in jit code cache
+    sw     a1, offThread_curFrame(rSELF)            # self->curFrame = newFp
+    sw     t9, (offStackSaveArea_localRefCookie - sizeofStackSaveArea)(a1)
+                                                 # newFp->localRefCookie=top
+    lhu     ra, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(rBIX, a1)                   # rBIX<- new stack save area
+
+    move    a2, a0                               # a2<- methodToCall
+    move    a0, a1                               # a0<- newFp
+    add     a1, rSELF, offThread_retval          # a1<- &retval
+    move    a3, rSELF                            # a3<- self
+    andi    ra, kSubModeMethodTrace
+    beqz    ra, 121f
+    # a2: methodToCall
+    # preserve a0-a3
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastMethodTraceEnter
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a0-a3
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                      # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+    # restore a2 again
+    SCRATCH_LOAD(a2, 8)
+
+    move    a0, a2
+    move    a1, rSELF
+    la      t9, dvmFastNativeMethodTraceExit
+    JALR(t9)
+    lw      gp, STACK_OFFSET_GP(sp)
+    b       212f
+
+121:
+    lw      t9, offMethod_nativeFunc(a2)
+    JALR(t9)                                     # call methodToCall->nativeFunc
+    lw      gp, STACK_OFFSET_GP(sp)
+
+212:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw     a2, offStackSaveArea_returnAddr(rBIX)     # a2 = chaining cell ret addr
+    lw     a0, offStackSaveArea_localRefCookie(rBIX) # a0<- saved->top
+    lw     a1, offThread_exception(rSELF)            # check for exception
+    sw     rFP, offThread_curFrame(rSELF)            # self->curFrame = fp
+    sw     a0, offThread_jniLocal_topCookie(rSELF)   # new top <- old top
+    lw     a0, offStackSaveArea_savedPc(rBIX)        # reload rPC
+
+    # a0 = dalvikCallsitePC
+    bnez   a1, .LhandleException                     # handle exception if any
+
+    sw     a2, offThread_inJitCodeCache(rSELF)       # set the mode properly
+    beqz   a2, 3f
+    jr     a2                                        # go if return chaining cell still exist
+
+3:
+    # continue executing the next instruction through the interpreter
+    la     a1, .LdvmJitToInterpTraceSelectNoChain    # defined in footer.S
+    lw     a1, (a1)
+    add    rPC, a0, 3*2                              # reconstruct new rPC
+
+#if defined(WITH_JIT_TUNING)
+    li     a0, kCallsiteInterpreted
+#endif
+    jr     a1
+
+
+/*
+ * On entry:
+ * a0  Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+    la     t0, .LdeadFood
+    lw     t0, (t0)                  # should not see this under self-verification mode
+    jr     t0
+.LdeadFood:
+    .word   0xdeadf00d
+#endif
+    sw     zero, offThread_inJitCodeCache(rSELF)  # in interpreter land
+    la     a1, .LdvmMterpCommonExceptionThrown  # PIC way of getting &func
+    lw     a1, (a1)
+    la     rIBASE, .LdvmAsmInstructionStart     # PIC way of getting &func
+    lw     rIBASE, (rIBASE)
+    move   rPC, a0                              # reload the faulting Dalvid address
+    jr     a1                                   # branch to dvmMterpCommonExeceptionThrown
+
+    .align  4
+.LdvmAsmInstructionStart:
+    .word   dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+    .word   dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+    .word   dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+    .word   dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+    .word   dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+    .word   dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+
+    .global dvmCompilerTemplateEnd
+dvmCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/rebuild.sh b/vm/compiler/template/rebuild.sh
new file mode 100755
index 0000000..60f45be
--- /dev/null
+++ b/vm/compiler/template/rebuild.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 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.
+
+#
+# Rebuild for all known targets.  Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+for arch in ia32 armv5te armv5te-vfp armv7-a armv7-a-neon mips; do TARGET_ARCH_EXT=$arch make -f Makefile-template; done
diff --git a/vm/dalvik b/vm/dalvik
new file mode 100644
index 0000000..431f0e9
--- /dev/null
+++ b/vm/dalvik
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright (C) 2011 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.
+
+function follow_links() {
+  file="$1"
+  while [ -h "$file" ]; do
+    # On Mac OS, readlink -f doesn't work.
+    file="$(readlink "$file")"
+  done
+  echo "$file"
+}
+
+PROG_NAME="$(follow_links "$BASH_SOURCE")"
+PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/"
+ANDROID_DATA=$PWD/android-data$$
+
+mkdir -p $ANDROID_DATA/dalvik-cache
+ANDROID_PRINTF_LOG=tag \
+ANDROID_LOG_TAGS="" \
+ANDROID_DATA=$ANDROID_DATA \
+ANDROID_ROOT=$ANDROID_BUILD_TOP/out/host/linux-x86 \
+LD_LIBRARY_PATH=$ANDROID_BUILD_TOP/out/host/linux-x86/lib \
+$ANDROID_BUILD_TOP/out/host/linux-x86/bin/dalvikvm \
+-XXlib:libdvm.so \
+-Xbootclasspath\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/core-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/conscrypt-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/okhttp-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/bouncycastle-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/apache-xml-hostdex.jar \
+$*
+EXIT_STATUS=$?
+rm -rf $ANDROID_DATA
+exit $EXIT_STATUS
diff --git a/vm/hprof/Hprof.cpp b/vm/hprof/Hprof.cpp
new file mode 100644
index 0000000..d1dd499
--- /dev/null
+++ b/vm/hprof/Hprof.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Preparation and completion of hprof data generation.  The output is
+ * written into two files and then combined.  This is necessary because
+ * we generate some of the data (strings and classes) while we dump the
+ * heap, and some analysis tools require that the class and string data
+ * appear first.
+ */
+
+#include "Hprof.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/Visit.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+
+#define kHeadSuffix "-hptemp"
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+                              bool directToDdms)
+{
+    hprofStartup_String();
+    hprofStartup_Class();
+
+    hprof_context_t *ctx = (hprof_context_t *)calloc(1, sizeof(*ctx));
+    if (ctx == NULL) {
+        ALOGE("hprof: can't allocate context.");
+        return NULL;
+    }
+
+    /* pass in name or descriptor of the output file */
+    hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
+
+    assert(ctx->memFp != NULL);
+
+    return ctx;
+}
+
+/*
+ * Finish up the hprof dump.  Returns true on success.
+ */
+bool hprofShutdown(hprof_context_t *tailCtx)
+{
+    /* flush the "tail" portion of the output */
+    hprofFlushCurrentRecord(tailCtx);
+
+    /*
+     * Create a new context struct for the start of the file.  We
+     * heap-allocate it so we can share the "free" function.
+     */
+    hprof_context_t *headCtx = (hprof_context_t *)calloc(1, sizeof(*headCtx));
+    if (headCtx == NULL) {
+        ALOGE("hprof: can't allocate context.");
+        hprofFreeContext(tailCtx);
+        return false;
+    }
+    hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
+        tailCtx->directToDdms);
+
+    ALOGI("hprof: dumping heap strings to \"%s\".", tailCtx->fileName);
+    hprofDumpStrings(headCtx);
+    hprofDumpClasses(headCtx);
+
+    /* Write a dummy stack trace record so the analysis
+     * tools don't freak out.
+     */
+    hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
+    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
+    hprofAddU4ToRecord(&headCtx->curRec, 0);    // no frames
+
+    hprofFlushCurrentRecord(headCtx);
+
+    hprofShutdown_Class();
+    hprofShutdown_String();
+
+    /* flush to ensure memstream pointer and size are updated */
+    fflush(headCtx->memFp);
+    fflush(tailCtx->memFp);
+
+    if (tailCtx->directToDdms) {
+        /* send the data off to DDMS */
+        struct iovec iov[2];
+        iov[0].iov_base = headCtx->fileDataPtr;
+        iov[0].iov_len = headCtx->fileDataSize;
+        iov[1].iov_base = tailCtx->fileDataPtr;
+        iov[1].iov_len = tailCtx->fileDataSize;
+        dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+    } else {
+        /*
+         * Open the output file, and copy the head and tail to it.
+         */
+        assert(headCtx->fd == tailCtx->fd);
+
+        int outFd;
+        if (headCtx->fd >= 0) {
+            outFd = dup(headCtx->fd);
+            if (outFd < 0) {
+                ALOGE("dup(%d) failed: %s", headCtx->fd, strerror(errno));
+                /* continue to fail-handler below */
+            }
+        } else {
+            outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+            if (outFd < 0) {
+                ALOGE("can't open %s: %s", headCtx->fileName, strerror(errno));
+                /* continue to fail-handler below */
+            }
+        }
+        if (outFd < 0) {
+            hprofFreeContext(headCtx);
+            hprofFreeContext(tailCtx);
+            return false;
+        }
+
+        int result;
+        result = sysWriteFully(outFd, headCtx->fileDataPtr,
+            headCtx->fileDataSize, "hprof-head");
+        result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
+            tailCtx->fileDataSize, "hprof-tail");
+        close(outFd);
+        if (result != 0) {
+            hprofFreeContext(headCtx);
+            hprofFreeContext(tailCtx);
+            return false;
+        }
+    }
+
+    /* throw out a log message for the benefit of "runhat" */
+    ALOGI("hprof: heap dump completed (%dKB)",
+        (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
+
+    hprofFreeContext(headCtx);
+    hprofFreeContext(tailCtx);
+
+    return true;
+}
+
+/*
+ * Free any heap-allocated items in "ctx", and then free "ctx" itself.
+ */
+void hprofFreeContext(hprof_context_t *ctx)
+{
+    assert(ctx != NULL);
+
+    /* we don't own ctx->fd, do not close */
+
+    if (ctx->memFp != NULL)
+        fclose(ctx->memFp);
+    free(ctx->curRec.body);
+    free(ctx->fileName);
+    free(ctx->fileDataPtr);
+    free(ctx);
+}
+
+/*
+ * Visitor invoked on every root reference.
+ */
+static void hprofRootVisitor(void *addr, u4 threadId, RootType type, void *arg)
+{
+    static const hprof_heap_tag_t xlate[] = {
+        HPROF_ROOT_UNKNOWN,
+        HPROF_ROOT_JNI_GLOBAL,
+        HPROF_ROOT_JNI_LOCAL,
+        HPROF_ROOT_JAVA_FRAME,
+        HPROF_ROOT_NATIVE_STACK,
+        HPROF_ROOT_STICKY_CLASS,
+        HPROF_ROOT_THREAD_BLOCK,
+        HPROF_ROOT_MONITOR_USED,
+        HPROF_ROOT_THREAD_OBJECT,
+        HPROF_ROOT_INTERNED_STRING,
+        HPROF_ROOT_FINALIZING,
+        HPROF_ROOT_DEBUGGER,
+        HPROF_ROOT_REFERENCE_CLEANUP,
+        HPROF_ROOT_VM_INTERNAL,
+        HPROF_ROOT_JNI_MONITOR,
+    };
+    hprof_context_t *ctx;
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    assert(type < NELEM(xlate));
+    obj = *(Object **)addr;
+    if (obj == NULL) {
+        return;
+    }
+    ctx = (hprof_context_t *)arg;
+    ctx->gcScanState = xlate[type];
+    ctx->gcThreadSerialNumber = threadId;
+    hprofMarkRootObject(ctx, obj, 0);
+    ctx->gcScanState = 0;
+    ctx->gcThreadSerialNumber = 0;
+}
+
+/*
+ * Visitor invoked on every heap object.
+ */
+static void hprofBitmapCallback(Object *obj, void *arg)
+{
+    assert(obj != NULL);
+    assert(arg != NULL);
+    hprof_context_t *ctx = (hprof_context_t *)arg;
+    hprofDumpHeapObject(ctx, obj);
+}
+
+/*
+ * Walk the roots and heap writing heap information to the specified
+ * file.
+ *
+ * If "fd" is >= 0, the output will be written to that file descriptor.
+ * Otherwise, "fileName" is used to create an output file.
+ *
+ * If "directToDdms" is set, the other arguments are ignored, and data is
+ * sent directly to DDMS.
+ *
+ * Returns 0 on success, or an error code on failure.
+ */
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms)
+{
+    hprof_context_t *ctx;
+    int success;
+
+    assert(fileName != NULL);
+    dvmLockHeap();
+    dvmSuspendAllThreads(SUSPEND_FOR_HPROF);
+    ctx = hprofStartup(fileName, fd, directToDdms);
+    if (ctx == NULL) {
+        return -1;
+    }
+    // first record
+    hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    dvmVisitRoots(hprofRootVisitor, ctx);
+    dvmHeapBitmapWalk(dvmHeapSourceGetLiveBits(), hprofBitmapCallback, ctx);
+    hprofFinishHeapDump(ctx);
+//TODO: write a HEAP_SUMMARY record
+    success = hprofShutdown(ctx) ? 0 : -1;
+    dvmResumeAllThreads(SUSPEND_FOR_HPROF);
+    dvmUnlockHeap();
+    return success;
+}
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
new file mode 100644
index 0000000..c807529
--- /dev/null
+++ b/vm/hprof/Hprof.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef DALVIK_HPROF_HPROF_H_
+#define DALVIK_HPROF_HPROF_H_
+
+#include "Dalvik.h"
+
+#define HPROF_ID_SIZE (sizeof (u4))
+
+#define UNIQUE_ERROR() \
+    -((((uintptr_t)__func__) << 16 | __LINE__) & (0x7fffffff))
+
+#define HPROF_TIME 0
+#define HPROF_NULL_STACK_TRACE   0
+#define HPROF_NULL_THREAD        0
+
+typedef u4 hprof_id;
+typedef hprof_id hprof_string_id;
+typedef hprof_id hprof_object_id;
+typedef hprof_id hprof_class_object_id;
+
+enum hprof_basic_type {
+    hprof_basic_object = 2,
+    hprof_basic_boolean = 4,
+    hprof_basic_char = 5,
+    hprof_basic_float = 6,
+    hprof_basic_double = 7,
+    hprof_basic_byte = 8,
+    hprof_basic_short = 9,
+    hprof_basic_int = 10,
+    hprof_basic_long = 11,
+};
+
+enum hprof_tag_t {
+    HPROF_TAG_STRING = 0x01,
+    HPROF_TAG_LOAD_CLASS = 0x02,
+    HPROF_TAG_UNLOAD_CLASS = 0x03,
+    HPROF_TAG_STACK_FRAME = 0x04,
+    HPROF_TAG_STACK_TRACE = 0x05,
+    HPROF_TAG_ALLOC_SITES = 0x06,
+    HPROF_TAG_HEAP_SUMMARY = 0x07,
+    HPROF_TAG_START_THREAD = 0x0A,
+    HPROF_TAG_END_THREAD = 0x0B,
+    HPROF_TAG_HEAP_DUMP = 0x0C,
+    HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C,
+    HPROF_TAG_HEAP_DUMP_END = 0x2C,
+    HPROF_TAG_CPU_SAMPLES = 0x0D,
+    HPROF_TAG_CONTROL_SETTINGS = 0x0E,
+};
+
+/* Values for the first byte of
+ * HEAP_DUMP and HEAP_DUMP_SEGMENT
+ * records:
+ */
+enum hprof_heap_tag_t {
+    /* standard */
+    HPROF_ROOT_UNKNOWN = 0xFF,
+    HPROF_ROOT_JNI_GLOBAL = 0x01,
+    HPROF_ROOT_JNI_LOCAL = 0x02,
+    HPROF_ROOT_JAVA_FRAME = 0x03,
+    HPROF_ROOT_NATIVE_STACK = 0x04,
+    HPROF_ROOT_STICKY_CLASS = 0x05,
+    HPROF_ROOT_THREAD_BLOCK = 0x06,
+    HPROF_ROOT_MONITOR_USED = 0x07,
+    HPROF_ROOT_THREAD_OBJECT = 0x08,
+    HPROF_CLASS_DUMP = 0x20,
+    HPROF_INSTANCE_DUMP = 0x21,
+    HPROF_OBJECT_ARRAY_DUMP = 0x22,
+    HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
+
+    /* Android */
+    HPROF_HEAP_DUMP_INFO = 0xfe,
+    HPROF_ROOT_INTERNED_STRING = 0x89,
+    HPROF_ROOT_FINALIZING = 0x8a,  /* obsolete */
+    HPROF_ROOT_DEBUGGER = 0x8b,
+    HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  /* obsolete */
+    HPROF_ROOT_VM_INTERNAL = 0x8d,
+    HPROF_ROOT_JNI_MONITOR = 0x8e,
+    HPROF_UNREACHABLE = 0x90,  /* obsolete */
+    HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+};
+
+/* Represents a top-level hprof record, whose serialized
+ * format is:
+ *
+ *     u1     TAG: denoting the type of the record
+ *     u4     TIME: number of microseconds since the time stamp in the header
+ *     u4     LENGTH: number of bytes that follow this u4 field
+ *                    and belong to this record
+ *     [u1]*  BODY: as many bytes as specified in the above u4 field
+ */
+struct hprof_record_t {
+    unsigned char *body;
+    u4 time;
+    u4 length;
+    size_t allocLen;
+    u1 tag;
+    bool dirty;
+};
+
+enum HprofHeapId {
+    HPROF_HEAP_DEFAULT = 0,
+    HPROF_HEAP_ZYGOTE = 'Z',
+    HPROF_HEAP_APP = 'A'
+};
+
+struct hprof_context_t {
+    /* curRec *must* be first so that we
+     * can cast from a context to a record.
+     */
+    hprof_record_t curRec;
+
+    u4 gcThreadSerialNumber;
+    u1 gcScanState;
+    HprofHeapId currentHeap;    // which heap we're currently emitting
+    u4 stackTraceSerialNumber;
+    size_t objectsInSegment;
+
+    /*
+     * If directToDdms is set, "fileName" and "fd" will be ignored.
+     * Otherwise, "fileName" must be valid, though if "fd" >= 0 it will
+     * only be used for debug messages.
+     */
+    bool directToDdms;
+    char *fileName;
+    char *fileDataPtr;          // for open_memstream
+    size_t fileDataSize;        // for open_memstream
+    FILE *memFp;
+    int fd;
+};
+
+
+/*
+ * HprofString.cpp functions
+ */
+
+hprof_string_id hprofLookupStringId(const char *str);
+
+int hprofDumpStrings(hprof_context_t *ctx);
+
+int hprofStartup_String(void);
+int hprofShutdown_String(void);
+
+
+/*
+ * HprofClass.cpp functions
+ */
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz);
+
+int hprofDumpClasses(hprof_context_t *ctx);
+
+int hprofStartup_Class(void);
+int hprofShutdown_Class(void);
+
+
+/*
+ * HprofHeap.cpp functions
+ */
+
+int hprofStartHeapDump(hprof_context_t *ctx);
+int hprofFinishHeapDump(hprof_context_t *ctx);
+
+int hprofSetGcScanState(hprof_context_t *ctx,
+                        hprof_heap_tag_t state, u4 threadSerialNumber);
+void hprofMarkRootObject(hprof_context_t *ctx,
+                         const Object *obj, jobject jniObj);
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj);
+
+/*
+ * HprofOutput.cpp functions
+ */
+
+void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+                      bool writeHeader, bool directToDdms);
+
+int hprofFlushRecord(hprof_record_t *rec, FILE *fp);
+int hprofFlushCurrentRecord(hprof_context_t *ctx);
+int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time);
+
+int hprofAddU1ToRecord(hprof_record_t *rec, u1 value);
+int hprofAddU1ListToRecord(hprof_record_t *rec,
+                           const u1 *values, size_t numValues);
+
+int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str);
+
+int hprofAddU2ToRecord(hprof_record_t *rec, u2 value);
+int hprofAddU2ListToRecord(hprof_record_t *rec,
+                           const u2 *values, size_t numValues);
+
+int hprofAddU4ToRecord(hprof_record_t *rec, u4 value);
+int hprofAddU4ListToRecord(hprof_record_t *rec,
+                           const u4 *values, size_t numValues);
+
+int hprofAddU8ToRecord(hprof_record_t *rec, u8 value);
+int hprofAddU8ListToRecord(hprof_record_t *rec,
+                           const u8 *values, size_t numValues);
+
+#define hprofAddIdToRecord(rec, id) hprofAddU4ToRecord((rec), (u4)(id))
+#define hprofAddIdListToRecord(rec, values, numValues) \
+            hprofAddU4ListToRecord((rec), (const u4 *)(values), (numValues))
+
+/*
+ * Hprof.cpp functions
+ */
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+    bool directToDdms);
+bool hprofShutdown(hprof_context_t *ctx);
+void hprofFreeContext(hprof_context_t *ctx);
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms);
+
+#endif  // DALVIK_HPROF_HPROF_H_
diff --git a/vm/hprof/HprofClass.cpp b/vm/hprof/HprofClass.cpp
new file mode 100644
index 0000000..023efdb
--- /dev/null
+++ b/vm/hprof/HprofClass.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Class object pool
+ */
+
+#include "Hprof.h"
+
+static HashTable *gClassHashTable;
+
+int hprofStartup_Class()
+{
+    gClassHashTable = dvmHashTableCreate(128, NULL);
+    if (gClassHashTable == NULL) {
+        return UNIQUE_ERROR();
+    }
+    return 0;
+}
+
+int hprofShutdown_Class()
+{
+    dvmHashTableFree(gClassHashTable);
+
+    return 0;
+}
+
+static u4 computeClassHash(const ClassObject *clazz)
+{
+    u4 hash;
+    const char *cp;
+    char c;
+
+    cp = clazz->descriptor;
+    hash = (u4)clazz->classLoader;
+    while ((c = *cp++) != '\0') {
+        hash = hash * 31 + c;
+    }
+
+    return hash;
+}
+
+static int classCmp(const void *v1, const void *v2)
+{
+    const ClassObject *c1 = (const ClassObject *)v1;
+    const ClassObject *c2 = (const ClassObject *)v2;
+    intptr_t diff;
+
+    diff = (uintptr_t)c1->classLoader - (uintptr_t)c2->classLoader;
+    if (diff == 0) {
+        return strcmp(c1->descriptor, c2->descriptor);
+    }
+    return diff;
+}
+
+static int getPrettyClassNameId(const char *descriptor) {
+    std::string name(dvmHumanReadableDescriptor(descriptor));
+    return hprofLookupStringId(name.c_str());
+}
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz)
+{
+    void *val;
+
+    if (clazz == NULL) {
+        /* Someone's probably looking up the superclass
+         * of java.lang.Object or of a primitive class.
+         */
+        return (hprof_class_object_id)0;
+    }
+
+    dvmHashTableLock(gClassHashTable);
+
+    /* We're using the hash table as a list.
+     * TODO: replace the hash table with a more suitable structure
+     */
+    val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz),
+            (void *)clazz, classCmp, true);
+    assert(val != NULL);
+
+    dvmHashTableUnlock(gClassHashTable);
+
+    /* Make sure that the class's name is in the string table.
+     * This is a bunch of extra work that we only have to do
+     * because of the order of tables in the output file
+     * (strings need to be dumped before classes).
+     */
+    getPrettyClassNameId(clazz->descriptor);
+
+    return (hprof_class_object_id)clazz;
+}
+
+int hprofDumpClasses(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    dvmHashTableLock(gClassHashTable);
+
+    for (err = 0, dvmHashIterBegin(gClassHashTable, &iter);
+         err == 0 && !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
+        if (err == 0) {
+            const ClassObject *clazz;
+
+            clazz = (const ClassObject *)dvmHashIterData(&iter);
+            assert(clazz != NULL);
+
+            /* LOAD CLASS format:
+             *
+             * u4:     class serial number (always > 0)
+             * ID:     class object ID
+             * u4:     stack trace serial number
+             * ID:     class name string ID
+             *
+             * We use the address of the class object structure as its ID.
+             */
+            hprofAddU4ToRecord(rec, clazz->serialNumber);
+            hprofAddIdToRecord(rec, (hprof_class_object_id)clazz);
+            hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE);
+            hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor));
+        }
+    }
+
+    dvmHashTableUnlock(gClassHashTable);
+
+    return err;
+}
diff --git a/vm/hprof/HprofHeap.cpp b/vm/hprof/HprofHeap.cpp
new file mode 100644
index 0000000..8c34ff3
--- /dev/null
+++ b/vm/hprof/HprofHeap.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Heap object dump
+ */
+#include "Hprof.h"
+
+/* Set DUMP_PRIM_DATA to 1 if you want to include the contents
+ * of primitive arrays (byte arrays, character arrays, etc.)
+ * in heap dumps.  This can be a large amount of data.
+ */
+#define DUMP_PRIM_DATA 1
+
+#define OBJECTS_PER_SEGMENT     ((size_t)128)
+#define BYTES_PER_SEGMENT       ((size_t)4096)
+
+/* The static field-name for the synthetic object generated to account
+ * for class Static overhead.
+ */
+#define STATIC_OVERHEAD_NAME    "$staticOverhead"
+/* The ID for the synthetic object generated to account for class
+ * Static overhead.
+ */
+#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
+
+int hprofStartHeapDump(hprof_context_t *ctx)
+{
+    UNUSED_PARAMETER(ctx);
+
+    ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
+    ctx->currentHeap = HPROF_HEAP_DEFAULT;
+    return 0;
+}
+
+int hprofFinishHeapDump(hprof_context_t *ctx)
+{
+    return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
+}
+
+int hprofSetGcScanState(hprof_context_t *ctx,
+                        hprof_heap_tag_t state,
+                        u4 threadSerialNumber)
+{
+    /* Used by hprofMarkRootObject()
+     */
+    ctx->gcScanState = state;
+    ctx->gcThreadSerialNumber = threadSerialNumber;
+    return 0;
+}
+
+static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
+                                                    size_t *sizeOut)
+{
+    char c = sig[0];
+    hprof_basic_type ret;
+    size_t size;
+
+    switch (c) {
+    case '[':
+    case 'L': ret = hprof_basic_object;  size = 4; break;
+    case 'Z': ret = hprof_basic_boolean; size = 1; break;
+    case 'C': ret = hprof_basic_char;    size = 2; break;
+    case 'F': ret = hprof_basic_float;   size = 4; break;
+    case 'D': ret = hprof_basic_double;  size = 8; break;
+    case 'B': ret = hprof_basic_byte;    size = 1; break;
+    case 'S': ret = hprof_basic_short;   size = 2; break;
+    default: assert(false);
+    case 'I': ret = hprof_basic_int;     size = 4; break;
+    case 'J': ret = hprof_basic_long;    size = 8; break;
+    }
+
+    if (sizeOut != NULL) {
+        *sizeOut = size;
+    }
+
+    return ret;
+}
+
+static hprof_basic_type primitiveToBasicTypeAndSize(PrimitiveType prim,
+                                                    size_t *sizeOut)
+{
+    hprof_basic_type ret;
+    size_t size;
+
+    switch (prim) {
+    case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break;
+    case PRIM_CHAR:    ret = hprof_basic_char;    size = 2; break;
+    case PRIM_FLOAT:   ret = hprof_basic_float;   size = 4; break;
+    case PRIM_DOUBLE:  ret = hprof_basic_double;  size = 8; break;
+    case PRIM_BYTE:    ret = hprof_basic_byte;    size = 1; break;
+    case PRIM_SHORT:   ret = hprof_basic_short;   size = 2; break;
+    default: assert(false);
+    case PRIM_INT:     ret = hprof_basic_int;     size = 4; break;
+    case PRIM_LONG:    ret = hprof_basic_long;    size = 8; break;
+    }
+
+    if (sizeOut != NULL) {
+        *sizeOut = size;
+    }
+
+    return ret;
+}
+
+/* Always called when marking objects, but only does
+ * something when ctx->gcScanState is non-zero, which is usually
+ * only true when marking the root set or unreachable
+ * objects.  Used to add rootset references to obj.
+ */
+void hprofMarkRootObject(hprof_context_t *ctx, const Object *obj,
+                         jobject jniObj)
+{
+    hprof_record_t *rec = &ctx->curRec;
+    hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
+
+    if (heapTag == 0) {
+        return;
+    }
+
+    if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
+        rec->length >= BYTES_PER_SEGMENT)
+    {
+        /* This flushes the old segment and starts a new one.
+         */
+        hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+        ctx->objectsInSegment = 0;
+    }
+
+    switch (heapTag) {
+    /* ID: object ID
+     */
+    case HPROF_ROOT_UNKNOWN:
+    case HPROF_ROOT_STICKY_CLASS:
+    case HPROF_ROOT_MONITOR_USED:
+    case HPROF_ROOT_INTERNED_STRING:
+    case HPROF_ROOT_FINALIZING:
+    case HPROF_ROOT_DEBUGGER:
+    case HPROF_ROOT_REFERENCE_CLEANUP:
+    case HPROF_ROOT_VM_INTERNAL:
+        hprofAddU1ToRecord(rec, heapTag);
+        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        break;
+
+    /* ID: object ID
+     * ID: JNI global ref ID
+     */
+    case HPROF_ROOT_JNI_GLOBAL:
+        hprofAddU1ToRecord(rec, heapTag);
+        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        hprofAddIdToRecord(rec, (hprof_id)jniObj);
+        break;
+
+    /* ID: object ID
+     * u4: thread serial number
+     * u4: frame number in stack trace (-1 for empty)
+     */
+    case HPROF_ROOT_JNI_LOCAL:
+    case HPROF_ROOT_JNI_MONITOR:
+    case HPROF_ROOT_JAVA_FRAME:
+        hprofAddU1ToRecord(rec, heapTag);
+        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+        hprofAddU4ToRecord(rec, (u4)-1);
+        break;
+
+    /* ID: object ID
+     * u4: thread serial number
+     */
+    case HPROF_ROOT_NATIVE_STACK:
+    case HPROF_ROOT_THREAD_BLOCK:
+        hprofAddU1ToRecord(rec, heapTag);
+        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+        break;
+
+    /* ID: thread object ID
+     * u4: thread serial number
+     * u4: stack trace serial number
+     */
+    case HPROF_ROOT_THREAD_OBJECT:
+        hprofAddU1ToRecord(rec, heapTag);
+        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+        hprofAddU4ToRecord(rec, (u4)-1);    //xxx
+        break;
+
+    default:
+        break;
+    }
+
+    ctx->objectsInSegment++;
+}
+
+static int stackTraceSerialNumber(const void *obj)
+{
+    return HPROF_NULL_STACK_TRACE;
+}
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
+{
+    const ClassObject *clazz;
+    hprof_record_t *rec = &ctx->curRec;
+    HprofHeapId desiredHeap;
+
+    desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
+
+    if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
+        rec->length >= BYTES_PER_SEGMENT)
+    {
+        /* This flushes the old segment and starts a new one.
+         */
+        hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+        ctx->objectsInSegment = 0;
+
+        /* Starting a new HEAP_DUMP resets the heap to default.
+         */
+        ctx->currentHeap = HPROF_HEAP_DEFAULT;
+    }
+
+    if (desiredHeap != ctx->currentHeap) {
+        hprof_string_id nameId;
+
+        /* This object is in a different heap than the current one.
+         * Emit a HEAP_DUMP_INFO tag to change heaps.
+         */
+        hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
+        hprofAddU4ToRecord(rec, (u4)desiredHeap);   // u4: heap id
+        switch (desiredHeap) {
+        case HPROF_HEAP_APP:
+            nameId = hprofLookupStringId("app");
+            break;
+        case HPROF_HEAP_ZYGOTE:
+            nameId = hprofLookupStringId("zygote");
+            break;
+        default:
+            /* Internal error. */
+            assert(!"Unexpected desiredHeap");
+            nameId = hprofLookupStringId("<ILLEGAL>");
+            break;
+        }
+        hprofAddIdToRecord(rec, nameId);
+        ctx->currentHeap = desiredHeap;
+    }
+
+    clazz = obj->clazz;
+
+    if (clazz == NULL) {
+        /* This object will bother HprofReader, because it has a NULL
+         * class, so just don't dump it. It could be
+         * gDvm.unlinkedJavaLangClass or it could be an object just
+         * allocated which hasn't been initialized yet.
+         */
+    } else {
+        if (dvmIsClassObject(obj)) {
+            const ClassObject *thisClass = (const ClassObject *)obj;
+            /* obj is a ClassObject.
+             */
+            int sFieldCount = thisClass->sfieldCount;
+            if (sFieldCount != 0) {
+                int byteLength = sFieldCount*sizeof(StaticField);
+                /* Create a byte array to reflect the allocation of the
+                 * StaticField array at the end of this class.
+                 */
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, byteLength);
+                hprofAddU1ToRecord(rec, hprof_basic_byte);
+                for (int i = 0; i < byteLength; i++) {
+                    hprofAddU1ToRecord(rec, 0);
+                }
+            }
+
+            hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
+            hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
+            hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
+            hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
+            hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
+            hprofAddIdToRecord(rec, (hprof_object_id)0);    // no signer
+            hprofAddIdToRecord(rec, (hprof_object_id)0);    // no prot domain
+            hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
+            hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
+            if (obj == (Object *)gDvm.classJavaLangClass) {
+                // ClassObjects have their static fields appended, so
+                // aren't all the same size. But they're at least this
+                // size.
+                hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
+            } else {
+                hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
+            }
+
+            hprofAddU2ToRecord(rec, 0);                     // empty const pool
+
+            /* Static fields
+             */
+            if (sFieldCount == 0) {
+                hprofAddU2ToRecord(rec, (u2)0);
+            } else {
+                hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
+                hprofAddIdToRecord(rec,
+                                   hprofLookupStringId(STATIC_OVERHEAD_NAME));
+                hprofAddU1ToRecord(rec, hprof_basic_object);
+                hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+                for (int i = 0; i < sFieldCount; i++) {
+                    hprof_basic_type t;
+                    size_t size;
+                    const StaticField *f = &thisClass->sfields[i];
+
+                    t = signatureToBasicTypeAndSize(f->signature, &size);
+                    hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
+                    hprofAddU1ToRecord(rec, t);
+                    if (size == 1) {
+                        hprofAddU1ToRecord(rec, (u1)f->value.b);
+                    } else if (size == 2) {
+                        hprofAddU2ToRecord(rec, (u2)f->value.c);
+                    } else if (size == 4) {
+                        hprofAddU4ToRecord(rec, (u4)f->value.i);
+                    } else if (size == 8) {
+                        hprofAddU8ToRecord(rec, (u8)f->value.j);
+                    } else {
+                        assert(false);
+                    }
+                }
+            }
+
+            /* Instance fields for this class (no superclass fields)
+             */
+            int iFieldCount = thisClass->ifieldCount;
+            hprofAddU2ToRecord(rec, (u2)iFieldCount);
+            for (int i = 0; i < iFieldCount; i++) {
+                const InstField *f = &thisClass->ifields[i];
+                hprof_basic_type t;
+
+                t = signatureToBasicTypeAndSize(f->signature, NULL);
+                hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
+                hprofAddU1ToRecord(rec, t);
+            }
+        } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+            const ArrayObject *aobj = (const ArrayObject *)obj;
+            u4 length = aobj->length;
+
+            if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+                /* obj is an object array.
+                 */
+                hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
+
+                hprofAddIdToRecord(rec, (hprof_object_id)obj);
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, length);
+                hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+                /* Dump the elements, which are always objects or NULL.
+                 */
+                hprofAddIdListToRecord(rec,
+                        (const hprof_object_id *)(void *)aobj->contents, length);
+            } else {
+                hprof_basic_type t;
+                size_t size;
+
+                t = primitiveToBasicTypeAndSize(clazz->elementClass->
+                                                primitiveType, &size);
+
+                /* obj is a primitive array.
+                 */
+#if DUMP_PRIM_DATA
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+#else
+                hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
+#endif
+
+                hprofAddIdToRecord(rec, (hprof_object_id)obj);
+                hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+                hprofAddU4ToRecord(rec, length);
+                hprofAddU1ToRecord(rec, t);
+
+#if DUMP_PRIM_DATA
+                /* Dump the raw, packed element values.
+                 */
+                if (size == 1) {
+                    hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
+                            length);
+                } else if (size == 2) {
+                    hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
+                            length);
+                } else if (size == 4) {
+                    hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
+                            length);
+                } else if (size == 8) {
+                    hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
+                            length);
+                }
+#endif
+            }
+        } else {
+            const ClassObject *sclass;
+            size_t sizePatchOffset, savedLen;
+
+            /* obj is an instance object.
+             */
+            hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
+            hprofAddIdToRecord(rec, (hprof_object_id)obj);
+            hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+            hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+            /* Reserve some space for the length of the instance
+             * data, which we won't know until we're done writing
+             * it.
+             */
+            sizePatchOffset = rec->length;
+            hprofAddU4ToRecord(rec, 0x77777777);
+
+            /* Write the instance data;  fields for this
+             * class, followed by super class fields, and so on.
+             */
+            sclass = clazz;
+            while (sclass != NULL) {
+                int ifieldCount = sclass->ifieldCount;
+                for (int i = 0; i < ifieldCount; i++) {
+                    const InstField *f = &sclass->ifields[i];
+                    size_t size;
+
+                    (void) signatureToBasicTypeAndSize(f->signature, &size);
+                    if (size == 1) {
+                        hprofAddU1ToRecord(rec,
+                                (u1)dvmGetFieldByte(obj, f->byteOffset));
+                    } else if (size == 2) {
+                        hprofAddU2ToRecord(rec,
+                                (u2)dvmGetFieldChar(obj, f->byteOffset));
+                    } else if (size == 4) {
+                        hprofAddU4ToRecord(rec,
+                                (u4)dvmGetFieldInt(obj, f->byteOffset));
+                    } else if (size == 8) {
+                        hprofAddU8ToRecord(rec,
+                                (u8)dvmGetFieldLong(obj, f->byteOffset));
+                    } else {
+                        assert(false);
+                    }
+                }
+
+                sclass = sclass->super;
+            }
+
+            /* Patch the instance field length.
+             */
+            savedLen = rec->length;
+            rec->length = sizePatchOffset;
+            hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
+            rec->length = savedLen;
+        }
+    }
+
+    ctx->objectsInSegment++;
+
+    return 0;
+}
diff --git a/vm/hprof/HprofOutput.cpp b/vm/hprof/HprofOutput.cpp
new file mode 100644
index 0000000..3a6b9d8
--- /dev/null
+++ b/vm/hprof/HprofOutput.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#include <sys/time.h>
+#include <cutils/open_memstream.h>
+#include <time.h>
+#include <errno.h>
+#include "Hprof.h"
+
+#define HPROF_MAGIC_STRING  "JAVA PROFILE 1.0.3"
+
+#define U2_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u2 value_ = (u2)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 1] = (unsigned char)(value_      ); \
+    } while (0)
+
+#define U4_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u4 value_ = (u4)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
+        buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
+        buf_[offset_ + 2] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 3] = (unsigned char)(value_      ); \
+    } while (0)
+
+#define U8_TO_BUF_BE(buf, offset, value) \
+    do { \
+        unsigned char *buf_ = (unsigned char *)(buf); \
+        int offset_ = (int)(offset); \
+        u8 value_ = (u8)(value); \
+        buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
+        buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
+        buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
+        buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
+        buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
+        buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
+        buf_[offset_ + 6] = (unsigned char)(value_ >>  8); \
+        buf_[offset_ + 7] = (unsigned char)(value_      ); \
+    } while (0)
+
+/*
+ * Initialize an hprof context struct.
+ *
+ * This will take ownership of "fileName".
+ *
+ * NOTE: ctx is expected to have been zeroed out prior to calling this
+ * function.
+ */
+void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+                      bool writeHeader, bool directToDdms)
+{
+    /*
+     * Have to do this here, because it must happen after we
+     * memset the struct (want to treat fileDataPtr/fileDataSize
+     * as read-only while the file is open).
+     */
+    FILE* fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
+    if (fp == NULL) {
+        /* not expected */
+        ALOGE("hprof: open_memstream failed: %s", strerror(errno));
+        dvmAbort();
+    }
+
+    ctx->directToDdms = directToDdms;
+    ctx->fileName = fileName;
+    ctx->memFp = fp;
+    ctx->fd = fd;
+
+    ctx->curRec.allocLen = 128;
+    ctx->curRec.body = (unsigned char *)malloc(ctx->curRec.allocLen);
+//xxx check for/return an error
+
+    if (writeHeader) {
+        char magic[] = HPROF_MAGIC_STRING;
+        unsigned char buf[4];
+        struct timeval now;
+        u8 nowMs;
+
+        /* Write the file header.
+         *
+         * [u1]*: NUL-terminated magic string.
+         */
+        fwrite(magic, 1, sizeof(magic), fp);
+
+        /* u4: size of identifiers.  We're using addresses
+         *     as IDs, so make sure a pointer fits.
+         */
+        U4_TO_BUF_BE(buf, 0, sizeof(void *));
+        fwrite(buf, 1, sizeof(u4), fp);
+
+        /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+         */
+        if (gettimeofday(&now, NULL) < 0) {
+            nowMs = 0;
+        } else {
+            nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+        }
+
+        /* u4: high word of the 64-bit time.
+         */
+        U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32));
+        fwrite(buf, 1, sizeof(u4), fp);
+
+        /* u4: low word of the 64-bit time.
+         */
+        U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL));
+        fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time
+    }
+}
+
+int hprofFlushRecord(hprof_record_t *rec, FILE *fp)
+{
+    if (rec->dirty) {
+        unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)];
+        int nb;
+
+        headBuf[0] = rec->tag;
+        U4_TO_BUF_BE(headBuf, 1, rec->time);
+        U4_TO_BUF_BE(headBuf, 5, rec->length);
+
+        nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
+        if (nb != sizeof(headBuf)) {
+            return UNIQUE_ERROR();
+        }
+        nb = fwrite(rec->body, 1, rec->length, fp);
+        if (nb != (int)rec->length) {
+            return UNIQUE_ERROR();
+        }
+
+        rec->dirty = false;
+    }
+//xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
+
+    return 0;
+}
+
+int hprofFlushCurrentRecord(hprof_context_t *ctx)
+{
+    return hprofFlushRecord(&ctx->curRec, ctx->memFp);
+}
+
+int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time)
+{
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    err = hprofFlushRecord(rec, ctx->memFp);
+    if (err != 0) {
+        return err;
+    } else if (rec->dirty) {
+        return UNIQUE_ERROR();
+    }
+
+    rec->dirty = true;
+    rec->tag = tag;
+    rec->time = time;
+    rec->length = 0;
+
+    return 0;
+}
+
+static inline int guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
+{
+    size_t minSize;
+
+    minSize = rec->length + nmore;
+    if (minSize > rec->allocLen) {
+        unsigned char *newBody;
+        size_t newAllocLen;
+
+        newAllocLen = rec->allocLen * 2;
+        if (newAllocLen < minSize) {
+            newAllocLen = rec->allocLen + nmore + nmore/2;
+        }
+        newBody = (unsigned char *)realloc(rec->body, newAllocLen);
+        if (newBody != NULL) {
+            rec->body = newBody;
+            rec->allocLen = newAllocLen;
+        } else {
+//TODO: set an error flag so future ops will fail
+            return UNIQUE_ERROR();
+        }
+    }
+
+    assert(rec->length + nmore <= rec->allocLen);
+    return 0;
+}
+
+int hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values,
+                           size_t numValues)
+{
+    int err;
+
+    err = guaranteeRecordAppend(rec, numValues);
+    if (err != 0) {
+        return err;
+    }
+
+    memcpy(rec->body + rec->length, values, numValues);
+    rec->length += numValues;
+
+    return 0;
+}
+
+int hprofAddU1ToRecord(hprof_record_t *rec, u1 value)
+{
+    int err;
+
+    err = guaranteeRecordAppend(rec, 1);
+    if (err != 0) {
+        return err;
+    }
+
+    rec->body[rec->length++] = value;
+
+    return 0;
+}
+
+int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
+{
+    /* The terminating NUL character is NOT written.
+     */
+//xxx don't do a strlen;  add and grow as necessary, until NUL
+    return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str));
+}
+
+int hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 2);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U2_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 2;
+
+    return 0;
+}
+
+int hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
+{
+    return hprofAddU2ListToRecord(rec, &value, 1);
+}
+
+int hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 4);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U4_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 4;
+
+    return 0;
+}
+
+int hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
+{
+    return hprofAddU4ListToRecord(rec, &value, 1);
+}
+
+int hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values,
+                           size_t numValues)
+{
+    int err = guaranteeRecordAppend(rec, numValues * 8);
+    if (err != 0) {
+        return err;
+    }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+    unsigned char *insert = rec->body + rec->length;
+    for (size_t i = 0; i < numValues; i++) {
+        U8_TO_BUF_BE(insert, 0, *values++);
+        insert += sizeof(*values);
+    }
+    rec->length += numValues * 8;
+
+    return 0;
+}
+
+int hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
+{
+    return hprofAddU8ListToRecord(rec, &value, 1);
+}
diff --git a/vm/hprof/HprofString.cpp b/vm/hprof/HprofString.cpp
new file mode 100644
index 0000000..3534f07
--- /dev/null
+++ b/vm/hprof/HprofString.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Common string pool for the profiler
+ */
+#include "Hprof.h"
+
+static HashTable *gStringHashTable;
+
+int hprofStartup_String()
+{
+    gStringHashTable = dvmHashTableCreate(512, free);
+    if (gStringHashTable == NULL) {
+        return UNIQUE_ERROR();
+    }
+    return 0;
+}
+
+int hprofShutdown_String()
+{
+    dvmHashTableFree(gStringHashTable);
+    return 0;
+}
+
+static u4 computeUtf8Hash(const char *str)
+{
+    u4 hash = 0;
+    const char *cp;
+    char c;
+
+    cp = str;
+    while ((c = *cp++) != '\0') {
+        hash = hash * 31 + c;
+    }
+
+    return hash;
+}
+
+hprof_string_id hprofLookupStringId(const char *str)
+{
+    void *val;
+    u4 hashValue;
+
+    dvmHashTableLock(gStringHashTable);
+
+    hashValue = computeUtf8Hash(str);
+    val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)str,
+            (HashCompareFunc)strcmp, false);
+    if (val == NULL) {
+        const char *newStr;
+
+        newStr = strdup(str);
+        val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)newStr,
+                (HashCompareFunc)strcmp, true);
+        assert(val != NULL);
+    }
+
+    dvmHashTableUnlock(gStringHashTable);
+
+    return (hprof_string_id)val;
+}
+
+int hprofDumpStrings(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+
+    dvmHashTableLock(gStringHashTable);
+
+    for (err = 0, dvmHashIterBegin(gStringHashTable, &iter);
+         err == 0 && !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        err = hprofStartNewRecord(ctx, HPROF_TAG_STRING, HPROF_TIME);
+        if (err == 0) {
+            const char *str;
+
+            str = (const char *)dvmHashIterData(&iter);
+            assert(str != NULL);
+
+            /* STRING format:
+             *
+             * ID:     ID for this string
+             * [u1]*:  UTF8 characters for string (NOT NULL terminated)
+             *         (the record format encodes the length)
+             *
+             * We use the address of the string data as its ID.
+             */
+            err = hprofAddU4ToRecord(rec, (u4)str);
+            if (err == 0) {
+                err = hprofAddUtf8StringToRecord(rec, str);
+            }
+        }
+    }
+
+    dvmHashTableUnlock(gStringHashTable);
+
+    return err;
+}
diff --git a/vm/interp/Interp.cpp b/vm/interp/Interp.cpp
new file mode 100644
index 0000000..42e2eca
--- /dev/null
+++ b/vm/interp/Interp.cpp
@@ -0,0 +1,1973 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Main interpreter entry point and support functions.
+ *
+ * The entry point selects the "standard" or "debug" interpreter and
+ * facilitates switching between them.  The standard interpreter may
+ * use the "fast" or "portable" implementation.
+ *
+ * Some debugger support functions are included here.
+ */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Debugger support
+ * ===========================================================================
+ */
+
+// fwd
+static BreakpointSet* dvmBreakpointSetAlloc();
+static void dvmBreakpointSetFree(BreakpointSet* pSet);
+
+#if defined(WITH_JIT)
+/* Target-specific save/restore */
+extern "C" void dvmJitCalleeSave(double *saveArea);
+extern "C" void dvmJitCalleeRestore(double *saveArea);
+/* Interpreter entry points from compiled code */
+extern "C" void dvmJitToInterpNormal();
+extern "C" void dvmJitToInterpNoChain();
+extern "C" void dvmJitToInterpPunt();
+extern "C" void dvmJitToInterpSingleStep();
+extern "C" void dvmJitToInterpTraceSelect();
+#if defined(WITH_SELF_VERIFICATION)
+extern "C" void dvmJitToInterpBackwardBranch();
+#endif
+#endif
+
+/*
+ * Initialize global breakpoint structures.
+ */
+bool dvmBreakpointStartup()
+{
+    gDvm.breakpointSet = dvmBreakpointSetAlloc();
+    return (gDvm.breakpointSet != NULL);
+}
+
+/*
+ * Free resources.
+ */
+void dvmBreakpointShutdown()
+{
+    dvmBreakpointSetFree(gDvm.breakpointSet);
+}
+
+
+/*
+ * This represents a breakpoint inserted in the instruction stream.
+ *
+ * The debugger may ask us to create the same breakpoint multiple times.
+ * We only remove the breakpoint when the last instance is cleared.
+ */
+struct Breakpoint {
+    Method*     method;                 /* method we're associated with */
+    u2*         addr;                   /* absolute memory address */
+    u1          originalOpcode;         /* original 8-bit opcode value */
+    int         setCount;               /* #of times this breakpoint was set */
+};
+
+/*
+ * Set of breakpoints.
+ */
+struct BreakpointSet {
+    /* grab lock before reading or writing anything else in here */
+    pthread_mutex_t lock;
+
+    /* vector of breakpoint structures */
+    int         alloc;
+    int         count;
+    Breakpoint* breakpoints;
+};
+
+/*
+ * Initialize a BreakpointSet.  Initially empty.
+ */
+static BreakpointSet* dvmBreakpointSetAlloc()
+{
+    BreakpointSet* pSet = (BreakpointSet*) calloc(1, sizeof(*pSet));
+
+    dvmInitMutex(&pSet->lock);
+    /* leave the rest zeroed -- will alloc on first use */
+
+    return pSet;
+}
+
+/*
+ * Free storage associated with a BreakpointSet.
+ */
+static void dvmBreakpointSetFree(BreakpointSet* pSet)
+{
+    if (pSet == NULL)
+        return;
+
+    free(pSet->breakpoints);
+    free(pSet);
+}
+
+/*
+ * Lock the breakpoint set.
+ *
+ * It's not currently necessary to switch to VMWAIT in the event of
+ * contention, because nothing in here can block.  However, it's possible
+ * that the bytecode-updater code could become fancier in the future, so
+ * we do the trylock dance as a bit of future-proofing.
+ */
+static void dvmBreakpointSetLock(BreakpointSet* pSet)
+{
+    if (dvmTryLockMutex(&pSet->lock) != 0) {
+        Thread* self = dvmThreadSelf();
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(&pSet->lock);
+        dvmChangeStatus(self, oldStatus);
+    }
+}
+
+/*
+ * Unlock the breakpoint set.
+ */
+static void dvmBreakpointSetUnlock(BreakpointSet* pSet)
+{
+    dvmUnlockMutex(&pSet->lock);
+}
+
+/*
+ * Return the #of breakpoints.
+ */
+static int dvmBreakpointSetCount(const BreakpointSet* pSet)
+{
+    return pSet->count;
+}
+
+/*
+ * See if we already have an entry for this address.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns the index of the breakpoint entry, or -1 if not found.
+ */
+static int dvmBreakpointSetFind(const BreakpointSet* pSet, const u2* addr)
+{
+    int i;
+
+    for (i = 0; i < pSet->count; i++) {
+        Breakpoint* pBreak = &pSet->breakpoints[i];
+        if (pBreak->addr == addr)
+            return i;
+    }
+
+    return -1;
+}
+
+/*
+ * Retrieve the opcode that was originally at the specified location.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" with the opcode in *pOrig on success.
+ */
+static bool dvmBreakpointSetOriginalOpcode(const BreakpointSet* pSet,
+    const u2* addr, u1* pOrig)
+{
+    int idx = dvmBreakpointSetFind(pSet, addr);
+    if (idx < 0)
+        return false;
+
+    *pOrig = pSet->breakpoints[idx].originalOpcode;
+    return true;
+}
+
+/*
+ * Check the opcode.  If it's a "magic" NOP, indicating the start of
+ * switch or array data in the instruction stream, we don't want to set
+ * a breakpoint.
+ *
+ * This can happen because the line number information dx generates
+ * associates the switch data with the switch statement's line number,
+ * and some debuggers put breakpoints at every address associated with
+ * a given line.  The result is that the breakpoint stomps on the NOP
+ * instruction that doubles as a data table magic number, and an explicit
+ * check in the interpreter results in an exception being thrown.
+ *
+ * We don't want to simply refuse to add the breakpoint to the table,
+ * because that confuses the housekeeping.  We don't want to reject the
+ * debugger's event request, and we want to be sure that there's exactly
+ * one un-set operation for every set op.
+ */
+static bool instructionIsMagicNop(const u2* addr)
+{
+    u2 curVal = *addr;
+    return ((GET_OPCODE(curVal)) == OP_NOP && (curVal >> 8) != 0);
+}
+
+/*
+ * Add a breakpoint at a specific address.  If the address is already
+ * present in the table, this just increments the count.
+ *
+ * For a new entry, this will extract and preserve the current opcode from
+ * the instruction stream, and replace it with a breakpoint opcode.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" on success.
+ */
+static bool dvmBreakpointSetAdd(BreakpointSet* pSet, Method* method,
+    unsigned int instrOffset)
+{
+    const int kBreakpointGrowth = 10;
+    const u2* addr = method->insns + instrOffset;
+    int idx = dvmBreakpointSetFind(pSet, addr);
+    Breakpoint* pBreak;
+
+    if (idx < 0) {
+        if (pSet->count == pSet->alloc) {
+            int newSize = pSet->alloc + kBreakpointGrowth;
+            Breakpoint* newVec;
+
+            ALOGV("+++ increasing breakpoint set size to %d", newSize);
+
+            /* pSet->breakpoints will be NULL on first entry */
+            newVec = (Breakpoint*)realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
+            if (newVec == NULL)
+                return false;
+
+            pSet->breakpoints = newVec;
+            pSet->alloc = newSize;
+        }
+
+        pBreak = &pSet->breakpoints[pSet->count++];
+        pBreak->method = method;
+        pBreak->addr = (u2*)addr;
+        pBreak->originalOpcode = *(u1*)addr;
+        pBreak->setCount = 1;
+
+        /*
+         * Change the opcode.  We must ensure that the BreakpointSet
+         * updates happen before we change the opcode.
+         *
+         * If the method has not been verified, we do NOT insert the
+         * breakpoint yet, since that will screw up the verifier.  The
+         * debugger is allowed to insert breakpoints in unverified code,
+         * but since we don't execute unverified code we don't need to
+         * alter the bytecode yet.
+         *
+         * The class init code will "flush" all pending opcode writes
+         * before verification completes.
+         */
+        assert(*(u1*)addr != OP_BREAKPOINT);
+        if (dvmIsClassVerified(method->clazz)) {
+            ALOGV("Class %s verified, adding breakpoint at %p",
+                method->clazz->descriptor, addr);
+            if (instructionIsMagicNop(addr)) {
+                ALOGV("Refusing to set breakpoint on %04x at %s.%s + %#x",
+                    *addr, method->clazz->descriptor, method->name,
+                    instrOffset);
+            } else {
+                ANDROID_MEMBAR_FULL();
+                dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+                    OP_BREAKPOINT);
+            }
+        } else {
+            ALOGV("Class %s NOT verified, deferring breakpoint at %p",
+                method->clazz->descriptor, addr);
+        }
+    } else {
+        /*
+         * Breakpoint already exists, just increase the count.
+         */
+        pBreak = &pSet->breakpoints[idx];
+        pBreak->setCount++;
+    }
+
+    return true;
+}
+
+/*
+ * Remove one instance of the specified breakpoint.  When the count
+ * reaches zero, the entry is removed from the table, and the original
+ * opcode is restored.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetRemove(BreakpointSet* pSet, Method* method,
+    unsigned int instrOffset)
+{
+    const u2* addr = method->insns + instrOffset;
+    int idx = dvmBreakpointSetFind(pSet, addr);
+
+    if (idx < 0) {
+        /* breakpoint not found in set -- unexpected */
+        if (*(u1*)addr == OP_BREAKPOINT) {
+            ALOGE("Unable to restore breakpoint opcode (%s.%s +%#x)",
+                method->clazz->descriptor, method->name, instrOffset);
+            dvmAbort();
+        } else {
+            ALOGW("Breakpoint was already restored? (%s.%s +%#x)",
+                method->clazz->descriptor, method->name, instrOffset);
+        }
+    } else {
+        Breakpoint* pBreak = &pSet->breakpoints[idx];
+        if (pBreak->setCount == 1) {
+            /*
+             * Must restore opcode before removing set entry.
+             *
+             * If the breakpoint was never flushed, we could be ovewriting
+             * a value with the same value.  Not a problem, though we
+             * could end up causing a copy-on-write here when we didn't
+             * need to.  (Not worth worrying about.)
+             */
+            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+                pBreak->originalOpcode);
+            ANDROID_MEMBAR_FULL();
+
+            if (idx != pSet->count-1) {
+                /* shift down */
+                memmove(&pSet->breakpoints[idx], &pSet->breakpoints[idx+1],
+                    (pSet->count-1 - idx) * sizeof(pSet->breakpoints[0]));
+            }
+            pSet->count--;
+            pSet->breakpoints[pSet->count].addr = (u2*) 0xdecadead; // debug
+        } else {
+            pBreak->setCount--;
+            assert(pBreak->setCount > 0);
+        }
+    }
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".  We want to
+ * change the opcode, which might not have happened when the breakpoint
+ * was initially set because the class was in the process of being
+ * verified.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetFlush(BreakpointSet* pSet, ClassObject* clazz)
+{
+    int i;
+    for (i = 0; i < pSet->count; i++) {
+        Breakpoint* pBreak = &pSet->breakpoints[i];
+        if (pBreak->method->clazz == clazz) {
+            /*
+             * The breakpoint is associated with a method in this class.
+             * It might already be there or it might not; either way,
+             * flush it out.
+             */
+            ALOGV("Flushing breakpoint at %p for %s",
+                pBreak->addr, clazz->descriptor);
+            if (instructionIsMagicNop(pBreak->addr)) {
+                ALOGV("Refusing to flush breakpoint on %04x at %s.%s + %#x",
+                    *pBreak->addr, pBreak->method->clazz->descriptor,
+                    pBreak->method->name, pBreak->addr - pBreak->method->insns);
+            } else {
+                dvmDexChangeDex1(clazz->pDvmDex, (u1*)pBreak->addr,
+                    OP_BREAKPOINT);
+            }
+        }
+    }
+}
+
+
+/*
+ * Do any debugger-attach-time initialization.
+ */
+void dvmInitBreakpoints()
+{
+    /* quick sanity check */
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    if (dvmBreakpointSetCount(pSet) != 0) {
+        ALOGW("WARNING: %d leftover breakpoints", dvmBreakpointSetCount(pSet));
+        /* generally not good, but we can keep going */
+    }
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add an address to the list, putting it in the first non-empty slot.
+ *
+ * Sometimes the debugger likes to add two entries for one breakpoint.
+ * We add two entries here, so that we get the right behavior when it's
+ * removed twice.
+ *
+ * This will only be run from the JDWP thread, and it will happen while
+ * we are updating the event list, which is synchronized.  We're guaranteed
+ * to be the only one adding entries, and the lock ensures that nobody
+ * will be trying to remove them while we're in here.
+ *
+ * "addr" is the absolute address of the breakpoint bytecode.
+ */
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetAdd(pSet, method, instrOffset);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Remove an address from the list by setting the entry to NULL.
+ *
+ * This can be called from the JDWP thread (because the debugger has
+ * cancelled the breakpoint) or from an event thread (because it's a
+ * single-shot breakpoint, e.g. "run to line").  We only get here as
+ * the result of removing an entry from the event list, which is
+ * synchronized, so it should not be possible for two threads to be
+ * updating breakpoints at the same time.
+ */
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetRemove(pSet, method, instrOffset);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Get the original opcode from under a breakpoint.
+ *
+ * On SMP hardware it's possible one core might try to execute a breakpoint
+ * after another core has cleared it.  We need to handle the case where
+ * there's no entry in the breakpoint set.  (The memory barriers in the
+ * locks and in the breakpoint update code should ensure that, once we've
+ * observed the absence of a breakpoint entry, we will also now observe
+ * the restoration of the original opcode.  The fact that we're holding
+ * the lock prevents other threads from confusing things further.)
+ */
+u1 dvmGetOriginalOpcode(const u2* addr)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    u1 orig = 0;
+
+    dvmBreakpointSetLock(pSet);
+    if (!dvmBreakpointSetOriginalOpcode(pSet, addr, &orig)) {
+        orig = *(u1*)addr;
+        if (orig == OP_BREAKPOINT) {
+            ALOGE("GLITCH: can't find breakpoint, opcode is still set");
+            dvmAbort();
+        }
+    }
+    dvmBreakpointSetUnlock(pSet);
+
+    return orig;
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ *
+ * We don't want to modify the bytecode of a method before the verifier
+ * gets a chance to look at it, so we postpone opcode replacement until
+ * after verification completes.
+ */
+void dvmFlushBreakpoints(ClassObject* clazz)
+{
+    BreakpointSet* pSet = gDvm.breakpointSet;
+
+    if (pSet == NULL)
+        return;
+
+    assert(dvmIsClassVerified(clazz));
+    dvmBreakpointSetLock(pSet);
+    dvmBreakpointSetFlush(pSet, clazz);
+    dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add a single step event.  Currently this is a global item.
+ *
+ * We set up some initial values based on the thread's current state.  This
+ * won't work well if the thread is running, so it's up to the caller to
+ * verify that it's suspended.
+ *
+ * This is only called from the JDWP thread.
+ */
+bool dvmAddSingleStep(Thread* thread, int size, int depth)
+{
+    StepControl* pCtrl = &gDvm.stepControl;
+
+    if (pCtrl->active && thread != pCtrl->thread) {
+        ALOGW("WARNING: single-step active for %p; adding %p",
+            pCtrl->thread, thread);
+
+        /*
+         * Keep going, overwriting previous.  This can happen if you
+         * suspend a thread in Object.wait, hit the single-step key, then
+         * switch to another thread and do the same thing again.
+         * The first thread's step is still pending.
+         *
+         * TODO: consider making single-step per-thread.  Adds to the
+         * overhead, but could be useful in rare situations.
+         */
+    }
+
+    pCtrl->size = static_cast<JdwpStepSize>(size);
+    pCtrl->depth = static_cast<JdwpStepDepth>(depth);
+    pCtrl->thread = thread;
+
+    /*
+     * We may be stepping into or over method calls, or running until we
+     * return from the current method.  To make this work we need to track
+     * the current line, current method, and current stack depth.  We need
+     * to be checking these after most instructions, notably those that
+     * call methods, return from methods, or are on a different line from the
+     * previous instruction.
+     *
+     * We have to start with a snapshot of the current state.  If we're in
+     * an interpreted method, everything we need is in the current frame.  If
+     * we're in a native method, possibly with some extra JNI frames pushed
+     * on by PushLocalFrame, we want to use the topmost native method.
+     */
+    const StackSaveArea* saveArea;
+    u4* fp;
+    u4* prevFp = NULL;
+
+    for (fp = thread->interpSave.curFrame; fp != NULL;
+         fp = saveArea->prevFrame) {
+        const Method* method;
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+        method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*)fp) && !dvmIsNativeMethod(method))
+            break;
+        prevFp = fp;
+    }
+    if (fp == NULL) {
+        ALOGW("Unexpected: step req in native-only threadid=%d",
+            thread->threadId);
+        return false;
+    }
+    if (prevFp != NULL) {
+        /*
+         * First interpreted frame wasn't the one at the bottom.  Break
+         * frames are only inserted when calling from native->interp, so we
+         * don't need to worry about one being here.
+         */
+        ALOGV("##### init step while in native method");
+        fp = prevFp;
+        assert(!dvmIsBreakFrame((u4*)fp));
+        assert(dvmIsNativeMethod(SAVEAREA_FROM_FP(fp)->method));
+        saveArea = SAVEAREA_FROM_FP(fp);
+    }
+
+    /*
+     * Pull the goodies out.  "xtra.currentPc" should be accurate since
+     * we update it on every instruction while the debugger is connected.
+     */
+    pCtrl->method = saveArea->method;
+    // Clear out any old address set
+    if (pCtrl->pAddressSet != NULL) {
+        // (discard const)
+        free((void *)pCtrl->pAddressSet);
+        pCtrl->pAddressSet = NULL;
+    }
+    if (dvmIsNativeMethod(pCtrl->method)) {
+        pCtrl->line = -1;
+    } else {
+        pCtrl->line = dvmLineNumFromPC(saveArea->method,
+                        saveArea->xtra.currentPc - saveArea->method->insns);
+        pCtrl->pAddressSet
+                = dvmAddressSetForLine(saveArea->method, pCtrl->line);
+    }
+    pCtrl->frameDepth =
+        dvmComputeVagueFrameDepth(thread, thread->interpSave.curFrame);
+    pCtrl->active = true;
+
+    ALOGV("##### step init: thread=%p meth=%p '%s' line=%d frameDepth=%d depth=%s size=%s",
+        pCtrl->thread, pCtrl->method, pCtrl->method->name,
+        pCtrl->line, pCtrl->frameDepth,
+        dvmJdwpStepDepthStr(pCtrl->depth),
+        dvmJdwpStepSizeStr(pCtrl->size));
+
+    return true;
+}
+
+/*
+ * Disable a single step event.
+ */
+void dvmClearSingleStep(Thread* thread)
+{
+    UNUSED_PARAMETER(thread);
+
+    gDvm.stepControl.active = false;
+}
+
+/*
+ * The interpreter just threw.  Handle any special subMode requirements.
+ * All interpSave state must be valid on entry.
+ */
+void dvmReportExceptionThrow(Thread* self, Object* exception)
+{
+    const Method* curMethod = self->interpSave.method;
+#if defined(WITH_JIT)
+    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+        dvmJitEndTraceSelect(self, self->interpSave.pc);
+    }
+    if (self->interpBreak.ctl.breakFlags & kInterpSingleStep) {
+        /* Discard any single-step native returns to translation */
+        self->jitResumeNPC = NULL;
+    }
+#endif
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        void *catchFrame;
+        int offset = self->interpSave.pc - curMethod->insns;
+        int catchRelPc = dvmFindCatchBlock(self, offset, exception,
+                                           true, &catchFrame);
+        dvmDbgPostException(self->interpSave.curFrame, offset, catchFrame,
+                            catchRelPc, exception);
+    }
+}
+
+/*
+ * The interpreter is preparing to do an invoke (both native & normal).
+ * Handle any special subMode requirements.  All interpSave state
+ * must be valid on entry.
+ */
+void dvmReportInvoke(Thread* self, const Method* methodToCall)
+{
+    TRACE_METHOD_ENTER(self, methodToCall);
+}
+
+/*
+ * The interpreter is preparing to do a native invoke. Handle any
+ * special subMode requirements.  NOTE: for a native invoke,
+ * dvmReportInvoke() and dvmReportPreNativeInvoke() will both
+ * be called prior to the invoke.  fp is the Dalvik FP of the calling
+ * method.
+ */
+void dvmReportPreNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
+{
+#if defined(WITH_JIT)
+    /*
+     * Actively building a trace?  If so, end it now.   The trace
+     * builder can't follow into or through a native method.
+     */
+    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+        dvmCheckJit(self->interpSave.pc, self);
+    }
+#endif
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
+        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
+        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_ENTRY);
+    }
+}
+
+/*
+ * The interpreter has returned from a native invoke. Handle any
+ * special subMode requirements.  fp is the Dalvik FP of the calling
+ * method.
+ */
+void dvmReportPostNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
+{
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
+        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
+        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_EXIT);
+    }
+    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        dvmFastNativeMethodTraceExit(methodToCall, self);
+    }
+}
+
+/*
+ * The interpreter has returned from a normal method.  Handle any special
+ * subMode requirements.  All interpSave state must be valid on entry.
+ */
+void dvmReportReturn(Thread* self)
+{
+    TRACE_METHOD_EXIT(self, self->interpSave.method);
+#if defined(WITH_JIT)
+    if (dvmIsBreakFrame(self->interpSave.curFrame) &&
+        (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild)) {
+        dvmCheckJit(self->interpSave.pc, self);
+    }
+#endif
+}
+
+/*
+ * Update the debugger on interesting events, such as hitting a breakpoint
+ * or a single-step point.  This is called from the top of the interpreter
+ * loop, before the current instruction is processed.
+ *
+ * Set "methodEntry" if we've just entered the method.  This detects
+ * method exit by checking to see if the next instruction is "return".
+ *
+ * This can't catch native method entry/exit, so we have to handle that
+ * at the point of invocation.  We also need to catch it in dvmCallMethod
+ * if we want to capture native->native calls made through JNI.
+ *
+ * Notes to self:
+ * - Don't want to switch to VMWAIT while posting events to the debugger.
+ *   Let the debugger code decide if we need to change state.
+ * - We may want to check for debugger-induced thread suspensions on
+ *   every instruction.  That would make a "suspend all" more responsive
+ *   and reduce the chances of multiple simultaneous events occurring.
+ *   However, it could change the behavior some.
+ *
+ * TODO: method entry/exit events are probably less common than location
+ * breakpoints.  We may be able to speed things up a bit if we don't query
+ * the event list unless we know there's at least one lurking within.
+ */
+static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
+                           Thread* self)
+{
+    int eventFlags = 0;
+
+    /*
+     * Update xtra.currentPc on every instruction.  We need to do this if
+     * there's a chance that we could get suspended.  This can happen if
+     * eventFlags != 0 here, or somebody manually requests a suspend
+     * (which gets handled at PERIOD_CHECKS time).  One place where this
+     * needs to be correct is in dvmAddSingleStep().
+     */
+    dvmExportPC(pc, fp);
+
+    if (self->debugIsMethodEntry) {
+        eventFlags |= DBG_METHOD_ENTRY;
+        self->debugIsMethodEntry = false;
+    }
+
+    /*
+     * See if we have a breakpoint here.
+     *
+     * Depending on the "mods" associated with event(s) on this address,
+     * we may or may not actually send a message to the debugger.
+     */
+    if (GET_OPCODE(*pc) == OP_BREAKPOINT) {
+        ALOGV("+++ breakpoint hit at %p", pc);
+        eventFlags |= DBG_BREAKPOINT;
+    }
+
+    /*
+     * If the debugger is single-stepping one of our threads, check to
+     * see if we're that thread and we've reached a step point.
+     */
+    const StepControl* pCtrl = &gDvm.stepControl;
+    if (pCtrl->active && pCtrl->thread == self) {
+        int frameDepth;
+        bool doStop = false;
+        const char* msg = NULL;
+
+        assert(!dvmIsNativeMethod(method));
+
+        if (pCtrl->depth == SD_INTO) {
+            /*
+             * Step into method calls.  We break when the line number
+             * or method pointer changes.  If we're in SS_MIN mode, we
+             * always stop.
+             */
+            if (pCtrl->method != method) {
+                doStop = true;
+                msg = "new method";
+            } else if (pCtrl->size == SS_MIN) {
+                doStop = true;
+                msg = "new instruction";
+            } else if (!dvmAddressSetGet(
+                    pCtrl->pAddressSet, pc - method->insns)) {
+                doStop = true;
+                msg = "new line";
+            }
+        } else if (pCtrl->depth == SD_OVER) {
+            /*
+             * Step over method calls.  We break when the line number is
+             * different and the frame depth is <= the original frame
+             * depth.  (We can't just compare on the method, because we
+             * might get unrolled past it by an exception, and it's tricky
+             * to identify recursion.)
+             */
+            frameDepth = dvmComputeVagueFrameDepth(self, fp);
+            if (frameDepth < pCtrl->frameDepth) {
+                /* popped up one or more frames, always trigger */
+                doStop = true;
+                msg = "method pop";
+            } else if (frameDepth == pCtrl->frameDepth) {
+                /* same depth, see if we moved */
+                if (pCtrl->size == SS_MIN) {
+                    doStop = true;
+                    msg = "new instruction";
+                } else if (!dvmAddressSetGet(pCtrl->pAddressSet,
+                            pc - method->insns)) {
+                    doStop = true;
+                    msg = "new line";
+                }
+            }
+        } else {
+            assert(pCtrl->depth == SD_OUT);
+            /*
+             * Return from the current method.  We break when the frame
+             * depth pops up.
+             *
+             * This differs from the "method exit" break in that it stops
+             * with the PC at the next instruction in the returned-to
+             * function, rather than the end of the returning function.
+             */
+            frameDepth = dvmComputeVagueFrameDepth(self, fp);
+            if (frameDepth < pCtrl->frameDepth) {
+                doStop = true;
+                msg = "method pop";
+            }
+        }
+
+        if (doStop) {
+            ALOGV("#####S %s", msg);
+            eventFlags |= DBG_SINGLE_STEP;
+        }
+    }
+
+    /*
+     * Check to see if this is a "return" instruction.  JDWP says we should
+     * send the event *after* the code has been executed, but it also says
+     * the location we provide is the last instruction.  Since the "return"
+     * instruction has no interesting side effects, we should be safe.
+     * (We can't just move this down to the returnFromMethod label because
+     * we potentially need to combine it with other events.)
+     *
+     * We're also not supposed to generate a method exit event if the method
+     * terminates "with a thrown exception".
+     */
+    u2 opcode = GET_OPCODE(*pc);
+    if (opcode == OP_RETURN_VOID || opcode == OP_RETURN || opcode == OP_RETURN_VOID_BARRIER ||
+        opcode == OP_RETURN_OBJECT || opcode == OP_RETURN_WIDE)
+    {
+        eventFlags |= DBG_METHOD_EXIT;
+    }
+
+    /*
+     * If there's something interesting going on, see if it matches one
+     * of the debugger filters.
+     */
+    if (eventFlags != 0) {
+        Object* thisPtr = dvmGetThisPtr(method, fp);
+        if (thisPtr != NULL && !dvmIsHeapAddress(thisPtr)) {
+            /*
+             * TODO: remove this check if we're confident that the "this"
+             * pointer is where it should be -- slows us down, especially
+             * during single-step.
+             */
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            ALOGE("HEY: invalid 'this' ptr %p (%s.%s %s)", thisPtr,
+                method->clazz->descriptor, method->name, desc);
+            free(desc);
+            dvmAbort();
+        }
+        dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+            eventFlags);
+    }
+}
+
+/*
+ * Recover the "this" pointer from the current interpreted method.  "this"
+ * is always in "in0" for non-static methods.
+ *
+ * The "ins" start at (#of registers - #of ins).  Note in0 != v0.
+ *
+ * This works because "dx" guarantees that it will work.  It's probably
+ * fairly common to have a virtual method that doesn't use its "this"
+ * pointer, in which case we're potentially wasting a register.  However,
+ * the debugger doesn't treat "this" as just another argument.  For
+ * example, events (such as breakpoints) can be enabled for specific
+ * values of "this".  There is also a separate StackFrame.ThisObject call
+ * in JDWP that is expected to work for any non-native non-static method.
+ *
+ * Because we need it when setting up debugger event filters, we want to
+ * be able to do this quickly.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp)
+{
+    if (dvmIsStaticMethod(method))
+        return NULL;
+    return (Object*)fp[method->registersSize - method->insSize];
+}
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+/*
+ * Verify that all internally-tracked references have been released.  If
+ * they haven't, print them and abort the VM.
+ *
+ * "debugTrackedRefStart" indicates how many refs were on the list when
+ * we were first invoked.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+    int debugTrackedRefStart)
+{
+    if (dvmReferenceTableEntries(&self->internalLocalRefTable)
+        != (size_t) debugTrackedRefStart)
+    {
+        char* desc;
+        Object** top;
+        int count;
+
+        count = dvmReferenceTableEntries(&self->internalLocalRefTable);
+
+        ALOGE("TRACK: unreleased internal reference (prev=%d total=%d)",
+            debugTrackedRefStart, count);
+        desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGE("       current method is %s.%s %s", method->clazz->descriptor,
+            method->name, desc);
+        free(desc);
+        top = self->internalLocalRefTable.table + debugTrackedRefStart;
+        while (top < self->internalLocalRefTable.nextEntry) {
+            ALOGE("  %p (%s)",
+                 *top,
+                 ((*top)->clazz != NULL) ? (*top)->clazz->descriptor : "");
+            top++;
+        }
+        dvmDumpThread(self, false);
+
+        dvmAbort();
+    }
+    //ALOGI("TRACK OK");
+}
+#endif
+
+
+#ifdef LOG_INSTR
+/*
+ * Dump the v-registers.  Sent to the ILOG log tag.
+ */
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly)
+{
+    int i, localCount;
+
+    localCount = method->registersSize - method->insSize;
+
+    ALOG(LOG_VERBOSE, LOG_TAG"i", "Registers (fp=%p):", framePtr);
+    for (i = method->registersSize-1; i >= 0; i--) {
+        if (i >= localCount) {
+            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d in%-2d : 0x%08x",
+                i, i-localCount, framePtr[i]);
+        } else {
+            if (inOnly) {
+                ALOG(LOG_VERBOSE, LOG_TAG"i", "  [...]");
+                break;
+            }
+            const char* name = "";
+#if 0   // "locals" structure has changed -- need to rewrite this
+            int j;
+            DexFile* pDexFile = method->clazz->pDexFile;
+            const DexCode* pDexCode = dvmGetMethodCode(method);
+            int localsSize = dexGetLocalsSize(pDexFile, pDexCode);
+            const DexLocal* locals = dvmDexGetLocals(pDexFile, pDexCode);
+            for (j = 0; j < localsSize, j++) {
+                if (locals[j].registerNum == (u4) i) {
+                    name = dvmDexStringStr(locals[j].pName);
+                    break;
+                }
+            }
+#endif
+            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d      : 0x%08x %s",
+                i, framePtr[i], name);
+        }
+    }
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Entry point and general support functions
+ * ===========================================================================
+ */
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the packed-switch
+ * instruction).
+ */
+s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal)
+{
+    const int kInstrLen = 3;
+
+    /*
+     * Packed switch data format:
+     *  ushort ident = 0x0100   magic value
+     *  ushort size             number of entries in the table
+     *  int first_key           first (and lowest) switch case value
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (4+size*2) 16-bit code units.
+     */
+    if (*switchData++ != kPackedSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError("bad packed switch magic");
+        return kInstrLen;
+    }
+
+    u2 size = *switchData++;
+    assert(size > 0);
+
+    s4 firstKey = *switchData++;
+    firstKey |= (*switchData++) << 16;
+
+    int index = testVal - firstKey;
+    if (index < 0 || index >= size) {
+        LOGVV("Value %d not found in switch (%d-%d)",
+            testVal, firstKey, firstKey+size-1);
+        return kInstrLen;
+    }
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    const s4* entries = (const s4*) switchData;
+    assert(((u4)entries & 0x3) == 0);
+
+    assert(index >= 0 && index < size);
+    LOGVV("Value %d found in slot %d (goto 0x%02x)",
+        testVal, index,
+        s4FromSwitchData(&entries[index]));
+    return s4FromSwitchData(&entries[index]);
+}
+
+/*
+ * Find the matching case.  Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the sparse-switch
+ * instruction).
+ */
+s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal)
+{
+    const int kInstrLen = 3;
+    u2 size;
+    const s4* keys;
+    const s4* entries;
+
+    /*
+     * Sparse switch data format:
+     *  ushort ident = 0x0200   magic value
+     *  ushort size             number of entries in the table; > 0
+     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
+     *  int targets[size]       branch targets, relative to switch opcode
+     *
+     * Total size is (2+size*4) 16-bit code units.
+     */
+
+    if (*switchData++ != kSparseSwitchSignature) {
+        /* should have been caught by verifier */
+        dvmThrowInternalError("bad sparse switch magic");
+        return kInstrLen;
+    }
+
+    size = *switchData++;
+    assert(size > 0);
+
+    /* The keys are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    keys = (const s4*) switchData;
+    assert(((u4)keys & 0x3) == 0);
+
+    /* The entries are guaranteed to be aligned on a 32-bit boundary;
+     * we can treat them as a native int array.
+     */
+    entries = keys + size;
+    assert(((u4)entries & 0x3) == 0);
+
+    /*
+     * Binary-search through the array of keys, which are guaranteed to
+     * be sorted low-to-high.
+     */
+    int lo = 0;
+    int hi = size - 1;
+    while (lo <= hi) {
+        int mid = (lo + hi) >> 1;
+
+        s4 foundVal = s4FromSwitchData(&keys[mid]);
+        if (testVal < foundVal) {
+            hi = mid - 1;
+        } else if (testVal > foundVal) {
+            lo = mid + 1;
+        } else {
+            LOGVV("Value %d found in entry %d (goto 0x%02x)",
+                testVal, mid, s4FromSwitchData(&entries[mid]));
+            return s4FromSwitchData(&entries[mid]);
+        }
+    }
+
+    LOGVV("Value %d not found in switch", testVal);
+    return kInstrLen;
+}
+
+/*
+ * Copy data for a fill-array-data instruction.  On a little-endian machine
+ * we can just do a memcpy(), on a big-endian system we have work to do.
+ *
+ * The trick here is that dexopt has byte-swapped each code unit, which is
+ * exactly what we want for short/char data.  For byte data we need to undo
+ * the swap, and for 4- or 8-byte values we need to swap pieces within
+ * each word.
+ */
+static void copySwappedArrayData(void* dest, const u2* src, u4 size, u2 width)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    memcpy(dest, src, size*width);
+#else
+    int i;
+
+    switch (width) {
+    case 1:
+        /* un-swap pairs of bytes as we go */
+        for (i = (size-1) & ~1; i >= 0; i -= 2) {
+            ((u1*)dest)[i] = ((u1*)src)[i+1];
+            ((u1*)dest)[i+1] = ((u1*)src)[i];
+        }
+        /*
+         * "src" is padded to end on a two-byte boundary, but we don't want to
+         * assume "dest" is, so we handle odd length specially.
+         */
+        if ((size & 1) != 0) {
+            ((u1*)dest)[size-1] = ((u1*)src)[size];
+        }
+        break;
+    case 2:
+        /* already swapped correctly */
+        memcpy(dest, src, size*width);
+        break;
+    case 4:
+        /* swap word halves */
+        for (i = 0; i < (int) size; i++) {
+            ((u4*)dest)[i] = (src[(i << 1) + 1] << 16) | src[i << 1];
+        }
+        break;
+    case 8:
+        /* swap word halves and words */
+        for (i = 0; i < (int) (size << 1); i += 2) {
+            ((int*)dest)[i] = (src[(i << 1) + 3] << 16) | src[(i << 1) + 2];
+            ((int*)dest)[i+1] = (src[(i << 1) + 1] << 16) | src[i << 1];
+        }
+        break;
+    default:
+        ALOGE("Unexpected width %d in copySwappedArrayData", width);
+        dvmAbort();
+        break;
+    }
+#endif
+}
+
+/*
+ * Fill the array with predefined constant values.
+ *
+ * Returns true if job is completed, otherwise false to indicate that
+ * an exception has been thrown.
+ */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObj, const u2* arrayData)
+{
+    u2 width;
+    u4 size;
+
+    if (arrayObj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+    assert (!IS_CLASS_FLAG_SET(((Object *)arrayObj)->clazz,
+                               CLASS_ISOBJECTARRAY));
+
+    /*
+     * Array data table format:
+     *  ushort ident = 0x0300   magic value
+     *  ushort width            width of each element in the table
+     *  uint   size             number of elements in the table
+     *  ubyte  data[size*width] table of data values (may contain a single-byte
+     *                          padding at the end)
+     *
+     * Total size is 4+(width * size + 1)/2 16-bit code units.
+     */
+    if (arrayData[0] != kArrayDataSignature) {
+        dvmThrowInternalError("bad array data magic");
+        return false;
+    }
+
+    width = arrayData[1];
+    size = arrayData[2] | (((u4)arrayData[3]) << 16);
+
+    if (size > arrayObj->length) {
+        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, size);
+        return false;
+    }
+    copySwappedArrayData(arrayObj->contents, &arrayData[4], size, width);
+    return true;
+}
+
+/*
+ * Find the concrete method that corresponds to "methodIdx".  The code in
+ * "method" is executing invoke-method with "thisClass" as its first argument.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+    const Method* method, DvmDex* methodClassDex)
+{
+    Method* absMethod;
+    Method* methodToCall;
+    int i, vtableIndex;
+
+    /*
+     * Resolve the method.  This gives us the abstract method from the
+     * interface class declaration.
+     */
+    absMethod = dvmDexGetResolvedMethod(methodClassDex, methodIdx);
+    if (absMethod == NULL) {
+        absMethod = dvmResolveInterfaceMethod(method->clazz, methodIdx);
+        if (absMethod == NULL) {
+            ALOGV("+ unknown method");
+            return NULL;
+        }
+    }
+
+    /* make sure absMethod->methodIndex means what we think it means */
+    assert(dvmIsAbstractMethod(absMethod));
+
+    /*
+     * Run through the "this" object's iftable.  Find the entry for
+     * absMethod's class, then use absMethod->methodIndex to find
+     * the method's entry.  The value there is the offset into our
+     * vtable of the actual method to execute.
+     *
+     * The verifier does not guarantee that objects stored into
+     * interface references actually implement the interface, so this
+     * check cannot be eliminated.
+     */
+    for (i = 0; i < thisClass->iftableCount; i++) {
+        if (thisClass->iftable[i].clazz == absMethod->clazz)
+            break;
+    }
+    if (i == thisClass->iftableCount) {
+        /* impossible in verified DEX, need to check for it in unverified */
+        dvmThrowIncompatibleClassChangeError("interface not implemented");
+        return NULL;
+    }
+
+    assert(absMethod->methodIndex <
+        thisClass->iftable[i].clazz->virtualMethodCount);
+
+    vtableIndex =
+        thisClass->iftable[i].methodIndexArray[absMethod->methodIndex];
+    assert(vtableIndex >= 0 && vtableIndex < thisClass->vtableCount);
+    methodToCall = thisClass->vtable[vtableIndex];
+
+#if 0
+    /* this can happen when there's a stale class file */
+    if (dvmIsAbstractMethod(methodToCall)) {
+        dvmThrowAbstractMethodError("interface method not implemented");
+        return NULL;
+    }
+#else
+    assert(!dvmIsAbstractMethod(methodToCall) ||
+        methodToCall->nativeFunc != NULL);
+#endif
+
+    LOGVV("+++ interface=%s.%s concrete=%s.%s",
+        absMethod->clazz->descriptor, absMethod->name,
+        methodToCall->clazz->descriptor, methodToCall->name);
+    assert(methodToCall != NULL);
+
+    return methodToCall;
+}
+
+
+
+/*
+ * Helpers for dvmThrowVerificationError().
+ *
+ * Each returns a newly-allocated string.
+ */
+#define kThrowShow_accessFromClass     1
+static std::string classNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    if (refType == VERIFY_ERROR_REF_FIELD) {
+        /* get class ID from field ID */
+        const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+        ref = pFieldId->classIdx;
+    } else if (refType == VERIFY_ERROR_REF_METHOD) {
+        /* get class ID from method ID */
+        const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+        ref = pMethodId->classIdx;
+    }
+
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, ref);
+    std::string dotClassName(dvmHumanReadableDescriptor(className));
+    if (flags == 0) {
+        return dotClassName;
+    }
+
+    std::string result;
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        result += "tried to access class " + dotClassName;
+        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
+    } else {
+        assert(false);      // should've been caught above
+    }
+
+    return result;
+}
+static std::string fieldNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    if (refType != VERIFY_ERROR_REF_FIELD) {
+        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_FIELD, refType);
+        return NULL;    /* no message */
+    }
+
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
+    const char* fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+    std::string dotName(dvmHumanReadableDescriptor(className));
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        std::string result;
+        result += "tried to access field ";
+        result += dotName + "." + fieldName;
+        result += " from class ";
+        result += dvmHumanReadableDescriptor(method->clazz->descriptor);
+        return result;
+    }
+    return dotName + "." + fieldName;
+}
+static std::string methodNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    if (refType != VERIFY_ERROR_REF_METHOD) {
+        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_METHOD,refType);
+        return NULL;    /* no message */
+    }
+
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
+    const char* methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+    std::string dotName(dvmHumanReadableDescriptor(className));
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        std::string result;
+        result += "tried to access method ";
+        result += dotName + "." + methodName + ":" + desc;
+        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
+        free(desc);
+        return result;
+    }
+    return dotName + "." + methodName;
+}
+
+/*
+ * Throw an exception for a problem identified by the verifier.
+ *
+ * This is used by the invoke-verification-error instruction.  It always
+ * throws an exception.
+ *
+ * "kind" indicates the kind of failure encountered by the verifier.  It
+ * has two parts, an error code and an indication of the reference type.
+ */
+void dvmThrowVerificationError(const Method* method, int kind, int ref)
+{
+    int errorPart = kind & ~(0xff << kVerifyErrorRefTypeShift);
+    int errorRefPart = kind >> kVerifyErrorRefTypeShift;
+    VerifyError errorKind = static_cast<VerifyError>(errorPart);
+    VerifyErrorRefType refType = static_cast<VerifyErrorRefType>(errorRefPart);
+    ClassObject* exceptionClass = gDvm.exVerifyError;
+    std::string msg;
+
+    switch ((VerifyError) errorKind) {
+    case VERIFY_ERROR_NO_CLASS:
+        exceptionClass = gDvm.exNoClassDefFoundError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_FIELD:
+        exceptionClass = gDvm.exNoSuchFieldError;
+        msg = fieldNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_METHOD:
+        exceptionClass = gDvm.exNoSuchMethodError;
+        msg = methodNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_ACCESS_CLASS:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = classNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_FIELD:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = fieldNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_METHOD:
+        exceptionClass = gDvm.exIllegalAccessError;
+        msg = methodNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_CLASS_CHANGE:
+        exceptionClass = gDvm.exIncompatibleClassChangeError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_INSTANTIATION:
+        exceptionClass = gDvm.exInstantiationError;
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+
+    case VERIFY_ERROR_GENERIC:
+        /* generic VerifyError; use default exception, no message */
+        break;
+    case VERIFY_ERROR_NONE:
+        /* should never happen; use default exception */
+        assert(false);
+        msg = "weird - no error specified";
+        break;
+
+    /* no default clause -- want warning if enum updated */
+    }
+
+    dvmThrowException(exceptionClass, msg.c_str());
+}
+
+/*
+ * Update interpBreak for a single thread.
+ */
+void updateInterpBreak(Thread* thread, ExecutionSubModes subMode, bool enable)
+{
+    InterpBreak oldValue, newValue;
+    do {
+        oldValue = newValue = thread->interpBreak;
+        newValue.ctl.breakFlags = kInterpNoBreak;  // Assume full reset
+        if (enable)
+            newValue.ctl.subMode |= subMode;
+        else
+            newValue.ctl.subMode &= ~subMode;
+        if (newValue.ctl.subMode & SINGLESTEP_BREAK_MASK)
+            newValue.ctl.breakFlags |= kInterpSingleStep;
+        if (newValue.ctl.subMode & SAFEPOINT_BREAK_MASK)
+            newValue.ctl.breakFlags |= kInterpSafePoint;
+#ifndef DVM_NO_ASM_INTERP
+        newValue.ctl.curHandlerTable = (newValue.ctl.breakFlags) ?
+            thread->altHandlerTable : thread->mainHandlerTable;
+#endif
+    } while (dvmQuasiAtomicCas64(oldValue.all, newValue.all,
+             &thread->interpBreak.all) != 0);
+}
+
+/*
+ * Update interpBreak for all threads.
+ */
+void updateAllInterpBreak(ExecutionSubModes subMode, bool enable)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    dvmLockThreadList(self);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        updateInterpBreak(thread, subMode, enable);
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Update the normal and debugger suspend counts for a thread.
+ * threadSuspendCount must be acquired before calling this to
+ * ensure a clean update of suspendCount, dbgSuspendCount and
+ * sumThreadSuspendCount.
+ *
+ * CLEANUP TODO: Currently only the JIT is using sumThreadSuspendCount.
+ * Move under WITH_JIT ifdefs.
+*/
+void dvmAddToSuspendCounts(Thread* thread, int delta, int dbgDelta)
+{
+    thread->suspendCount += delta;
+    thread->dbgSuspendCount += dbgDelta;
+    updateInterpBreak(thread, kSubModeSuspendPending,
+                      (thread->suspendCount != 0));
+    // Update the global suspend count total
+    gDvm.sumThreadSuspendCount += delta;
+}
+
+
+void dvmDisableSubMode(Thread* thread, ExecutionSubModes subMode)
+{
+    updateInterpBreak(thread, subMode, false);
+}
+
+void dvmEnableSubMode(Thread* thread, ExecutionSubModes subMode)
+{
+    updateInterpBreak(thread, subMode, true);
+}
+
+void dvmEnableAllSubMode(ExecutionSubModes subMode)
+{
+    updateAllInterpBreak(subMode, true);
+}
+
+void dvmDisableAllSubMode(ExecutionSubModes subMode)
+{
+    updateAllInterpBreak(subMode, false);
+}
+
+/*
+ * Do a sanity check on interpreter state saved to Thread.
+ * A failure here doesn't necessarily mean that something is wrong,
+ * so this code should only be used during development to suggest
+ * a possible problem.
+ */
+void dvmCheckInterpStateConsistency()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    uint8_t breakFlags;
+    uint8_t subMode;
+#ifndef DVM_NO_ASM_INTERP
+    void* handlerTable;
+#endif
+
+    dvmLockThreadList(self);
+    breakFlags = self->interpBreak.ctl.breakFlags;
+    subMode = self->interpBreak.ctl.subMode;
+#ifndef DVM_NO_ASM_INTERP
+    handlerTable = self->interpBreak.ctl.curHandlerTable;
+#endif
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (subMode != thread->interpBreak.ctl.subMode) {
+            ALOGD("Warning: subMode mismatch - %#x:%#x, tid[%d]",
+                subMode,thread->interpBreak.ctl.subMode,thread->threadId);
+         }
+        if (breakFlags != thread->interpBreak.ctl.breakFlags) {
+            ALOGD("Warning: breakFlags mismatch - %#x:%#x, tid[%d]",
+                breakFlags,thread->interpBreak.ctl.breakFlags,thread->threadId);
+         }
+#ifndef DVM_NO_ASM_INTERP
+        if (handlerTable != thread->interpBreak.ctl.curHandlerTable) {
+            ALOGD("Warning: curHandlerTable mismatch - %#x:%#x, tid[%d]",
+                (int)handlerTable,(int)thread->interpBreak.ctl.curHandlerTable,
+                thread->threadId);
+         }
+#endif
+#if defined(WITH_JIT)
+         if (thread->pJitProfTable != gDvmJit.pProfTable) {
+             ALOGD("Warning: pJitProfTable mismatch - %#x:%#x, tid[%d]",
+                  (int)thread->pJitProfTable,(int)gDvmJit.pProfTable,
+                  thread->threadId);
+         }
+         if (thread->jitThreshold != gDvmJit.threshold) {
+             ALOGD("Warning: jitThreshold mismatch - %#x:%#x, tid[%d]",
+                  (int)thread->jitThreshold,(int)gDvmJit.threshold,
+                  thread->threadId);
+         }
+#endif
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Arm a safepoint callback for a thread.  If funct is null,
+ * clear any pending callback.
+ * TODO: only gc is currently using this feature, and will have
+ * at most a single outstanding callback request.  Until we need
+ * something more capable and flexible, enforce this limit.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg)
+{
+    dvmLockMutex(&thread->callbackMutex);
+    if ((funct == NULL) || (thread->callback == NULL)) {
+        thread->callback = funct;
+        thread->callbackArg = arg;
+        if (funct != NULL) {
+            dvmEnableSubMode(thread, kSubModeCallbackPending);
+        } else {
+            dvmDisableSubMode(thread, kSubModeCallbackPending);
+        }
+    } else {
+        // Already armed.  Different?
+        if ((funct != thread->callback) ||
+            (arg != thread->callbackArg)) {
+            // Yes - report failure and die
+            ALOGE("ArmSafePointCallback failed, thread %d", thread->threadId);
+            dvmUnlockMutex(&thread->callbackMutex);
+            dvmAbort();
+        }
+    }
+    dvmUnlockMutex(&thread->callbackMutex);
+}
+
+/*
+ * One-time initialization at thread creation.  Here we initialize
+ * useful constants.
+ */
+void dvmInitInterpreterState(Thread* self)
+{
+#if defined(WITH_JIT)
+    /*
+     * Reserve a static entity here to quickly setup runtime contents as
+     * gcc will issue block copy instructions.
+     */
+    static struct JitToInterpEntries jitToInterpEntries = {
+        dvmJitToInterpNormal,
+        dvmJitToInterpNoChain,
+        dvmJitToInterpPunt,
+        dvmJitToInterpSingleStep,
+        dvmJitToInterpTraceSelect,
+#if defined(WITH_SELF_VERIFICATION)
+        dvmJitToInterpBackwardBranch,
+#else
+        NULL,
+#endif
+    };
+#endif
+
+    // Begin initialization
+    self->cardTable = gDvm.biasedCardTableBase;
+#if defined(WITH_JIT)
+    // One-time initializations
+    self->jitToInterpEntries = jitToInterpEntries;
+    self->icRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+    self->pProfileCountdown = &gDvmJit.profileCountdown;
+    // Jit state that can change
+    dvmJitUpdateThreadStateSingle(self);
+#endif
+    dvmInitializeInterpBreak(self);
+}
+
+/*
+ * For a newly-created thread, we need to start off with interpBreak
+ * set to any existing global modes.  The caller must hold the
+ * thread list lock.
+ */
+void dvmInitializeInterpBreak(Thread* thread)
+{
+    if (gDvm.instructionCountEnableCount > 0) {
+        dvmEnableSubMode(thread, kSubModeInstCounting);
+    }
+    TracingMode mode = dvmGetMethodTracingMode();
+    if (mode != TRACING_INACTIVE) {
+        if (mode == SAMPLE_PROFILING_ACTIVE) {
+            dvmEnableSubMode(thread, kSubModeSampleTrace);
+        } else {
+            dvmEnableSubMode(thread, kSubModeMethodTrace);
+        }
+    }
+    if (gDvm.emulatorTraceEnableCount > 0) {
+        dvmEnableSubMode(thread, kSubModeEmulatorTrace);
+    }
+    if (gDvm.debuggerActive) {
+        dvmEnableSubMode(thread, kSubModeDebuggerActive);
+    }
+#if defined(WITH_JIT)
+    dvmJitUpdateThreadStateSingle(thread);
+#endif
+#if 0
+    // Debugging stress mode - force checkBefore
+    dvmEnableSubMode(thread, kSubModeCheckAlways);
+#endif
+}
+
+/*
+ * Inter-instruction handler invoked in between instruction interpretations
+ * to handle exceptional events such as debugging housekeeping, instruction
+ * count profiling, JIT trace building, etc.  Dalvik PC has been exported
+ * prior to call, but Thread copy of dPC & fp are not current.
+ */
+void dvmCheckBefore(const u2 *pc, u4 *fp, Thread* self)
+{
+    const Method* method = self->interpSave.method;
+    assert(pc >= method->insns && pc <
+           method->insns + dvmGetMethodInsnsSize(method));
+
+#if 0
+    /*
+     * When we hit a specific method, enable verbose instruction logging.
+     * Sometimes it's helpful to use the debugger attach as a trigger too.
+     */
+    if (*pIsMethodEntry) {
+        static const char* cd = "Landroid/test/Arithmetic;";
+        static const char* mn = "shiftTest2";
+        static const char* sg = "()V";
+
+        if (/*self->interpBreak.ctl.subMode & kSubModeDebuggerActive &&*/
+            strcmp(method->clazz->descriptor, cd) == 0 &&
+            strcmp(method->name, mn) == 0 &&
+            strcmp(method->shorty, sg) == 0)
+        {
+            ALOGW("Reached %s.%s, enabling verbose mode",
+                method->clazz->descriptor, method->name);
+            android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+            dumpRegs(method, fp, true);
+        }
+
+        if (!gDvm.debuggerActive)
+            *pIsMethodEntry = false;
+    }
+#endif
+
+    /* Safe point handling */
+    if (self->suspendCount ||
+        (self->interpBreak.ctl.subMode & kSubModeCallbackPending)) {
+        // Are we are a safe point?
+        int flags;
+        flags = dexGetFlagsFromOpcode(dexOpcodeFromCodeUnit(*pc));
+        if (flags & (VERIFY_GC_INST_MASK & ~kInstrCanThrow)) {
+            // Yes, at a safe point.  Pending callback?
+            if (self->interpBreak.ctl.subMode & kSubModeCallbackPending) {
+                SafePointCallback callback;
+                void* arg;
+                // Get consistent funct/arg pair
+                dvmLockMutex(&self->callbackMutex);
+                callback = self->callback;
+                arg = self->callbackArg;
+                dvmUnlockMutex(&self->callbackMutex);
+                // Update Thread structure
+                self->interpSave.pc = pc;
+                self->interpSave.curFrame = fp;
+                if (callback != NULL) {
+                    // Do the callback
+                    if (!callback(self,arg)) {
+                        // disarm
+                        dvmArmSafePointCallback(self, NULL, NULL);
+                    }
+                }
+            }
+            // Need to suspend?
+            if (self->suspendCount) {
+                dvmExportPC(pc, fp);
+                dvmCheckSuspendPending(self);
+            }
+        }
+    }
+
+    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+        updateDebugger(method, pc, fp, self);
+    }
+    if (gDvm.instructionCountEnableCount != 0) {
+        /*
+         * Count up the #of executed instructions.  This isn't synchronized
+         * for thread-safety; if we need that we should make this
+         * thread-local and merge counts into the global area when threads
+         * exit (perhaps suspending all other threads GC-style and pulling
+         * the data out of them).
+         */
+        gDvm.executedInstrCounts[GET_OPCODE(*pc)]++;
+    }
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+    dvmInterpCheckTrackedRefs(self, method,
+                              self->interpSave.debugTrackedRefStart);
+#endif
+
+#if defined(WITH_JIT)
+    // Does the JIT need anything done now?
+    if (self->interpBreak.ctl.subMode &
+            (kSubModeJitTraceBuild | kSubModeJitSV)) {
+        // Are we building a trace?
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
+            dvmCheckJit(pc, self);
+        }
+
+#if defined(WITH_SELF_VERIFICATION)
+        // Are we replaying a trace?
+        if (self->interpBreak.ctl.subMode & kSubModeJitSV) {
+            dvmCheckSelfVerification(pc, self);
+        }
+#endif
+    }
+#endif
+
+    /*
+     * CountedStep processing.  NOTE: must be the last here to allow
+     * preceeding special case handler to manipulate single-step count.
+     */
+    if (self->interpBreak.ctl.subMode & kSubModeCountedStep) {
+        if (self->singleStepCount == 0) {
+            // We've exhausted our single step count
+            dvmDisableSubMode(self, kSubModeCountedStep);
+#if defined(WITH_JIT)
+#if 0
+            /*
+             * For debugging.  If jitResumeDPC is non-zero, then
+             * we expect to return to a trace in progress.   There
+             * are valid reasons why we wouldn't (such as an exception
+             * throw), but here we can keep track.
+             */
+            if (self->jitResumeDPC != NULL) {
+                if (self->jitResumeDPC == pc) {
+                    if (self->jitResumeNPC != NULL) {
+                        ALOGD("SS return to trace - pc:%#x to 0x:%x",
+                             (int)pc, (int)self->jitResumeNPC);
+                    } else {
+                        ALOGD("SS return to interp - pc:%#x",(int)pc);
+                    }
+                } else {
+                    ALOGD("SS failed to return.  Expected %#x, now at %#x",
+                         (int)self->jitResumeDPC, (int)pc);
+                }
+            }
+#endif
+#if 0
+            // TODO - fix JIT single-stepping resume mode (b/5551114)
+            // self->jitResumeNPC needs to be cleared in callPrep
+
+            // If we've got a native return and no other reasons to
+            // remain in singlestep/break mode, do a long jump
+            if (self->jitResumeNPC != NULL &&
+                self->interpBreak.ctl.breakFlags == 0) {
+                assert(self->jitResumeDPC == pc);
+                self->jitResumeDPC = NULL;
+                dvmJitResumeTranslation(self, pc, fp);
+                // Doesn't return
+                dvmAbort();
+            }
+            // In case resume is blocked by non-zero breakFlags, clear
+            // jitResumeNPC here.
+            self->jitResumeNPC = NULL;
+            self->jitResumeDPC = NULL;
+            self->inJitCodeCache = NULL;
+#endif
+#endif
+        } else {
+            self->singleStepCount--;
+#if defined(WITH_JIT)
+            if ((self->singleStepCount > 0) && (self->jitResumeNPC != NULL)) {
+                /*
+                 * Direct return to an existing translation following a
+                 * single step is valid only if we step once.  If we're
+                 * here, an additional step was added so we need to invalidate
+                 * the return to translation.
+                 */
+                self->jitResumeNPC = NULL;
+                self->inJitCodeCache = NULL;
+            }
+#endif
+        }
+    }
+}
+
+/*
+ * Main interpreter loop entry point.
+ *
+ * This begins executing code at the start of "method".  On exit, "pResult"
+ * holds the return value of the method (or, if "method" returns NULL, it
+ * holds an undefined value).
+ *
+ * The interpreted stack frame, which holds the method arguments, has
+ * already been set up.
+ */
+void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
+{
+    InterpSaveState interpSaveState;
+    ExecutionSubModes savedSubModes;
+
+#if defined(WITH_JIT)
+    /* Target-specific save/restore */
+    double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
+    /*
+     * If the previous VM left the code cache through single-stepping the
+     * inJitCodeCache flag will be set when the VM is re-entered (for example,
+     * in self-verification mode we single-step NEW_INSTANCE which may re-enter
+     * the VM through findClassFromLoaderNoInit). Because of that, we cannot
+     * assert that self->inJitCodeCache is NULL here.
+     */
+#endif
+
+    /*
+     * Save interpreter state from previous activation, linking
+     * new to last.
+     */
+    interpSaveState = self->interpSave;
+    self->interpSave.prev = &interpSaveState;
+    /*
+     * Strip out and save any flags that should not be inherited by
+     * nested interpreter activation.
+     */
+    savedSubModes = (ExecutionSubModes)(
+              self->interpBreak.ctl.subMode & LOCAL_SUBMODE);
+    if (savedSubModes != kSubModeNormal) {
+        dvmDisableSubMode(self, savedSubModes);
+    }
+#if defined(WITH_JIT)
+    dvmJitCalleeSave(calleeSave);
+#endif
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+    self->interpSave.debugTrackedRefStart =
+        dvmReferenceTableEntries(&self->internalLocalRefTable);
+#endif
+    self->debugIsMethodEntry = true;
+#if defined(WITH_JIT)
+    /* Initialize the state to kJitNot */
+    self->jitState = kJitNot;
+#endif
+
+    /*
+     * Initialize working state.
+     *
+     * No need to initialize "retval".
+     */
+    self->interpSave.method = method;
+    self->interpSave.curFrame = (u4*) self->interpSave.curFrame;
+    self->interpSave.pc = method->insns;
+
+    assert(!dvmIsNativeMethod(method));
+
+    /*
+     * Make sure the class is ready to go.  Shouldn't be possible to get
+     * here otherwise.
+     */
+    if (method->clazz->status < CLASS_INITIALIZING ||
+        method->clazz->status == CLASS_ERROR)
+    {
+        ALOGE("ERROR: tried to execute code in unprepared class '%s' (%d)",
+            method->clazz->descriptor, method->clazz->status);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    typedef void (*Interpreter)(Thread*);
+    Interpreter stdInterp;
+    if (gDvm.executionMode == kExecutionModeInterpFast)
+        stdInterp = dvmMterpStd;
+#if defined(WITH_JIT)
+    else if (gDvm.executionMode == kExecutionModeJit ||
+             gDvm.executionMode == kExecutionModeNcgO0 ||
+             gDvm.executionMode == kExecutionModeNcgO1)
+        stdInterp = dvmMterpStd;
+#endif
+    else
+        stdInterp = dvmInterpretPortable;
+
+    // Call the interpreter
+    (*stdInterp)(self);
+
+    *pResult = self->interpSave.retval;
+
+    /* Restore interpreter state from previous activation */
+    self->interpSave = interpSaveState;
+#if defined(WITH_JIT)
+    dvmJitCalleeRestore(calleeSave);
+#endif
+    if (savedSubModes != kSubModeNormal) {
+        dvmEnableSubMode(self, savedSubModes);
+    }
+}
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
new file mode 100644
index 0000000..e54ec61
--- /dev/null
+++ b/vm/interp/Interp.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Dalvik interpreter public definitions.
+ */
+#ifndef DALVIK_INTERP_INTERP_H_
+#define DALVIK_INTERP_INTERP_H_
+
+/*
+ * Stash the dalvik PC in the frame.  Called  during interpretation.
+ */
+INLINE void dvmExportPC(const u2* pc, const u4* fp)
+{
+    SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc;
+}
+
+/*
+ * Extract the Dalvik opcode
+ */
+#define GET_OPCODE(_inst) (_inst & 0xff)
+
+/*
+ * Interpreter entry point.  Call here after setting up the interpreted
+ * stack (most code will want to get here via dvmCallMethod().)
+ */
+void dvmInterpret(Thread* thread, const Method* method, JValue* pResult);
+
+/*
+ * Throw an exception for a problem detected by the verifier.
+ *
+ * This is called from the handler for the throw-verification-error
+ * instruction.  "method" is the method currently being executed.
+ */
+extern "C" void dvmThrowVerificationError(const Method* method,
+                                          int kind, int ref);
+
+/*
+ * One-time initialization and shutdown.
+ */
+bool dvmBreakpointStartup(void);
+void dvmBreakpointShutdown(void);
+void dvmInitInterpreterState(Thread* self);
+
+/*
+ * Breakpoint implementation.
+ */
+void dvmInitBreakpoints();
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset);
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset);
+bool dvmAddSingleStep(Thread* thread, int size, int depth);
+void dvmClearSingleStep(Thread* thread);
+
+/*
+ * Recover the opcode that was replaced by a breakpoint.
+ */
+extern "C" u1 dvmGetOriginalOpcode(const u2* addr);
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ */
+void dvmFlushBreakpoints(ClassObject* clazz);
+
+/*
+ * Debugger support
+ */
+extern "C" void dvmCheckBefore(const u2 *dPC, u4 *fp, Thread* self);
+extern "C" void dvmReportExceptionThrow(Thread* self, Object* exception);
+extern "C" void dvmReportPreNativeInvoke(const Method* methodToCall, Thread* self, u4* fp);
+extern "C" void dvmReportPostNativeInvoke(const Method* methodToCall, Thread* self, u4* fp);
+extern "C" void dvmReportInvoke(Thread* self, const Method* methodToCall);
+extern "C" void dvmReportReturn(Thread* self);
+
+/*
+ * InterpBreak & subMode control
+ */
+void dvmDisableSubMode(Thread* thread, ExecutionSubModes subMode);
+extern "C" void dvmEnableSubMode(Thread* thread, ExecutionSubModes subMode);
+void dvmDisableAllSubMode(ExecutionSubModes subMode);
+void dvmEnableAllSubMode(ExecutionSubModes subMode);
+void dvmAddToSuspendCounts(Thread* thread, int delta, int dbgDelta);
+void dvmCheckInterpStateConsistency();
+void dvmInitializeInterpBreak(Thread* thread);
+
+/*
+ * Register a callback to occur at the next safe point for a single thread.
+ * If funct is NULL, the previous registration is cancelled.
+ *
+ * The callback prototype is:
+ *        bool funct(Thread* thread, void* arg)
+ *
+ *  If funct returns false, the callback will be disarmed.  If true,
+ *  it will stay in effect.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg);
+
+
+#ifndef DVM_NO_ASM_INTERP
+extern void* dvmAsmInstructionStart[];
+extern void* dvmAsmAltInstructionStart[];
+#endif
+
+#endif  // DALVIK_INTERP_INTERP_H_
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
new file mode 100644
index 0000000..a053f6d
--- /dev/null
+++ b/vm/interp/InterpDefs.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Dalvik interpreter definitions.  These are internal to the interpreter.
+ *
+ * This includes defines, types, function declarations, and inline functions
+ * that are common to all interpreter implementations.
+ *
+ * Functions and globals declared here are defined in Interp.c.
+ */
+#ifndef DALVIK_INTERP_DEFS_H_
+#define DALVIK_INTERP_DEFS_H_
+
+#if defined(WITH_JIT)
+/*
+ * Size of save area for callee-save FP regs, which are not automatically
+ * saved by interpreter main because it doesn't use them (but Jit'd code
+ * may). Save/restore routine is defined by target, and size should
+ * be >= max needed by any target.
+ */
+#define JIT_CALLEE_SAVE_DOUBLE_COUNT 8
+
+#endif
+
+/*
+ * Portable interpreter.
+ */
+extern void dvmInterpretPortable(Thread* self);
+
+/*
+ * "mterp" interpreter.
+ */
+extern void dvmMterpStd(Thread* self);
+
+/*
+ * Get the "this" pointer from the current frame.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp);
+
+/*
+ * Verify that our tracked local references are valid.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+    int debugTrackedRefStart);
+
+/*
+ * Process switch statement.
+ */
+extern "C" s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal);
+extern "C" s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal);
+
+/*
+ * Process fill-array-data.
+ */
+extern "C" bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,
+                                  const u2* arrayData);
+
+/*
+ * Find an interface method.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+    const Method* method, DvmDex* methodClassDex);
+
+/*
+ * Determine if the debugger or profiler is currently active.
+ */
+static inline bool dvmDebuggerOrProfilerActive()
+{
+    return gDvm.debuggerActive || gDvm.activeProfilers != 0;
+}
+
+#if defined(WITH_JIT)
+/*
+ * Determine if the jit, debugger or profiler is currently active.  Used when
+ * selecting which interpreter to switch to.
+ */
+static inline bool dvmJitDebuggerOrProfilerActive()
+{
+    return (gDvmJit.pProfTable != NULL) || dvmDebuggerOrProfilerActive();
+}
+
+/*
+ * Hide the translations and stick with the interpreter as long as one of the
+ * following conditions is true.
+ */
+static inline bool dvmJitHideTranslation()
+{
+    return (gDvm.sumThreadSuspendCount != 0) ||
+           (gDvmJit.codeCacheFull == true) ||
+           (gDvmJit.pProfTable == NULL);
+}
+
+#endif
+
+/*
+ * Construct an s4 from two consecutive half-words of switch data.
+ * This needs to check endianness because the DEX optimizer only swaps
+ * half-words in instruction stream.
+ *
+ * "switchData" must be 32-bit aligned.
+ */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline s4 s4FromSwitchData(const void* switchData) {
+    return *(s4*) switchData;
+}
+#else
+static inline s4 s4FromSwitchData(const void* switchData) {
+    u2* data = switchData;
+    return data[0] | (((s4) data[1]) << 16);
+}
+#endif
+
+#endif  // DALVIK_INTERP_DEFS_H_
diff --git a/vm/interp/InterpState.h b/vm/interp/InterpState.h
new file mode 100644
index 0000000..8d2c224
--- /dev/null
+++ b/vm/interp/InterpState.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+/*
+ * Dalvik interpreter definitions.  These are internal to the interpreter.
+ *
+ * This includes defines, types, function declarations, and inline functions
+ * that are common to all interpreter implementations.
+ *
+ * Functions and globals declared here are defined in Interp.c.
+ */
+#ifndef DALVIK_INTERP_STATE_H_
+#define DALVIK_INTERP_STATE_H_
+
+/*
+ * For x86 JIT. In the lowered code sequences for bytecodes, at most 10
+ * temporary variables may be live at the same time. Therefore, at most
+ * 10 temporary variables can be spilled at the same time.
+*/
+#define MAX_SPILL_JIT_IA 10
+
+/*
+ * Execution mode, e.g. interpreter vs. JIT.
+ */
+enum ExecutionMode {
+    kExecutionModeUnknown = 0,
+    kExecutionModeInterpPortable,
+    kExecutionModeInterpFast,
+#if defined(WITH_JIT)
+    kExecutionModeJit,
+#endif
+#if defined(WITH_JIT)  /* IA only */
+    kExecutionModeNcgO0,
+    kExecutionModeNcgO1,
+#endif
+};
+
+/*
+ * Execution sub modes, e.g. debugging, profiling, etc.
+ * Treated as bit flags for fast access.  These values are used directly
+ * by assembly code in the mterp interpeter and may also be used by
+ * code generated by the JIT.  Take care when changing.
+ */
+enum ExecutionSubModes {
+    kSubModeNormal            = 0x0000,   /* No active subMode */
+    kSubModeMethodTrace       = 0x0001,
+    kSubModeEmulatorTrace     = 0x0002,
+    kSubModeInstCounting      = 0x0004,
+    kSubModeDebuggerActive    = 0x0008,
+    kSubModeSuspendPending    = 0x0010,
+    kSubModeCallbackPending   = 0x0020,
+    kSubModeCountedStep       = 0x0040,
+    kSubModeCheckAlways       = 0x0080,
+    kSubModeSampleTrace       = 0x0100,
+    kSubModeJitTraceBuild     = 0x4000,
+    kSubModeJitSV             = 0x8000,
+    kSubModeDebugProfile   = (kSubModeMethodTrace |
+                              kSubModeEmulatorTrace |
+                              kSubModeInstCounting |
+                              kSubModeDebuggerActive)
+};
+
+/*
+ * Interpreter break flags.  When set, causes the interpreter to
+ * break from normal execution and invoke the associated callback
+ * handler.
+ */
+
+enum InterpBreakFlags {
+    kInterpNoBreak            = 0x00,    /* Don't check */
+    kInterpSingleStep         = 0x01,    /* Check between each inst */
+    kInterpSafePoint          = 0x02,    /* Check at safe points */
+};
+
+/*
+ * Mapping between subModes and required check intervals.  Note: in
+ * the future we might want to make this mapping target-dependent.
+ */
+#define SINGLESTEP_BREAK_MASK ( kSubModeInstCounting | \
+                                kSubModeDebuggerActive | \
+                                kSubModeCountedStep | \
+                                kSubModeCheckAlways | \
+                                kSubModeJitSV | \
+                                kSubModeJitTraceBuild )
+
+#define SAFEPOINT_BREAK_MASK  ( kSubModeSuspendPending | \
+                                kSubModeCallbackPending )
+
+typedef bool (*SafePointCallback)(struct Thread* thread, void* arg);
+
+/*
+ * Identify which break and submode flags should be local
+ * to an interpreter activation.
+ */
+#define LOCAL_SUBMODE (kSubModeJitTraceBuild)
+
+struct InterpSaveState {
+    const u2*       pc;         // Dalvik PC
+    u4*             curFrame;   // Dalvik frame pointer
+    const Method    *method;    // Method being executed
+    DvmDex*         methodClassDex;
+    JValue          retval;
+    void*           bailPtr;
+#if defined(WITH_TRACKREF_CHECKS)
+    int             debugTrackedRefStart;
+#else
+    int             unused;        // Keep struct size constant
+#endif
+    struct InterpSaveState* prev;  // To follow nested activations
+} __attribute__ ((__packed__));
+
+#ifdef WITH_JIT
+/*
+ * NOTE: Only entry points dispatched via [self + #offset] are put
+ * in this struct, and there are six of them:
+ * 1) dvmJitToInterpNormal: find if there is a corresponding compilation for
+ *    the new dalvik PC. If so, chain the originating compilation with the
+ *    target then jump to it. If the destination trace doesn't exist, update
+ *    the profile count for that Dalvik PC.
+ * 2) dvmJitToInterpNoChain: similar to dvmJitToInterpNormal but chaining is
+ *    not performed.
+ * 3) dvmJitToInterpPunt: use the fast interpreter to execute the next
+ *    instruction(s) and stay there as long as it is appropriate to return
+ *    to the compiled land. This is used when the jit'ed code is about to
+ *    throw an exception.
+ * 4) dvmJitToInterpSingleStep: use the portable interpreter to execute the
+ *    next instruction only and return to pre-specified location in the
+ *    compiled code to resume execution. This is mainly used as debugging
+ *    feature to bypass problematic opcode implementations without
+ *    disturbing the trace formation.
+ * 5) dvmJitToTraceSelect: Similar to dvmJitToInterpNormal except for the
+ *    profiling operation. If the new Dalvik PC is dominated by an already
+ *    translated trace, directly request a new translation if the destinaion
+ *    trace doesn't exist.
+ * 6) dvmJitToBackwardBranch: special case for SELF_VERIFICATION when the
+ *    destination Dalvik PC is included by the trace itself.
+ */
+struct JitToInterpEntries {
+    void (*dvmJitToInterpNormal)(void);
+    void (*dvmJitToInterpNoChain)(void);
+    void (*dvmJitToInterpPunt)(void);
+    void (*dvmJitToInterpSingleStep)(void);
+    void (*dvmJitToInterpTraceSelect)(void);
+#if defined(WITH_SELF_VERIFICATION)
+    void (*dvmJitToInterpBackwardBranch)(void);
+#else
+    void (*unused)(void);  // Keep structure size constant
+#endif
+};
+
+/* States of the interpreter when serving a JIT-related request */
+enum JitState {
+    /* Entering states in the debug interpreter */
+    kJitNot = 0,               // Non-JIT related reasons */
+    kJitTSelectRequest = 1,    // Request a trace (subject to filtering)
+    kJitTSelectRequestHot = 2, // Request a hot trace (bypass the filter)
+    kJitSelfVerification = 3,  // Self Verification Mode
+
+    /* Operational states in the debug interpreter */
+    kJitTSelect = 4,           // Actively selecting a trace
+    kJitTSelectEnd = 5,        // Done with the trace - wrap it up
+    kJitDone = 6,              // No further JIT actions for interpBreak
+};
+
+#if defined(WITH_SELF_VERIFICATION)
+enum SelfVerificationState {
+    kSVSIdle = 0,           // Idle
+    kSVSStart = 1,          // Shadow space set up, running compiled code
+    kSVSPunt = 2,           // Exiting compiled code by punting
+    kSVSSingleStep = 3,     // Exiting compiled code by single stepping
+    kSVSNoProfile = 4,      // Exiting compiled code and don't collect profiles
+    kSVSTraceSelect = 5,    // Exiting compiled code and compile the next pc
+    kSVSNormal = 6,         // Exiting compiled code normally
+    kSVSNoChain = 7,        // Exiting compiled code by no chain
+    kSVSBackwardBranch = 8, // Exiting compiled code with backward branch trace
+    kSVSDebugInterp = 9,    // Normal state restored, running debug interpreter
+};
+#endif
+
+/* Number of entries in the 2nd level JIT profiler filter cache */
+#define JIT_TRACE_THRESH_FILTER_SIZE 32
+/* Number of low dalvik pc address bits to include in 2nd level filter key */
+#define JIT_TRACE_THRESH_FILTER_PC_BITS 16
+#define MAX_JIT_RUN_LEN 64
+
+enum JitHint {
+   kJitHintNone = 0,
+   kJitHintTaken = 1,         // Last inst in run was taken branch
+   kJitHintNotTaken = 2,      // Last inst in run was not taken branch
+   kJitHintNoBias = 3,        // Last inst in run was unbiased branch
+};
+
+/*
+ * Element of a Jit trace description. If the isCode bit is set, it describes
+ * a contiguous sequence of Dalvik byte codes.
+ */
+struct JitCodeDesc {
+    unsigned numInsts:8;     // Number of Byte codes in run
+    unsigned runEnd:1;       // Run ends with last byte code
+    JitHint hint:7;          // Hint to apply to final code of run
+    u2 startOffset;          // Starting offset for trace run
+};
+
+/*
+ * A complete list of trace runs passed to the compiler looks like the
+ * following:
+ *   frag1
+ *   frag2
+ *   frag3
+ *   meta1
+ *     :
+ *   metan
+ *   frag4
+ *
+ * frags 1-4 have the "isCode" field set and describe the location/length of
+ * real code traces, while metas 1-n are misc information.
+ * The meaning of the meta content is loosely defined. It is usually the code
+ * fragment right before the first meta field (frag3 in this case) to
+ * understand and parse them. Frag4 could be a dummy one with 0 "numInsts" but
+ * the "runEnd" field set.
+ *
+ * For example, if a trace run contains a method inlining target, the class
+ * descriptor/loader of "this" and the currently resolved method pointer are
+ * three instances of meta information stored there.
+ */
+struct JitTraceRun {
+    union {
+        JitCodeDesc frag;
+        void*       meta;
+    } info;
+    u4 isCode:1;
+    u4 unused:31;
+};
+
+#if defined(ARCH_IA32)
+/*
+ * JIT code genarator optimization level
+ */
+enum JitOptLevel {
+    kJitOptLevelO0 = 0,
+    kJitOptLevelO1 = 1,
+};
+#endif  // #if defined(ARCH_IA32)
+#endif
+
+#endif  // DALVIK_INTERP_STATE_H_
diff --git a/vm/interp/Jit.cpp b/vm/interp/Jit.cpp
new file mode 100644
index 0000000..6d53954
--- /dev/null
+++ b/vm/interp/Jit.cpp
@@ -0,0 +1,1519 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifdef WITH_JIT
+
+/*
+ * Target independent portion of Android's Jit
+ */
+
+#include "Dalvik.h"
+#include "Jit.h"
+
+#include "libdex/DexOpcodes.h"
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "compiler/Compiler.h"
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include <errno.h>
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Allocate space for per-thread ShadowSpace data structures */
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self)
+{
+    self->shadowSpace = (ShadowSpace*) calloc(1, sizeof(ShadowSpace));
+    if (self->shadowSpace == NULL)
+        return NULL;
+
+    self->shadowSpace->registerSpaceSize = REG_SPACE;
+    self->shadowSpace->registerSpace =
+        (int*) calloc(self->shadowSpace->registerSpaceSize, sizeof(int));
+
+    return self->shadowSpace->registerSpace;
+}
+
+/* Free per-thread ShadowSpace data structures */
+void dvmSelfVerificationShadowSpaceFree(Thread* self)
+{
+    free(self->shadowSpace->registerSpace);
+    free(self->shadowSpace);
+}
+
+/*
+ * Save out PC, FP, thread state, and registers to shadow space.
+ * Return a pointer to the shadow space for JIT to use.
+ *
+ * The set of saved state from the Thread structure is:
+ *     pc  (Dalvik PC)
+ *     fp  (Dalvik FP)
+ *     retval
+ *     method
+ *     methodClassDex
+ *     interpStackEnd
+ */
+void* dvmSelfVerificationSaveState(const u2* pc, u4* fp,
+                                   Thread* self, int targetTrace)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    unsigned preBytes = self->interpSave.method->outsSize*4 +
+        sizeof(StackSaveArea);
+    unsigned postBytes = self->interpSave.method->registersSize*4;
+
+    //ALOGD("### selfVerificationSaveState(%d) pc: %#x fp: %#x",
+    //    self->threadId, (int)pc, (int)fp);
+
+    if (shadowSpace->selfVerificationState != kSVSIdle) {
+        ALOGD("~~~ Save: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        ALOGD("********** SHADOW STATE DUMP **********");
+        ALOGD("PC: %#x FP: %#x", (int)pc, (int)fp);
+    }
+    shadowSpace->selfVerificationState = kSVSStart;
+
+    // Dynamically grow shadow register space if necessary
+    if (preBytes + postBytes > shadowSpace->registerSpaceSize * sizeof(u4)) {
+        free(shadowSpace->registerSpace);
+        shadowSpace->registerSpaceSize = (preBytes + postBytes) / sizeof(u4);
+        shadowSpace->registerSpace =
+            (int*) calloc(shadowSpace->registerSpaceSize, sizeof(u4));
+    }
+
+    // Remember original state
+    shadowSpace->startPC = pc;
+    shadowSpace->fp = fp;
+    shadowSpace->retval = self->interpSave.retval;
+    shadowSpace->interpStackEnd = self->interpStackEnd;
+
+    /*
+     * Store the original method here in case the trace ends with a
+     * return/invoke, the last method.
+     */
+    shadowSpace->method = self->interpSave.method;
+    shadowSpace->methodClassDex = self->interpSave.methodClassDex;
+
+    shadowSpace->shadowFP = shadowSpace->registerSpace +
+                            shadowSpace->registerSpaceSize - postBytes/4;
+
+    self->interpSave.curFrame = (u4*)shadowSpace->shadowFP;
+    self->interpStackEnd = (u1*)shadowSpace->registerSpace;
+
+    // Create a copy of the stack
+    memcpy(((char*)shadowSpace->shadowFP)-preBytes, ((char*)fp)-preBytes,
+        preBytes+postBytes);
+
+    // Setup the shadowed heap space
+    shadowSpace->heapSpaceTail = shadowSpace->heapSpace;
+
+    // Reset trace length
+    shadowSpace->traceLength = 0;
+
+    return shadowSpace;
+}
+
+/*
+ * Save ending PC, FP and compiled code exit point to shadow space.
+ * Return a pointer to the shadow space for JIT to restore state.
+ */
+void* dvmSelfVerificationRestoreState(const u2* pc, u4* fp,
+                                      SelfVerificationState exitState,
+                                      Thread* self)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    shadowSpace->endPC = pc;
+    shadowSpace->endShadowFP = fp;
+    shadowSpace->jitExitState = exitState;
+
+    //ALOGD("### selfVerificationRestoreState(%d) pc: %#x fp: %#x endPC: %#x",
+    //    self->threadId, (int)shadowSpace->startPC, (int)shadowSpace->fp,
+    //    (int)pc);
+
+    if (shadowSpace->selfVerificationState != kSVSStart) {
+        ALOGD("~~~ Restore: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        ALOGD("********** SHADOW STATE DUMP **********");
+        ALOGD("Dalvik PC: %#x endPC: %#x", (int)shadowSpace->startPC,
+            (int)shadowSpace->endPC);
+        ALOGD("Interp FP: %#x", (int)shadowSpace->fp);
+        ALOGD("Shadow FP: %#x endFP: %#x", (int)shadowSpace->shadowFP,
+            (int)shadowSpace->endShadowFP);
+    }
+
+    // Special case when punting after a single instruction
+    if (exitState == kSVSPunt && pc == shadowSpace->startPC) {
+        shadowSpace->selfVerificationState = kSVSIdle;
+    } else {
+        shadowSpace->selfVerificationState = exitState;
+    }
+
+    /* Restore state before returning */
+    self->interpSave.pc = shadowSpace->startPC;
+    self->interpSave.curFrame = shadowSpace->fp;
+    self->interpSave.method = shadowSpace->method;
+    self->interpSave.methodClassDex = shadowSpace->methodClassDex;
+    self->interpSave.retval = shadowSpace->retval;
+    self->interpStackEnd = shadowSpace->interpStackEnd;
+
+    return shadowSpace;
+}
+
+/* Print contents of virtual registers */
+static void selfVerificationPrintRegisters(int* addr, int* addrRef,
+                                           int numWords)
+{
+    int i;
+    for (i = 0; i < numWords; i++) {
+        ALOGD("(v%d) 0x%8x%s", i, addr[i], addr[i] != addrRef[i] ? " X" : "");
+    }
+}
+
+/* Print values maintained in shadowSpace */
+static void selfVerificationDumpState(const u2* pc, Thread* self)
+{
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    int frameBytes = (int) shadowSpace->registerSpace +
+                     shadowSpace->registerSpaceSize*4 -
+                     (int) shadowSpace->shadowFP;
+    int localRegs = 0;
+    int frameBytes2 = 0;
+    if ((uintptr_t)self->interpSave.curFrame < (uintptr_t)shadowSpace->fp) {
+        localRegs = (stackSave->method->registersSize -
+                     stackSave->method->insSize)*4;
+        frameBytes2 = (int) shadowSpace->fp -
+                      (int)self->interpSave.curFrame - localRegs;
+    }
+    ALOGD("********** SHADOW STATE DUMP **********");
+    ALOGD("CurrentPC: %#x, Offset: 0x%04x", (int)pc,
+        (int)(pc - stackSave->method->insns));
+    ALOGD("Class: %s", shadowSpace->method->clazz->descriptor);
+    ALOGD("Method: %s", shadowSpace->method->name);
+    ALOGD("Dalvik PC: %#x endPC: %#x", (int)shadowSpace->startPC,
+        (int)shadowSpace->endPC);
+    ALOGD("Interp FP: %#x endFP: %#x", (int)shadowSpace->fp,
+        (int)self->interpSave.curFrame);
+    ALOGD("Shadow FP: %#x endFP: %#x", (int)shadowSpace->shadowFP,
+        (int)shadowSpace->endShadowFP);
+    ALOGD("Frame1 Bytes: %d Frame2 Local: %d Bytes: %d", frameBytes,
+        localRegs, frameBytes2);
+    ALOGD("Trace length: %d State: %d", shadowSpace->traceLength,
+        shadowSpace->selfVerificationState);
+}
+
+/* Print decoded instructions in the current trace */
+static void selfVerificationDumpTrace(const u2* pc, Thread* self)
+{
+    ShadowSpace* shadowSpace = self->shadowSpace;
+    StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    int i, addr, offset;
+    DecodedInstruction *decInsn;
+
+    ALOGD("********** SHADOW TRACE DUMP **********");
+    for (i = 0; i < shadowSpace->traceLength; i++) {
+        addr = shadowSpace->trace[i].addr;
+        offset =  (int)((u2*)addr - stackSave->method->insns);
+        decInsn = &(shadowSpace->trace[i].decInsn);
+        /* Not properly decoding instruction, some registers may be garbage */
+        ALOGD("%#x: (0x%04x) %s",
+            addr, offset, dexGetOpcodeName(decInsn->opcode));
+    }
+}
+
+/* Code is forced into this spin loop when a divergence is detected */
+static void selfVerificationSpinLoop(ShadowSpace *shadowSpace)
+{
+    const u2 *startPC = shadowSpace->startPC;
+    JitTraceDescription* desc = dvmCopyTraceDescriptor(startPC, NULL);
+    if (desc) {
+        dvmCompilerWorkEnqueue(startPC, kWorkOrderTraceDebug, desc);
+        /*
+         * This function effectively terminates the VM right here, so not
+         * freeing the desc pointer when the enqueuing fails is acceptable.
+         */
+    }
+    gDvmJit.selfVerificationSpin = true;
+    while(gDvmJit.selfVerificationSpin) sleep(10);
+}
+
+/*
+ * If here, we're re-interpreting an instruction that was included
+ * in a trace that was just executed.  This routine is called for
+ * each instruction in the original trace, and compares state
+ * when it reaches the end point.
+ *
+ * TUNING: the interpretation mechanism now supports a counted
+ * single-step mechanism.  If we were to associate an instruction
+ * count with each trace exit, we could just single-step the right
+ * number of cycles and then compare.  This would improve detection
+ * of control divergences, as well as (slightly) simplify this code.
+ */
+void dvmCheckSelfVerification(const u2* pc, Thread* self)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    SelfVerificationState state = shadowSpace->selfVerificationState;
+
+    DecodedInstruction decInsn;
+    dexDecodeInstruction(pc, &decInsn);
+
+    //ALOGD("### DbgIntp(%d): PC: %#x endPC: %#x state: %d len: %d %s",
+    //    self->threadId, (int)pc, (int)shadowSpace->endPC, state,
+    //    shadowSpace->traceLength, dexGetOpcodeName(decInsn.opcode));
+
+    if (state == kSVSIdle || state == kSVSStart) {
+        ALOGD("~~~ DbgIntrp: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, state);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+    }
+
+    /*
+     * Generalize the self verification state to kSVSDebugInterp unless the
+     * entry reason is kSVSBackwardBranch or kSVSSingleStep.
+     */
+    if (state != kSVSBackwardBranch && state != kSVSSingleStep) {
+        shadowSpace->selfVerificationState = kSVSDebugInterp;
+    }
+
+    /*
+     * Check if the current pc matches the endPC. Only check for non-zero
+     * trace length when backward branches are involved.
+     */
+    if (pc == shadowSpace->endPC &&
+        (state == kSVSDebugInterp || state == kSVSSingleStep ||
+         (state == kSVSBackwardBranch && shadowSpace->traceLength != 0))) {
+
+        shadowSpace->selfVerificationState = kSVSIdle;
+
+        /* Check register space */
+        int frameBytes = (int) shadowSpace->registerSpace +
+                         shadowSpace->registerSpaceSize*4 -
+                         (int) shadowSpace->shadowFP;
+        if (memcmp(shadowSpace->fp, shadowSpace->shadowFP, frameBytes)) {
+            if (state == kSVSBackwardBranch) {
+                /* State mismatch on backward branch - try one more iteration */
+                shadowSpace->selfVerificationState = kSVSDebugInterp;
+                goto log_and_continue;
+            }
+            ALOGD("~~~ DbgIntp(%d): REGISTERS DIVERGENCE!", self->threadId);
+            selfVerificationDumpState(pc, self);
+            selfVerificationDumpTrace(pc, self);
+            ALOGD("*** Interp Registers: addr: %#x bytes: %d",
+                (int)shadowSpace->fp, frameBytes);
+            selfVerificationPrintRegisters((int*)shadowSpace->fp,
+                                           (int*)shadowSpace->shadowFP,
+                                           frameBytes/4);
+            ALOGD("*** Shadow Registers: addr: %#x bytes: %d",
+                (int)shadowSpace->shadowFP, frameBytes);
+            selfVerificationPrintRegisters((int*)shadowSpace->shadowFP,
+                                           (int*)shadowSpace->fp,
+                                           frameBytes/4);
+            selfVerificationSpinLoop(shadowSpace);
+        }
+        /* Check new frame if it exists (invokes only) */
+        if ((uintptr_t)self->interpSave.curFrame < (uintptr_t)shadowSpace->fp) {
+            StackSaveArea* stackSave =
+                SAVEAREA_FROM_FP(self->interpSave.curFrame);
+            int localRegs = (stackSave->method->registersSize -
+                             stackSave->method->insSize)*4;
+            int frameBytes2 = (int) shadowSpace->fp -
+                              (int) self->interpSave.curFrame - localRegs;
+            if (memcmp(((char*)self->interpSave.curFrame)+localRegs,
+                ((char*)shadowSpace->endShadowFP)+localRegs, frameBytes2)) {
+                if (state == kSVSBackwardBranch) {
+                    /*
+                     * State mismatch on backward branch - try one more
+                     * iteration.
+                     */
+                    shadowSpace->selfVerificationState = kSVSDebugInterp;
+                    goto log_and_continue;
+                }
+                ALOGD("~~~ DbgIntp(%d): REGISTERS (FRAME2) DIVERGENCE!",
+                    self->threadId);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                ALOGD("*** Interp Registers: addr: %#x l: %d bytes: %d",
+                    (int)self->interpSave.curFrame, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)self->interpSave.curFrame,
+                                               (int*)shadowSpace->endShadowFP,
+                                               (frameBytes2+localRegs)/4);
+                ALOGD("*** Shadow Registers: addr: %#x l: %d bytes: %d",
+                    (int)shadowSpace->endShadowFP, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)shadowSpace->endShadowFP,
+                                               (int*)self->interpSave.curFrame,
+                                               (frameBytes2+localRegs)/4);
+                selfVerificationSpinLoop(shadowSpace);
+            }
+        }
+
+        /* Check memory space */
+        bool memDiff = false;
+        ShadowHeap* heapSpacePtr;
+        for (heapSpacePtr = shadowSpace->heapSpace;
+             heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+            int memData = *((unsigned int*) heapSpacePtr->addr);
+            if (heapSpacePtr->data != memData) {
+                if (state == kSVSBackwardBranch) {
+                    /*
+                     * State mismatch on backward branch - try one more
+                     * iteration.
+                     */
+                    shadowSpace->selfVerificationState = kSVSDebugInterp;
+                    goto log_and_continue;
+                }
+                ALOGD("~~~ DbgIntp(%d): MEMORY DIVERGENCE!", self->threadId);
+                ALOGD("Addr: %#x Intrp Data: %#x Jit Data: %#x",
+                    heapSpacePtr->addr, memData, heapSpacePtr->data);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                memDiff = true;
+            }
+        }
+        if (memDiff) selfVerificationSpinLoop(shadowSpace);
+
+
+        /*
+         * Success.  If this shadowed trace included a single-stepped
+         * instruction, we need to stay in the interpreter for one
+         * more interpretation before resuming.
+         */
+        if (state == kSVSSingleStep) {
+            assert(self->jitResumeNPC != NULL);
+            assert(self->singleStepCount == 0);
+            self->singleStepCount = 1;
+            dvmEnableSubMode(self, kSubModeCountedStep);
+        }
+
+        /*
+         * Switch off shadow replay mode.  The next shadowed trace
+         * execution will turn it back on.
+         */
+        dvmDisableSubMode(self, kSubModeJitSV);
+
+        self->jitState = kJitDone;
+        return;
+    }
+log_and_continue:
+    /* If end not been reached, make sure max length not exceeded */
+    if (shadowSpace->traceLength >= JIT_MAX_TRACE_LEN) {
+        ALOGD("~~~ DbgIntp(%d): CONTROL DIVERGENCE!", self->threadId);
+        ALOGD("startPC: %#x endPC: %#x currPC: %#x",
+            (int)shadowSpace->startPC, (int)shadowSpace->endPC, (int)pc);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+        selfVerificationSpinLoop(shadowSpace);
+        return;
+    }
+    /* Log the instruction address and decoded instruction for debug */
+    shadowSpace->trace[shadowSpace->traceLength].addr = (int)pc;
+    shadowSpace->trace[shadowSpace->traceLength].decInsn = decInsn;
+    shadowSpace->traceLength++;
+}
+#endif
+
+/*
+ * If one of our fixed tables or the translation buffer fills up,
+ * call this routine to avoid wasting cycles on future translation requests.
+ */
+void dvmJitStopTranslationRequests()
+{
+    /*
+     * Note 1: This won't necessarily stop all translation requests, and
+     * operates on a delayed mechanism.  Running threads look to the copy
+     * of this value in their private thread structures and won't see
+     * this change until it is refreshed (which happens on interpreter
+     * entry).
+     * Note 2: This is a one-shot memory leak on this table. Because this is a
+     * permanent off switch for Jit profiling, it is a one-time leak of 1K
+     * bytes, and no further attempt will be made to re-allocate it.  Can't
+     * free it because some thread may be holding a reference.
+     */
+    gDvmJit.pProfTable = NULL;
+    dvmJitUpdateThreadStateAll();
+}
+
+#if defined(WITH_JIT_TUNING)
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNoChain(int from)
+{
+    gDvmJit.noChainExit[from]++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNormal()
+{
+    gDvmJit.normalExit++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpPunt(int from)
+{
+    gDvmJit.puntExit++;
+}
+#endif
+
+/* Dumps debugging & tuning stats to the log */
+void dvmJitStats()
+{
+    int i;
+    int hit;
+    int not_hit;
+    int chains;
+    int stubs;
+    if (gDvmJit.pJitEntryTable) {
+        for (i=0, stubs=chains=hit=not_hit=0;
+             i < (int) gDvmJit.jitTableSize;
+             i++) {
+            if (gDvmJit.pJitEntryTable[i].dPC != 0) {
+                hit++;
+                if (gDvmJit.pJitEntryTable[i].codeAddress ==
+                      dvmCompilerGetInterpretTemplate())
+                    stubs++;
+            } else
+                not_hit++;
+            if (gDvmJit.pJitEntryTable[i].u.info.chain != gDvmJit.jitTableSize)
+                chains++;
+        }
+        ALOGD("JIT: table size is %d, entries used is %d",
+             gDvmJit.jitTableSize,  gDvmJit.jitTableEntriesUsed);
+        ALOGD("JIT: %d traces, %d slots, %d chains, %d thresh, %s",
+             hit, not_hit + hit, chains, gDvmJit.threshold,
+             gDvmJit.blockingMode ? "Blocking" : "Non-blocking");
+
+#if defined(WITH_JIT_TUNING)
+        ALOGD("JIT: Code cache patches: %d", gDvmJit.codeCachePatches);
+
+        ALOGD("JIT: Lookups: %d hits, %d misses; %d normal, %d punt",
+             gDvmJit.addrLookupsFound, gDvmJit.addrLookupsNotFound,
+             gDvmJit.normalExit, gDvmJit.puntExit);
+
+        ALOGD("JIT: ICHits: %d", gDvmICHitCount);
+
+        ALOGD("JIT: noChainExit: %d IC miss, %d interp callsite, "
+             "%d switch overflow",
+             gDvmJit.noChainExit[kInlineCacheMiss],
+             gDvmJit.noChainExit[kCallsiteInterpreted],
+             gDvmJit.noChainExit[kSwitchOverflow]);
+
+        ALOGD("JIT: ICPatch: %d init, %d rejected, %d lock-free, %d queued, "
+             "%d dropped",
+             gDvmJit.icPatchInit, gDvmJit.icPatchRejected,
+             gDvmJit.icPatchLockFree, gDvmJit.icPatchQueued,
+             gDvmJit.icPatchDropped);
+
+        ALOGD("JIT: Invoke: %d mono, %d poly, %d native, %d return",
+             gDvmJit.invokeMonomorphic, gDvmJit.invokePolymorphic,
+             gDvmJit.invokeNative, gDvmJit.returnOp);
+        ALOGD("JIT: Inline: %d mgetter, %d msetter, %d pgetter, %d psetter",
+             gDvmJit.invokeMonoGetterInlined, gDvmJit.invokeMonoSetterInlined,
+             gDvmJit.invokePolyGetterInlined, gDvmJit.invokePolySetterInlined);
+        ALOGD("JIT: Total compilation time: %llu ms", gDvmJit.jitTime / 1000);
+        ALOGD("JIT: Avg unit compilation time: %llu us",
+             gDvmJit.numCompilations == 0 ? 0 :
+             gDvmJit.jitTime / gDvmJit.numCompilations);
+        ALOGD("JIT: Potential GC blocked by compiler: max %llu us / "
+             "avg %llu us (%d)",
+             gDvmJit.maxCompilerThreadBlockGCTime,
+             gDvmJit.numCompilerThreadBlockGC == 0 ?
+                 0 : gDvmJit.compilerThreadBlockGCTime /
+                     gDvmJit.numCompilerThreadBlockGC,
+             gDvmJit.numCompilerThreadBlockGC);
+#endif
+
+        ALOGD("JIT: %d Translation chains, %d interp stubs",
+             gDvmJit.translationChains, stubs);
+        if (gDvmJit.profileMode == kTraceProfilingContinuous) {
+            dvmCompilerSortAndPrintTraceProfiles();
+        }
+    }
+}
+
+
+/* End current trace now & don't include current instruction */
+void dvmJitEndTraceSelect(Thread* self, const u2* dPC)
+{
+    if (self->jitState == kJitTSelect) {
+        self->jitState = kJitTSelectEnd;
+    }
+    if (self->jitState == kJitTSelectEnd) {
+        // Clean up and finish now.
+        dvmCheckJit(dPC, self);
+    }
+}
+
+/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+static JitEntry *lookupAndAdd(const u2* dPC, bool callerLocked,
+                              bool isMethodEntry)
+{
+    u4 chainEndMarker = gDvmJit.jitTableSize;
+    u4 idx = dvmJitHash(dPC);
+
+    /*
+     * Walk the bucket chain to find an exact match for our PC and trace/method
+     * type
+     */
+    while ((gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) &&
+           ((gDvmJit.pJitEntryTable[idx].dPC != dPC) ||
+            (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry !=
+             isMethodEntry))) {
+        idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+    }
+
+    if (gDvmJit.pJitEntryTable[idx].dPC != dPC ||
+        gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry != isMethodEntry) {
+        /*
+         * No match.  Aquire jitTableLock and find the last
+         * slot in the chain. Possibly continue the chain walk in case
+         * some other thread allocated the slot we were looking
+         * at previuosly (perhaps even the dPC we're trying to enter).
+         */
+        if (!callerLocked)
+            dvmLockMutex(&gDvmJit.tableLock);
+        /*
+         * At this point, if .dPC is NULL, then the slot we're
+         * looking at is the target slot from the primary hash
+         * (the simple, and common case).  Otherwise we're going
+         * to have to find a free slot and chain it.
+         */
+        ANDROID_MEMBAR_FULL(); /* Make sure we reload [].dPC after lock */
+        if (gDvmJit.pJitEntryTable[idx].dPC != NULL) {
+            u4 prev;
+            while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+                if (gDvmJit.pJitEntryTable[idx].dPC == dPC &&
+                    gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                        isMethodEntry) {
+                    /* Another thread got there first for this dPC */
+                    if (!callerLocked)
+                        dvmUnlockMutex(&gDvmJit.tableLock);
+                    return &gDvmJit.pJitEntryTable[idx];
+                }
+                idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+            }
+            /* Here, idx should be pointing to the last cell of an
+             * active chain whose last member contains a valid dPC */
+            assert(gDvmJit.pJitEntryTable[idx].dPC != NULL);
+            /* Linear walk to find a free cell and add it to the end */
+            prev = idx;
+            while (true) {
+                idx++;
+                if (idx == chainEndMarker)
+                    idx = 0;  /* Wraparound */
+                if ((gDvmJit.pJitEntryTable[idx].dPC == NULL) ||
+                    (idx == prev))
+                    break;
+            }
+            if (idx != prev) {
+                JitEntryInfoUnion oldValue;
+                JitEntryInfoUnion newValue;
+                /*
+                 * Although we hold the lock so that noone else will
+                 * be trying to update a chain field, the other fields
+                 * packed into the word may be in use by other threads.
+                 */
+                do {
+                    oldValue = gDvmJit.pJitEntryTable[prev].u;
+                    newValue = oldValue;
+                    newValue.info.chain = idx;
+                } while (android_atomic_release_cas(oldValue.infoWord,
+                        newValue.infoWord,
+                        &gDvmJit.pJitEntryTable[prev].u.infoWord) != 0);
+            }
+        }
+        if (gDvmJit.pJitEntryTable[idx].dPC == NULL) {
+            gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry = isMethodEntry;
+            /*
+             * Initialize codeAddress and allocate the slot.  Must
+             * happen in this order (since dPC is set, the entry is live.
+             */
+            android_atomic_release_store((int32_t)dPC,
+                 (volatile int32_t *)(void *)&gDvmJit.pJitEntryTable[idx].dPC);
+            /* for simulator mode, we need to initialized codeAddress to null */
+            gDvmJit.pJitEntryTable[idx].codeAddress = NULL;
+            gDvmJit.pJitEntryTable[idx].dPC = dPC;
+            gDvmJit.jitTableEntriesUsed++;
+        } else {
+            /* Table is full */
+            idx = chainEndMarker;
+        }
+        if (!callerLocked)
+            dvmUnlockMutex(&gDvmJit.tableLock);
+    }
+    return (idx == chainEndMarker) ? NULL : &gDvmJit.pJitEntryTable[idx];
+}
+
+/* Dump a trace description */
+void dvmJitDumpTraceDesc(JitTraceDescription *trace)
+{
+    int i;
+    bool done = false;
+    const u2* dpc;
+    const u2* dpcBase;
+    int curFrag = 0;
+    ALOGD("===========================================");
+    ALOGD("Trace dump %#x, Method %s off %#x",(int)trace,
+         trace->method->name,trace->trace[curFrag].info.frag.startOffset);
+    dpcBase = trace->method->insns;
+    while (!done) {
+        DecodedInstruction decInsn;
+        if (trace->trace[curFrag].isCode) {
+            ALOGD("Frag[%d]- Insts: %d, start: %#x, hint: %#x, end: %d",
+                 curFrag, trace->trace[curFrag].info.frag.numInsts,
+                 trace->trace[curFrag].info.frag.startOffset,
+                 trace->trace[curFrag].info.frag.hint,
+                 trace->trace[curFrag].info.frag.runEnd);
+            dpc = dpcBase + trace->trace[curFrag].info.frag.startOffset;
+            for (i=0; i<trace->trace[curFrag].info.frag.numInsts; i++) {
+                dexDecodeInstruction(dpc, &decInsn);
+                ALOGD("    0x%04x - %s %#x",(dpc-dpcBase),
+                     dexGetOpcodeName(decInsn.opcode),(int)dpc);
+                dpc += dexGetWidthFromOpcode(decInsn.opcode);
+            }
+            if (trace->trace[curFrag].info.frag.runEnd) {
+                done = true;
+            }
+        } else {
+            ALOGD("Frag[%d]- META info: 0x%08x", curFrag,
+                 (int)trace->trace[curFrag].info.meta);
+        }
+        curFrag++;
+    }
+    ALOGD("-------------------------------------------");
+}
+
+/*
+ * Append the class ptr of "this" and the current method ptr to the current
+ * trace. That is, the trace runs will contain the following components:
+ *  + trace run that ends with an invoke (existing entry)
+ *  + thisClass (new)
+ *  + calleeMethod (new)
+ */
+static void insertClassMethodInfo(Thread* self,
+                                  const ClassObject* thisClass,
+                                  const Method* calleeMethod,
+                                  const DecodedInstruction* insn)
+{
+    int currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = thisClass ?
+                                    (void *) thisClass->descriptor : NULL;
+    self->trace[currTraceRun].isCode = false;
+
+    currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = thisClass ?
+                                    (void *) thisClass->classLoader : NULL;
+    self->trace[currTraceRun].isCode = false;
+
+    currTraceRun = ++self->currTraceRun;
+    self->trace[currTraceRun].info.meta = (void *) calleeMethod;
+    self->trace[currTraceRun].isCode = false;
+}
+
+/*
+ * Check if the next instruction following the invoke is a move-result and if
+ * so add it to the trace. That is, this will add the trace run that includes
+ * the move-result to the trace list.
+ *
+ *  + trace run that ends with an invoke (existing entry)
+ *  + thisClass (existing entry)
+ *  + calleeMethod (existing entry)
+ *  + move result (new)
+ *
+ * lastPC, len, offset are all from the preceding invoke instruction
+ */
+static void insertMoveResult(const u2 *lastPC, int len, int offset,
+                             Thread *self)
+{
+    DecodedInstruction nextDecInsn;
+    const u2 *moveResultPC = lastPC + len;
+
+    dexDecodeInstruction(moveResultPC, &nextDecInsn);
+    if ((nextDecInsn.opcode != OP_MOVE_RESULT) &&
+        (nextDecInsn.opcode != OP_MOVE_RESULT_WIDE) &&
+        (nextDecInsn.opcode != OP_MOVE_RESULT_OBJECT))
+        return;
+
+    /* We need to start a new trace run */
+    int currTraceRun = ++self->currTraceRun;
+    self->currRunHead = moveResultPC;
+    self->trace[currTraceRun].info.frag.startOffset = offset + len;
+    self->trace[currTraceRun].info.frag.numInsts = 1;
+    self->trace[currTraceRun].info.frag.runEnd = false;
+    self->trace[currTraceRun].info.frag.hint = kJitHintNone;
+    self->trace[currTraceRun].isCode = true;
+    self->totalTraceLen++;
+
+    self->currRunLen = dexGetWidthFromInstruction(moveResultPC);
+}
+
+/*
+ * Adds to the current trace request one instruction at a time, just
+ * before that instruction is interpreted.  This is the primary trace
+ * selection function.  NOTE: return instruction are handled a little
+ * differently.  In general, instructions are "proposed" to be added
+ * to the current trace prior to interpretation.  If the interpreter
+ * then successfully completes the instruction, is will be considered
+ * part of the request.  This allows us to examine machine state prior
+ * to interpretation, and also abort the trace request if the instruction
+ * throws or does something unexpected.  However, return instructions
+ * will cause an immediate end to the translation request - which will
+ * be passed to the compiler before the return completes.  This is done
+ * in response to special handling of returns by the interpreter (and
+ * because returns cannot throw in a way that causes problems for the
+ * translated code.
+ */
+void dvmCheckJit(const u2* pc, Thread* self)
+{
+    const ClassObject *thisClass = self->callsiteClass;
+    const Method* curMethod = self->methodToCall;
+    int flags, len;
+    int allDone = false;
+    /* Stay in break/single-stop mode for the next instruction */
+    bool stayOneMoreInst = false;
+
+    /* Prepare to handle last PC and stage the current PC & method*/
+    const u2 *lastPC = self->lastPC;
+
+    self->lastPC = pc;
+
+    switch (self->jitState) {
+        int offset;
+        DecodedInstruction decInsn;
+        case kJitTSelect:
+            /* First instruction - just remember the PC and exit */
+            if (lastPC == NULL) break;
+            /* Grow the trace around the last PC if jitState is kJitTSelect */
+            dexDecodeInstruction(lastPC, &decInsn);
+#if TRACE_OPCODE_FILTER
+            /* Only add JIT support opcode to trace. End the trace if
+             * this opcode is not supported.
+             */
+            if (!dvmIsOpcodeSupportedByJit(decInsn.opcode)) {
+                self->jitState = kJitTSelectEnd;
+                break;
+            }
+#endif
+            /*
+             * Treat {PACKED,SPARSE}_SWITCH as trace-ending instructions due
+             * to the amount of space it takes to generate the chaining
+             * cells.
+             */
+            if (self->totalTraceLen != 0 &&
+                (decInsn.opcode == OP_PACKED_SWITCH ||
+                 decInsn.opcode == OP_SPARSE_SWITCH)) {
+                self->jitState = kJitTSelectEnd;
+                break;
+            }
+
+#if defined(SHOW_TRACE)
+            ALOGD("TraceGen: adding %s. lpc:%#x, pc:%#x",
+                 dexGetOpcodeName(decInsn.opcode), (int)lastPC, (int)pc);
+#endif
+            flags = dexGetFlagsFromOpcode(decInsn.opcode);
+            len = dexGetWidthFromInstruction(lastPC);
+            offset = lastPC - self->traceMethod->insns;
+            assert((unsigned) offset <
+                   dvmGetMethodInsnsSize(self->traceMethod));
+            if (lastPC != self->currRunHead + self->currRunLen) {
+                int currTraceRun;
+                /* We need to start a new trace run */
+                currTraceRun = ++self->currTraceRun;
+                self->currRunLen = 0;
+                self->currRunHead = (u2*)lastPC;
+                self->trace[currTraceRun].info.frag.startOffset = offset;
+                self->trace[currTraceRun].info.frag.numInsts = 0;
+                self->trace[currTraceRun].info.frag.runEnd = false;
+                self->trace[currTraceRun].info.frag.hint = kJitHintNone;
+                self->trace[currTraceRun].isCode = true;
+            }
+            self->trace[self->currTraceRun].info.frag.numInsts++;
+            self->totalTraceLen++;
+            self->currRunLen += len;
+
+            /*
+             * If the last instruction is an invoke, we will try to sneak in
+             * the move-result* (if existent) into a separate trace run.
+             */
+            {
+              int needReservedRun = (flags & kInstrInvoke) ? 1 : 0;
+
+              /* Will probably never hit this with the current trace builder */
+              if (self->currTraceRun ==
+                   (MAX_JIT_RUN_LEN - 1 - needReservedRun)) {
+                self->jitState = kJitTSelectEnd;
+              }
+            }
+
+            if (!dexIsGoto(flags) &&
+                  ((flags & (kInstrCanBranch |
+                             kInstrCanSwitch |
+                             kInstrCanReturn |
+                             kInstrInvoke)) != 0)) {
+                    self->jitState = kJitTSelectEnd;
+#if defined(SHOW_TRACE)
+                ALOGD("TraceGen: ending on %s, basic block end",
+                     dexGetOpcodeName(decInsn.opcode));
+#endif
+
+                /*
+                 * If the current invoke is a {virtual,interface}, get the
+                 * current class/method pair into the trace as well.
+                 * If the next instruction is a variant of move-result, insert
+                 * it to the trace too.
+                 */
+                if (flags & kInstrInvoke) {
+                    insertClassMethodInfo(self, thisClass, curMethod,
+                                          &decInsn);
+                    insertMoveResult(lastPC, len, offset, self);
+                }
+            }
+            /* Break on throw or self-loop */
+            if ((decInsn.opcode == OP_THROW) || (lastPC == pc)){
+                self->jitState = kJitTSelectEnd;
+            }
+            if (self->totalTraceLen >= JIT_MAX_TRACE_LEN) {
+                self->jitState = kJitTSelectEnd;
+            }
+            if ((flags & kInstrCanReturn) != kInstrCanReturn) {
+                break;
+            }
+            else {
+                /*
+                 * Last instruction is a return - stay in the dbg interpreter
+                 * for one more instruction if it is a non-void return, since
+                 * we don't want to start a trace with move-result as the first
+                 * instruction (which is already included in the trace
+                 * containing the invoke.
+                 */
+                if (decInsn.opcode != OP_RETURN_VOID) {
+                    stayOneMoreInst = true;
+                }
+            }
+            /* NOTE: intentional fallthrough for returns */
+        case kJitTSelectEnd:
+            {
+                /* Empty trace - set to bail to interpreter */
+                if (self->totalTraceLen == 0) {
+                    dvmJitSetCodeAddr(self->currTraceHead,
+                                      dvmCompilerGetInterpretTemplate(),
+                                      dvmCompilerGetInterpretTemplateSet(),
+                                      false /* Not method entry */, 0);
+                    self->jitState = kJitDone;
+                    allDone = true;
+                    break;
+                }
+
+                int lastTraceDesc = self->currTraceRun;
+
+                /* Extend a new empty desc if the last slot is meta info */
+                if (!self->trace[lastTraceDesc].isCode) {
+                    lastTraceDesc = ++self->currTraceRun;
+                    self->trace[lastTraceDesc].info.frag.startOffset = 0;
+                    self->trace[lastTraceDesc].info.frag.numInsts = 0;
+                    self->trace[lastTraceDesc].info.frag.hint = kJitHintNone;
+                    self->trace[lastTraceDesc].isCode = true;
+                }
+
+                /* Mark the end of the trace runs */
+                self->trace[lastTraceDesc].info.frag.runEnd = true;
+
+                JitTraceDescription* desc =
+                   (JitTraceDescription*)malloc(sizeof(JitTraceDescription) +
+                     sizeof(JitTraceRun) * (self->currTraceRun+1));
+
+                if (desc == NULL) {
+                    ALOGE("Out of memory in trace selection");
+                    dvmJitStopTranslationRequests();
+                    self->jitState = kJitDone;
+                    allDone = true;
+                    break;
+                }
+
+                desc->method = self->traceMethod;
+                memcpy((char*)&(desc->trace[0]),
+                    (char*)&(self->trace[0]),
+                    sizeof(JitTraceRun) * (self->currTraceRun+1));
+#if defined(SHOW_TRACE)
+                ALOGD("TraceGen:  trace done, adding to queue");
+                dvmJitDumpTraceDesc(desc);
+#endif
+                if (dvmCompilerWorkEnqueue(
+                       self->currTraceHead,kWorkOrderTrace,desc)) {
+                    /* Work order successfully enqueued */
+                    if (gDvmJit.blockingMode) {
+                        dvmCompilerDrainQueue();
+                    }
+                } else {
+                    /*
+                     * Make sure the descriptor for the abandoned work order is
+                     * freed.
+                     */
+                    free(desc);
+                }
+                self->jitState = kJitDone;
+                allDone = true;
+            }
+            break;
+        case kJitDone:
+            allDone = true;
+            break;
+        case kJitNot:
+            allDone = true;
+            break;
+        default:
+            ALOGE("Unexpected JIT state: %d", self->jitState);
+            dvmAbort();
+            break;
+    }
+
+    /*
+     * If we're done with trace selection, switch off the control flags.
+     */
+     if (allDone) {
+         dvmDisableSubMode(self, kSubModeJitTraceBuild);
+         if (stayOneMoreInst) {
+             // Clear jitResumeNPC explicitly since we know we don't need it
+             // here.
+             self->jitResumeNPC = NULL;
+             // Keep going in single-step mode for at least one more inst
+             if (self->singleStepCount == 0)
+                 self->singleStepCount = 1;
+             dvmEnableSubMode(self, kSubModeCountedStep);
+         }
+     }
+     return;
+}
+
+JitEntry *dvmJitFindEntry(const u2* pc, bool isMethodEntry)
+{
+    int idx = dvmJitHash(pc);
+
+    /* Expect a high hit rate on 1st shot */
+    if ((gDvmJit.pJitEntryTable[idx].dPC == pc) &&
+        (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry == isMethodEntry))
+        return &gDvmJit.pJitEntryTable[idx];
+    else {
+        int chainEndMarker = gDvmJit.jitTableSize;
+        while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+            idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+            if ((gDvmJit.pJitEntryTable[idx].dPC == pc) &&
+                (gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                isMethodEntry))
+                return &gDvmJit.pJitEntryTable[idx];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Walk through the JIT profile table and find the corresponding JIT code, in
+ * the specified format (ie trace vs method). This routine needs to be fast.
+ */
+void* getCodeAddrCommon(const u2* dPC, bool methodEntry)
+{
+    int idx = dvmJitHash(dPC);
+    const u2* pc = gDvmJit.pJitEntryTable[idx].dPC;
+    if (pc != NULL) {
+        bool hideTranslation = dvmJitHideTranslation();
+        if (pc == dPC &&
+            gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry == methodEntry) {
+            int offset = (gDvmJit.profileMode >= kTraceProfilingContinuous) ?
+                 0 : gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+            intptr_t codeAddress =
+                (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.addrLookupsFound++;
+#endif
+            return hideTranslation || !codeAddress ?  NULL :
+                  (void *)(codeAddress + offset);
+        } else {
+            int chainEndMarker = gDvmJit.jitTableSize;
+            while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+                idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+                if (gDvmJit.pJitEntryTable[idx].dPC == dPC &&
+                    gDvmJit.pJitEntryTable[idx].u.info.isMethodEntry ==
+                        methodEntry) {
+                    int offset = (gDvmJit.profileMode >=
+                        kTraceProfilingContinuous) ? 0 :
+                        gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+                    intptr_t codeAddress =
+                        (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
+#if defined(WITH_JIT_TUNING)
+                    gDvmJit.addrLookupsFound++;
+#endif
+                    return hideTranslation || !codeAddress ? NULL :
+                        (void *)(codeAddress + offset);
+                }
+            }
+        }
+    }
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.addrLookupsNotFound++;
+#endif
+    return NULL;
+}
+
+/*
+ * If a translated code address, in trace format, exists for the davik byte code
+ * pointer return it.
+ */
+void* dvmJitGetTraceAddr(const u2* dPC)
+{
+    return getCodeAddrCommon(dPC, false /* method entry */);
+}
+
+/*
+ * If a translated code address, in whole-method format, exists for the davik
+ * byte code pointer return it.
+ */
+void* dvmJitGetMethodAddr(const u2* dPC)
+{
+    return getCodeAddrCommon(dPC, true /* method entry */);
+}
+
+/*
+ * Similar to dvmJitGetTraceAddr, but returns null if the calling
+ * thread is in a single-step mode.
+ */
+void* dvmJitGetTraceAddrThread(const u2* dPC, Thread* self)
+{
+    return (self->interpBreak.ctl.breakFlags != 0) ? NULL :
+            getCodeAddrCommon(dPC, false /* method entry */);
+}
+
+/*
+ * Similar to dvmJitGetMethodAddr, but returns null if the calling
+ * thread is in a single-step mode.
+ */
+void* dvmJitGetMethodAddrThread(const u2* dPC, Thread* self)
+{
+    return (self->interpBreak.ctl.breakFlags != 0) ? NULL :
+            getCodeAddrCommon(dPC, true /* method entry */);
+}
+
+/*
+ * Register the translated code pointer into the JitTable.
+ * NOTE: Once a codeAddress field transitions from initial state to
+ * JIT'd code, it must not be altered without first halting all
+ * threads.  We defer the setting of the profile prefix size until
+ * after the new code address is set to ensure that the prefix offset
+ * is never applied to the initial interpret-only translation.  All
+ * translations with non-zero profile prefixes will still be correct
+ * if entered as if the profile offset is 0, but the interpret-only
+ * template cannot handle a non-zero prefix.
+ * NOTE: JitTable must not be in danger of reset while this
+ * code is executing. see Issue 4271784 for details.
+ */
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+                       bool isMethodEntry, int profilePrefixSize)
+{
+    JitEntryInfoUnion oldValue;
+    JitEntryInfoUnion newValue;
+    /*
+     * Get the JitTable slot for this dPC (or create one if JitTable
+     * has been reset between the time the trace was requested and
+     * now.
+     */
+    JitEntry *jitEntry = isMethodEntry ?
+        lookupAndAdd(dPC, false /* caller holds tableLock */, isMethodEntry) :
+                     dvmJitFindEntry(dPC, isMethodEntry);
+    assert(jitEntry);
+    /* Note: order of update is important */
+    do {
+        oldValue = jitEntry->u;
+        newValue = oldValue;
+        newValue.info.isMethodEntry = isMethodEntry;
+        newValue.info.instructionSet = set;
+        newValue.info.profileOffset = profilePrefixSize;
+    } while (android_atomic_release_cas(
+             oldValue.infoWord, newValue.infoWord,
+             &jitEntry->u.infoWord) != 0);
+    jitEntry->codeAddress = nPC;
+}
+
+/*
+ * Determine if valid trace-bulding request is active.  If so, set
+ * the proper flags in interpBreak and return.  Trace selection will
+ * then begin normally via dvmCheckBefore.
+ */
+void dvmJitCheckTraceRequest(Thread* self)
+{
+    int i;
+    /*
+     * A note on trace "hotness" filtering:
+     *
+     * Our first level trigger is intentionally loose - we need it to
+     * fire easily not just to identify potential traces to compile, but
+     * also to allow re-entry into the code cache.
+     *
+     * The 2nd level filter (done here) exists to be selective about
+     * what we actually compile.  It works by requiring the same
+     * trace head "key" (defined as filterKey below) to appear twice in
+     * a relatively short period of time.   The difficulty is defining the
+     * shape of the filterKey.  Unfortunately, there is no "one size fits
+     * all" approach.
+     *
+     * For spiky execution profiles dominated by a smallish
+     * number of very hot loops, we would want the second-level filter
+     * to be very selective.  A good selective filter is requiring an
+     * exact match of the Dalvik PC.  In other words, defining filterKey as:
+     *     intptr_t filterKey = (intptr_t)self->interpSave.pc
+     *
+     * However, for flat execution profiles we do best when aggressively
+     * translating.  A heuristically decent proxy for this is to use
+     * the value of the method pointer containing the trace as the filterKey.
+     * Intuitively, this is saying that once any trace in a method appears hot,
+     * immediately translate any other trace from that same method that
+     * survives the first-level filter.  Here, filterKey would be defined as:
+     *     intptr_t filterKey = (intptr_t)self->interpSave.method
+     *
+     * The problem is that we can't easily detect whether we're dealing
+     * with a spiky or flat profile.  If we go with the "pc" match approach,
+     * flat profiles perform poorly.  If we go with the loose "method" match,
+     * we end up generating a lot of useless translations.  Probably the
+     * best approach in the future will be to retain profile information
+     * across runs of each application in order to determine it's profile,
+     * and then choose once we have enough history.
+     *
+     * However, for now we've decided to chose a compromise filter scheme that
+     * includes elements of both.  The high order bits of the filter key
+     * are drawn from the enclosing method, and are combined with a slice
+     * of the low-order bits of the Dalvik pc of the trace head.  The
+     * looseness of the filter can be adjusted by changing with width of
+     * the Dalvik pc slice (JIT_TRACE_THRESH_FILTER_PC_BITS).  The wider
+     * the slice, the tighter the filter.
+     *
+     * Note: the fixed shifts in the function below reflect assumed word
+     * alignment for method pointers, and half-word alignment of the Dalvik pc.
+     * for method pointers and half-word alignment for dalvik pc.
+     */
+    u4 methodKey = (u4)self->interpSave.method <<
+                   (JIT_TRACE_THRESH_FILTER_PC_BITS - 2);
+    u4 pcKey = ((u4)self->interpSave.pc >> 1) &
+               ((1 << JIT_TRACE_THRESH_FILTER_PC_BITS) - 1);
+    intptr_t filterKey = (intptr_t)(methodKey | pcKey);
+
+    // Shouldn't be here if already building a trace.
+    assert((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild)==0);
+
+    /* Check if the JIT request can be handled now */
+    if ((gDvmJit.pJitEntryTable != NULL) &&
+        ((self->interpBreak.ctl.breakFlags & kInterpSingleStep) == 0)){
+        /* Bypass the filter for hot trace requests or during stress mode */
+        if (self->jitState == kJitTSelectRequest &&
+            gDvmJit.threshold > 6) {
+            /* Two-level filtering scheme */
+            for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
+                if (filterKey == self->threshFilter[i]) {
+                    self->threshFilter[i] = 0; // Reset filter entry
+                    break;
+                }
+            }
+            if (i == JIT_TRACE_THRESH_FILTER_SIZE) {
+                /*
+                 * Use random replacement policy - otherwise we could miss a
+                 * large loop that contains more traces than the size of our
+                 * filter array.
+                 */
+                i = rand() % JIT_TRACE_THRESH_FILTER_SIZE;
+                self->threshFilter[i] = filterKey;
+                self->jitState = kJitDone;
+            }
+        }
+
+        /* If the compiler is backlogged, cancel any JIT actions */
+        if (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater) {
+            self->jitState = kJitDone;
+        }
+
+        /*
+         * Check for additional reasons that might force the trace select
+         * request to be dropped
+         */
+        if (self->jitState == kJitTSelectRequest ||
+            self->jitState == kJitTSelectRequestHot) {
+            if (dvmJitFindEntry(self->interpSave.pc, false)) {
+                /* In progress - nothing do do */
+               self->jitState = kJitDone;
+            } else {
+                JitEntry *slot = lookupAndAdd(self->interpSave.pc,
+                                              false /* lock */,
+                                              false /* method entry */);
+                if (slot == NULL) {
+                    /*
+                     * Table is full.  This should have been
+                     * detected by the compiler thread and the table
+                     * resized before we run into it here.  Assume bad things
+                     * are afoot and disable profiling.
+                     */
+                    self->jitState = kJitDone;
+                    ALOGD("JIT: JitTable full, disabling profiling");
+                    dvmJitStopTranslationRequests();
+                }
+            }
+        }
+
+        switch (self->jitState) {
+            case kJitTSelectRequest:
+            case kJitTSelectRequestHot:
+                self->jitState = kJitTSelect;
+                self->traceMethod = self->interpSave.method;
+                self->currTraceHead = self->interpSave.pc;
+                self->currTraceRun = 0;
+                self->totalTraceLen = 0;
+                self->currRunHead = self->interpSave.pc;
+                self->currRunLen = 0;
+                self->trace[0].info.frag.startOffset =
+                     self->interpSave.pc - self->interpSave.method->insns;
+                self->trace[0].info.frag.numInsts = 0;
+                self->trace[0].info.frag.runEnd = false;
+                self->trace[0].info.frag.hint = kJitHintNone;
+                self->trace[0].isCode = true;
+                self->lastPC = 0;
+                /* Turn on trace selection mode */
+                dvmEnableSubMode(self, kSubModeJitTraceBuild);
+#if defined(SHOW_TRACE)
+                ALOGD("Starting trace for %s at %#x",
+                     self->interpSave.method->name, (int)self->interpSave.pc);
+#endif
+                break;
+            case kJitDone:
+                break;
+            default:
+                ALOGE("Unexpected JIT state: %d", self->jitState);
+                dvmAbort();
+        }
+    } else {
+        /* Cannot build trace this time */
+        self->jitState = kJitDone;
+    }
+}
+
+/*
+ * Resizes the JitTable.  Must be a power of 2, and returns true on failure.
+ * Stops all threads, and thus is a heavyweight operation. May only be called
+ * by the compiler thread.
+ */
+bool dvmJitResizeJitTable( unsigned int size )
+{
+    JitEntry *pNewTable;
+    JitEntry *pOldTable;
+    JitEntry tempEntry;
+    unsigned int oldSize;
+    unsigned int i;
+
+    assert(gDvmJit.pJitEntryTable != NULL);
+    assert(size && !(size & (size - 1)));   /* Is power of 2? */
+
+    ALOGI("Jit: resizing JitTable from %d to %d", gDvmJit.jitTableSize, size);
+
+    if (size <= gDvmJit.jitTableSize) {
+        return true;
+    }
+
+    /* Make sure requested size is compatible with chain field width */
+    tempEntry.u.info.chain = size;
+    if (tempEntry.u.info.chain != size) {
+        ALOGD("Jit: JitTable request of %d too big", size);
+        return true;
+    }
+
+    pNewTable = (JitEntry*)calloc(size, sizeof(*pNewTable));
+    if (pNewTable == NULL) {
+        return true;
+    }
+    for (i=0; i< size; i++) {
+        pNewTable[i].u.info.chain = size;  /* Initialize chain termination */
+    }
+
+    /* Stop all other interpreting/jit'ng threads */
+    dvmSuspendAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+    pOldTable = gDvmJit.pJitEntryTable;
+    oldSize = gDvmJit.jitTableSize;
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    gDvmJit.pJitEntryTable = pNewTable;
+    gDvmJit.jitTableSize = size;
+    gDvmJit.jitTableMask = size - 1;
+    gDvmJit.jitTableEntriesUsed = 0;
+
+    for (i=0; i < oldSize; i++) {
+        if (pOldTable[i].dPC) {
+            JitEntry *p;
+            u2 chain;
+            p = lookupAndAdd(pOldTable[i].dPC, true /* holds tableLock*/,
+                             pOldTable[i].u.info.isMethodEntry);
+            p->codeAddress = pOldTable[i].codeAddress;
+            /* We need to preserve the new chain field, but copy the rest */
+            chain = p->u.info.chain;
+            p->u = pOldTable[i].u;
+            p->u.info.chain = chain;
+        }
+    }
+
+    dvmUnlockMutex(&gDvmJit.tableLock);
+
+    free(pOldTable);
+
+    /* Restart the world */
+    dvmResumeAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+    return false;
+}
+
+/*
+ * Reset the JitTable to the initial clean state.
+ */
+void dvmJitResetTable()
+{
+    JitEntry *jitEntry = gDvmJit.pJitEntryTable;
+    unsigned int size = gDvmJit.jitTableSize;
+    unsigned int i;
+
+    dvmLockMutex(&gDvmJit.tableLock);
+
+    /* Note: If need to preserve any existing counts. Do so here. */
+    if (gDvmJit.pJitTraceProfCounters) {
+        for (i=0; i < JIT_PROF_BLOCK_BUCKETS; i++) {
+            if (gDvmJit.pJitTraceProfCounters->buckets[i])
+                memset((void *) gDvmJit.pJitTraceProfCounters->buckets[i],
+                       0, sizeof(JitTraceCounter_t) * JIT_PROF_BLOCK_ENTRIES);
+        }
+        gDvmJit.pJitTraceProfCounters->next = 0;
+    }
+
+    memset((void *) jitEntry, 0, sizeof(JitEntry) * size);
+    for (i=0; i< size; i++) {
+        jitEntry[i].u.info.chain = size;  /* Initialize chain termination */
+    }
+    gDvmJit.jitTableEntriesUsed = 0;
+    dvmUnlockMutex(&gDvmJit.tableLock);
+}
+
+/*
+ * Return the address of the next trace profile counter.  This address
+ * will be embedded in the generated code for the trace, and thus cannot
+ * change while the trace exists.
+ */
+JitTraceCounter_t *dvmJitNextTraceCounter()
+{
+    int idx = gDvmJit.pJitTraceProfCounters->next / JIT_PROF_BLOCK_ENTRIES;
+    int elem = gDvmJit.pJitTraceProfCounters->next % JIT_PROF_BLOCK_ENTRIES;
+    JitTraceCounter_t *res;
+    /* Lazily allocate blocks of counters */
+    if (!gDvmJit.pJitTraceProfCounters->buckets[idx]) {
+        JitTraceCounter_t *p =
+              (JitTraceCounter_t*) calloc(JIT_PROF_BLOCK_ENTRIES, sizeof(*p));
+        if (!p) {
+            ALOGE("Failed to allocate block of trace profile counters");
+            dvmAbort();
+        }
+        gDvmJit.pJitTraceProfCounters->buckets[idx] = p;
+    }
+    res = &gDvmJit.pJitTraceProfCounters->buckets[idx][elem];
+    gDvmJit.pJitTraceProfCounters->next++;
+    return res;
+}
+
+/*
+ * Float/double conversion requires clamping to min and max of integer form.  If
+ * target doesn't support this normally, use these.
+ */
+s8 dvmJitd2l(double d)
+{
+    static const double kMaxLong = (double)(s8)0x7fffffffffffffffULL;
+    static const double kMinLong = (double)(s8)0x8000000000000000ULL;
+    if (d >= kMaxLong)
+        return (s8)0x7fffffffffffffffULL;
+    else if (d <= kMinLong)
+        return (s8)0x8000000000000000ULL;
+    else if (d != d) // NaN case
+        return 0;
+    else
+        return (s8)d;
+}
+
+s8 dvmJitf2l(float f)
+{
+    static const float kMaxLong = (float)(s8)0x7fffffffffffffffULL;
+    static const float kMinLong = (float)(s8)0x8000000000000000ULL;
+    if (f >= kMaxLong)
+        return (s8)0x7fffffffffffffffULL;
+    else if (f <= kMinLong)
+        return (s8)0x8000000000000000ULL;
+    else if (f != f) // NaN case
+        return 0;
+    else
+        return (s8)f;
+}
+
+/* Should only be called by the compiler thread */
+void dvmJitChangeProfileMode(TraceProfilingModes newState)
+{
+    if (gDvmJit.profileMode != newState) {
+        gDvmJit.profileMode = newState;
+        dvmJitUnchainAll();
+    }
+}
+
+void dvmJitTraceProfilingOn()
+{
+    if (gDvmJit.profileMode == kTraceProfilingPeriodicOff)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingPeriodicOn);
+    else if (gDvmJit.profileMode == kTraceProfilingDisabled)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingContinuous);
+}
+
+void dvmJitTraceProfilingOff()
+{
+    if (gDvmJit.profileMode == kTraceProfilingPeriodicOn)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingPeriodicOff);
+    else if (gDvmJit.profileMode == kTraceProfilingContinuous)
+        dvmCompilerForceWorkEnqueue(NULL, kWorkOrderProfileMode,
+                                    (void*) kTraceProfilingDisabled);
+}
+
+/*
+ * Update JIT-specific info in Thread structure for a single thread
+ */
+void dvmJitUpdateThreadStateSingle(Thread* thread)
+{
+    thread->pJitProfTable = gDvmJit.pProfTable;
+    thread->jitThreshold = gDvmJit.threshold;
+}
+
+/*
+ * Walk through the thread list and refresh all local copies of
+ * JIT global state (which was placed there for fast access).
+ */
+void dvmJitUpdateThreadStateAll()
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+
+    dvmLockThreadList(self);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        dvmJitUpdateThreadStateSingle(thread);
+    }
+    dvmUnlockThreadList();
+
+}
+#endif /* WITH_JIT */
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
new file mode 100644
index 0000000..962040b
--- /dev/null
+++ b/vm/interp/Jit.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+/*
+ * Jit control
+ */
+#ifndef DALVIK_INTERP_JIT_H_
+#define DALVIK_INTERP_JIT_H_
+
+#include "InterpDefs.h"
+#include "mterp/common/jit-config.h"
+
+#define JIT_MAX_TRACE_LEN 100
+
+#if defined (WITH_SELF_VERIFICATION)
+
+#define REG_SPACE 256                /* default size of shadow space */
+#define HEAP_SPACE JIT_MAX_TRACE_LEN /* default size of heap space */
+
+struct ShadowHeap {
+    int addr;
+    int data;
+};
+
+struct InstructionTrace {
+    int addr;
+    DecodedInstruction decInsn;
+};
+
+struct ShadowSpace {
+    const u2* startPC;          /* starting pc of jitted region */
+    u4* fp;                     /* starting fp of jitted region */
+    const Method *method;
+    DvmDex* methodClassDex;
+    JValue retval;
+    const u1* interpStackEnd;
+    SelfVerificationState jitExitState;  /* exit point for JIT'ed code */
+    SelfVerificationState selfVerificationState;  /* current SV running state */
+    const u2* endPC;            /* ending pc of jitted region */
+    void* shadowFP;       /* pointer to fp in shadow space */
+    int* registerSpace;         /* copy of register state */
+    int registerSpaceSize;      /* current size of register space */
+    ShadowHeap heapSpace[HEAP_SPACE]; /* copy of heap space */
+    ShadowHeap* heapSpaceTail;        /* tail pointer to heapSpace */
+    const void* endShadowFP;    /* ending fp in shadow space */
+    InstructionTrace trace[JIT_MAX_TRACE_LEN]; /* opcode trace for debugging */
+    int traceLength;            /* counter for current trace length */
+};
+
+/*
+ * Self verification functions.
+ */
+extern "C" {
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self);
+void dvmSelfVerificationShadowSpaceFree(Thread* self);
+void* dvmSelfVerificationSaveState(const u2* pc, u4* fp,
+                                   Thread* self,
+                                   int targetTrace);
+void* dvmSelfVerificationRestoreState(const u2* pc, u4* fp,
+                                      SelfVerificationState exitPoint,
+                                      Thread *self);
+void dvmCheckSelfVerification(const u2* pc, Thread* self);
+}
+#endif
+
+/*
+ * Offsets for metadata in the trace run array from the trace that ends with
+ * invoke instructions.
+ */
+#define JIT_TRACE_CLASS_DESC    1
+#define JIT_TRACE_CLASS_LOADER  2
+#define JIT_TRACE_CUR_METHOD    3
+
+/*
+ * JitTable hash function.
+ */
+
+static inline u4 dvmJitHashMask( const u2* p, u4 mask ) {
+    return ((((u4)p>>12)^(u4)p)>>1) & (mask);
+}
+
+static inline u4 dvmJitHash( const u2* p ) {
+    return dvmJitHashMask( p, gDvmJit.jitTableMask );
+}
+
+/*
+ * The width of the chain field in JitEntryInfo sets the upper
+ * bound on the number of translations.  Be careful if changing
+ * the size of JitEntry struct - the Dalvik PC to JitEntry
+ * hash functions have built-in knowledge of the size.
+ */
+#define JIT_ENTRY_CHAIN_WIDTH 2
+#define JIT_MAX_ENTRIES (1 << (JIT_ENTRY_CHAIN_WIDTH * 8))
+
+/*
+ * The trace profiling counters are allocated in blocks and individual
+ * counters must not move so long as any referencing trace exists.
+ */
+#define JIT_PROF_BLOCK_ENTRIES 1024
+#define JIT_PROF_BLOCK_BUCKETS (JIT_MAX_ENTRIES / JIT_PROF_BLOCK_ENTRIES)
+
+typedef s4 JitTraceCounter_t;
+
+struct JitTraceProfCounters {
+    unsigned int           next;
+    JitTraceCounter_t      *buckets[JIT_PROF_BLOCK_BUCKETS];
+};
+
+/*
+ * Entries in the JIT's address lookup hash table.
+ * Fields which may be updated by multiple threads packed into a
+ * single 32-bit word to allow use of atomic update.
+ */
+
+struct JitEntryInfo {
+    unsigned int           isMethodEntry:1;
+    unsigned int           inlineCandidate:1;
+    unsigned int           profileEnabled:1;
+    JitInstructionSetType  instructionSet:3;
+    unsigned int           profileOffset:5;
+    unsigned int           unused:5;
+    u2                     chain;                 /* Index of next in chain */
+};
+
+union JitEntryInfoUnion {
+    JitEntryInfo info;
+    volatile int infoWord;
+};
+
+struct JitEntry {
+    JitEntryInfoUnion   u;
+    const u2*           dPC;            /* Dalvik code address */
+    void*               codeAddress;    /* Code address of native translation */
+};
+
+extern "C" {
+void dvmCheckJit(const u2* pc, Thread* self);
+void* dvmJitGetTraceAddr(const u2* dPC);
+void* dvmJitGetMethodAddr(const u2* dPC);
+void* dvmJitGetTraceAddrThread(const u2* dPC, Thread* self);
+void* dvmJitGetMethodAddrThread(const u2* dPC, Thread* self);
+void dvmJitCheckTraceRequest(Thread* self);
+void dvmJitStopTranslationRequests(void);
+#if defined(WITH_JIT_TUNING)
+void dvmBumpNoChain(int from);
+void dvmBumpNormal(void);
+void dvmBumpPunt(int from);
+#endif
+void dvmJitStats(void);
+bool dvmJitResizeJitTable(unsigned int size);
+void dvmJitResetTable(void);
+JitEntry *dvmJitFindEntry(const u2* pc, bool isMethodEntry);
+s8 dvmJitd2l(double d);
+s8 dvmJitf2l(float f);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+                       bool isMethodEntry, int profilePrefixSize);
+void dvmJitEndTraceSelect(Thread* self, const u2* dPC);
+JitTraceCounter_t *dvmJitNextTraceCounter(void);
+void dvmJitTraceProfilingOff(void);
+void dvmJitTraceProfilingOn(void);
+void dvmJitChangeProfileMode(TraceProfilingModes newState);
+void dvmJitDumpTraceDesc(JitTraceDescription *trace);
+void dvmJitUpdateThreadStateSingle(Thread* threead);
+void dvmJitUpdateThreadStateAll(void);
+void dvmJitResumeTranslation(Thread* self, const u2* pc, const u4* fp);
+}
+
+#endif  // DALVIK_INTERP_JIT_H_
diff --git a/vm/interp/README.txt b/vm/interp/README.txt
new file mode 100644
index 0000000..170d2b1
--- /dev/null
+++ b/vm/interp/README.txt
@@ -0,0 +1,3 @@
+Dalvik interpreter entry point.
+
+The "mterp" directory now holds the interpreter implementation.
diff --git a/vm/interp/Stack.cpp b/vm/interp/Stack.cpp
new file mode 100644
index 0000000..ef0a0de
--- /dev/null
+++ b/vm/interp/Stack.cpp
@@ -0,0 +1,1403 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Stacks and their uses (e.g. native --> interpreted method calls).
+ *
+ * See the majestic ASCII art in Stack.h.
+ */
+#include "Dalvik.h"
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <backtrace/Backtrace.h>
+#include "UniquePtr.h"
+#endif
+
+/*
+ * Initialize the interpreter stack in a new thread.
+ *
+ * Currently this doesn't do much, since we don't need to zero out the
+ * stack (and we really don't want to if it was created with mmap).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize)
+{
+    assert(thread->interpStackStart != NULL);
+
+    assert(thread->interpSave.curFrame == NULL);
+
+    return true;
+}
+
+/*
+ * We're calling an interpreted method from an internal VM function or
+ * via reflection.
+ *
+ * Push a frame for an interpreted method onto the stack.  This is only
+ * used when calling into interpreted code from native code.  (The
+ * interpreter does its own stack frame manipulation for interp-->interp
+ * calls.)
+ *
+ * The size we need to reserve is the sum of parameters, local variables,
+ * saved goodies, and outbound parameters.
+ *
+ * We start by inserting a "break" frame, which ensures that the interpreter
+ * hands control back to us after the function we call returns or an
+ * uncaught exception is thrown.
+ */
+static bool dvmPushInterpFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    StackSaveArea* breakSaveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(!dvmIsNativeMethod(method));
+    assert(!dvmIsAbstractMethod(method));
+
+    stackReq = method->registersSize * 4        // params + locals
+                + sizeof(StackSaveArea) * 2     // break frame + regular frame
+                + method->outsSize * 4;         // args to other methods
+
+    if (self->interpSave.curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        ALOGW("Stack overflow on call to interp "
+             "(req=%d top=%p cur=%p size=%d %s.%s)",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->clazz->descriptor, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for the function's
+     * args/registers and save area.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    breakSaveBlock = (StackSaveArea*)stackPtr;
+    stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack, unless we want valgrind's help */
+    memset(stackPtr - (method->outsSize*4), 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    breakSaveBlock->prevSave =
+       (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame);
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->interpSave.curFrame;
+    breakSaveBlock->savedPc = NULL;             // not required
+    breakSaveBlock->xtra.localRefCookie = 0;    // not required
+    breakSaveBlock->method = NULL;
+    saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.currentPc = NULL;           // not required?
+    saveBlock->method = method;
+
+    LOGVV("PUSH frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * We're calling a JNI native method from an internal VM fuction or
+ * via reflection.  This is also used to create the "fake" native-method
+ * frames at the top of the interpreted stack.
+ *
+ * This actually pushes two frames; the first is a "break" frame.
+ *
+ * The top frame has additional space for JNI local reference tracking.
+ */
+bool dvmPushJNIFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    StackSaveArea* breakSaveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(dvmIsNativeMethod(method));
+
+    stackReq = method->registersSize * 4        // params only
+                + sizeof(StackSaveArea) * 2;    // break frame + regular frame
+
+    if (self->interpSave.curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        ALOGW("Stack overflow on call to native "
+             "(req=%d top=%p cur=%p size=%d '%s')",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for just the stack save
+     * area for the break frame, then shift down farther for the full frame.
+     * We leave space for the method args, which are copied in later.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    breakSaveBlock = (StackSaveArea*)stackPtr;
+    stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack */
+    memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    if (self->interpSave.curFrame == NULL)
+        breakSaveBlock->prevSave = NULL;
+    else {
+        void* fp = FP_FROM_SAVEAREA(self->interpSave.curFrame);
+        breakSaveBlock->prevSave = (StackSaveArea*)fp;
+    }
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->interpSave.curFrame;
+    breakSaveBlock->savedPc = NULL;             // not required
+    breakSaveBlock->xtra.localRefCookie = 0;    // not required
+    breakSaveBlock->method = NULL;
+    saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * This is used by the JNI PushLocalFrame call.  We push a new frame onto
+ * the stack that has no ins, outs, or locals, and no break frame above it.
+ * It's strictly used for tracking JNI local refs, and will be popped off
+ * by dvmPopFrame if it's not removed explicitly.
+ */
+bool dvmPushLocalFrame(Thread* self, const Method* method)
+{
+    StackSaveArea* saveBlock;
+    int stackReq;
+    u1* stackPtr;
+
+    assert(dvmIsNativeMethod(method));
+
+    stackReq = sizeof(StackSaveArea);       // regular frame
+
+    assert(self->interpSave.curFrame != NULL);
+    stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame);
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space; let JNI throw the exception */
+        ALOGW("Stack overflow on PushLocal "
+             "(req=%d top=%p cur=%p size=%d '%s')",
+            stackReq, self->interpStackStart, self->interpSave.curFrame,
+            self->interpStackSize, method->name);
+        dvmHandleStackOverflow(self, method);
+        assert(dvmCheckException(self));
+        return false;
+    }
+
+    /*
+     * Shift the stack pointer down, leaving space for just the stack save
+     * area for the break frame, then shift down farther for the full frame.
+     */
+    stackPtr -= sizeof(StackSaveArea);
+    saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+    /* debug -- memset the new stack */
+    memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+    saveBlock->prevSave =
+        (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame);
+#endif
+
+    saveBlock->prevFrame = self->interpSave.curFrame;
+    saveBlock->savedPc = NULL;                  // not required
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)",
+        self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+    return true;
+}
+
+/*
+ * Pop one frame pushed on by JNI PushLocalFrame.
+ *
+ * If we've gone too far, the previous frame is either a break frame or
+ * an interpreted frame.  Either way, the method pointer won't match.
+ */
+bool dvmPopLocalFrame(Thread* self)
+{
+    StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+
+    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+    if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) {
+        /*
+         * The previous frame doesn't have the same method pointer -- we've
+         * been asked to pop too much.
+         */
+        assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) ||
+               !dvmIsNativeMethod(
+                       SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
+        return false;
+    }
+
+    LOGVV("POP JNI local frame: removing %s, now %s",
+        saveBlock->method->name,
+        SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name);
+    dvmPopJniLocals(self, saveBlock);
+    self->interpSave.curFrame = saveBlock->prevFrame;
+
+    return true;
+}
+
+/*
+ * Pop a frame we added.  There should be one method frame and one break
+ * frame.
+ *
+ * If JNI Push/PopLocalFrame calls were mismatched, we might end up
+ * popping multiple method frames before we find the break.
+ *
+ * Returns "false" if there was no frame to pop.
+ */
+static bool dvmPopFrame(Thread* self)
+{
+    StackSaveArea* saveBlock;
+
+    if (self->interpSave.curFrame == NULL)
+        return false;
+
+    saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame));
+
+    /*
+     * Remove everything up to the break frame.  If this was a call into
+     * native code, pop the JNI local references table.
+     */
+    while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) {
+        /* probably a native->native JNI call */
+
+        if (dvmIsNativeMethod(saveBlock->method)) {
+            LOGVV("Popping JNI stack frame for %s.%s%s",
+                saveBlock->method->clazz->descriptor,
+                saveBlock->method->name,
+                (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
+                "" : " (JNI local)");
+            dvmPopJniLocals(self, saveBlock);
+        }
+
+        saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
+    }
+    if (saveBlock->method != NULL) {
+        ALOGE("PopFrame missed the break");
+        assert(false);
+        dvmAbort();     // stack trashed -- nowhere to go in this thread
+    }
+
+    LOGVV("POP frame: cur=%p new=%p",
+        self->interpSave.curFrame, saveBlock->prevFrame);
+
+    self->interpSave.curFrame = saveBlock->prevFrame;
+    return true;
+}
+
+/*
+ * Common code for dvmCallMethodV/A and dvmInvokeMethod.
+ *
+ * Pushes a call frame on, advancing self->interpSave.curFrame.
+ */
+static ClassObject* callPrep(Thread* self, const Method* method, Object* obj,
+    bool checkAccess)
+{
+    ClassObject* clazz;
+
+#ifndef NDEBUG
+    if (self->status != THREAD_RUNNING) {
+        ALOGW("threadid=%d: status=%d on call to %s.%s -",
+            self->threadId, self->status,
+            method->clazz->descriptor, method->name);
+    }
+#endif
+
+    assert(self != NULL);
+    assert(method != NULL);
+
+    if (obj != NULL)
+        clazz = obj->clazz;
+    else
+        clazz = method->clazz;
+
+    IF_LOGVV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        LOGVV("thread=%d native code calling %s.%s %s", self->threadId,
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    if (checkAccess) {
+        /* needed for java.lang.reflect.Method.invoke */
+        if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->interpSave.curFrame),
+                method))
+        {
+            /* note this throws IAException, not IAError */
+            dvmThrowIllegalAccessException("access to method denied");
+            return NULL;
+        }
+    }
+
+    /*
+     * Push a call frame on.  If there isn't enough room for ins, locals,
+     * outs, and the saved state, it will throw an exception.
+     *
+     * This updates self->interpSave.curFrame.
+     */
+    if (dvmIsNativeMethod(method)) {
+        /* native code calling native code the hard way */
+        if (!dvmPushJNIFrame(self, method)) {
+            assert(dvmCheckException(self));
+            return NULL;
+        }
+    } else {
+        /* native code calling interpreted code */
+        if (!dvmPushInterpFrame(self, method)) {
+            assert(dvmCheckException(self));
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Issue a method call.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * (Note this can't be inlined because it takes a variable number of args.)
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+    JValue* pResult, ...)
+{
+    va_list args;
+    va_start(args, pResult);
+    dvmCallMethodV(self, method, obj, false, pResult, args);
+    va_end(args);
+}
+
+/*
+ * Issue a method call with a variable number of arguments.  We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * We don't need to take the class as an argument because, in Dalvik,
+ * we don't need to worry about static synchronized methods.
+ */
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, va_list args)
+{
+    const char* desc = &(method->shorty[1]); // [0] is the return type.
+    int verifyCount = 0;
+    ClassObject* clazz;
+    u4* ins;
+
+    clazz = callPrep(self, method, obj, false);
+    if (clazz == NULL)
+        return;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((u4*)self->interpSave.curFrame) +
+           (method->registersSize - method->insSize);
+
+    //ALOGD("  FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+        assert(obj != NULL && dvmIsHeapAddress(obj));
+#endif
+        *ins++ = (u4) obj;
+        verifyCount++;
+    }
+
+    while (*desc != '\0') {
+        switch (*(desc++)) {
+            case 'D': case 'J': {
+                u8 val = va_arg(args, u8);
+                memcpy(ins, &val, 8);       // EABI prevents direct store
+                ins += 2;
+                verifyCount += 2;
+                break;
+            }
+            case 'F': {
+                /* floats were normalized to doubles; convert back */
+                float f = (float) va_arg(args, double);
+                *ins++ = dvmFloatToU4(f);
+                verifyCount++;
+                break;
+            }
+            case 'L': {     /* 'shorty' descr uses L for all refs, incl array */
+                void* arg = va_arg(args, void*);
+                assert(obj == NULL || dvmIsHeapAddress(obj));
+                jobject argObj = reinterpret_cast<jobject>(arg);
+                if (fromJni)
+                    *ins++ = (u4) dvmDecodeIndirectRef(self, argObj);
+                else
+                    *ins++ = (u4) argObj;
+                verifyCount++;
+                break;
+            }
+            default: {
+                /* Z B C S I -- all passed as 32-bit integers */
+                *ins++ = va_arg(args, u4);
+                verifyCount++;
+                break;
+            }
+        }
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    //dvmDumpThreadStack(dvmThreadSelf());
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, pResult);
+    }
+
+#ifndef NDEBUG
+bail:
+#endif
+    dvmPopFrame(self);
+}
+
+/*
+ * Issue a method call with arguments provided in an array.  We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * The values were likely placed into an uninitialized jvalue array using
+ * the field specifiers, which means that sub-32-bit fields (e.g. short,
+ * boolean) may not have 32 or 64 bits of valid data.  This is different
+ * from the varargs invocation where the C compiler does a widening
+ * conversion when calling a function.  As a result, we have to be a
+ * little more precise when pulling stuff out.
+ *
+ * "args" may be NULL if the method has no arguments.
+ */
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, const jvalue* args)
+{
+    const char* desc = &(method->shorty[1]); // [0] is the return type.
+    int verifyCount = 0;
+    ClassObject* clazz;
+    u4* ins;
+
+    clazz = callPrep(self, method, obj, false);
+    if (clazz == NULL)
+        return;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((u4*)self->interpSave.curFrame) +
+        (method->registersSize - method->insSize);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+        assert(obj != NULL);
+        *ins++ = (u4) obj;              /* obj is a "real" ref */
+        verifyCount++;
+    }
+
+    while (*desc != '\0') {
+        switch (*desc++) {
+        case 'D':                       /* 64-bit quantity; have to use */
+        case 'J':                       /*  memcpy() in case of mis-alignment */
+            memcpy(ins, &args->j, 8);
+            ins += 2;
+            verifyCount++;              /* this needs an extra push */
+            break;
+        case 'L':                       /* includes array refs */
+            if (fromJni)
+                *ins++ = (u4) dvmDecodeIndirectRef(self, args->l);
+            else
+                *ins++ = (u4) args->l;
+            break;
+        case 'F':
+        case 'I':
+            *ins++ = args->i;           /* full 32 bits */
+            break;
+        case 'S':
+            *ins++ = args->s;           /* 16 bits, sign-extended */
+            break;
+        case 'C':
+            *ins++ = args->c;           /* 16 bits, unsigned */
+            break;
+        case 'B':
+            *ins++ = args->b;           /* 8 bits, sign-extended */
+            break;
+        case 'Z':
+            *ins++ = args->z;           /* 8 bits, zero or non-zero */
+            break;
+        default:
+            ALOGE("Invalid char %c in short signature of %s.%s",
+                *(desc-1), clazz->descriptor, method->name);
+            assert(false);
+            goto bail;
+        }
+
+        verifyCount++;
+        args++;
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, pResult);
+    }
+
+bail:
+    dvmPopFrame(self);
+}
+
+static void throwArgumentTypeMismatch(int argIndex, ClassObject* expected, DataObject* arg) {
+    std::string expectedClassName(dvmHumanReadableDescriptor(expected->descriptor));
+    std::string actualClassName = dvmHumanReadableType(arg);
+    dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, "argument %d should have type %s, got %s",
+            argIndex + 1, expectedClassName.c_str(), actualClassName.c_str());
+}
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * one of the reflection interfaces.  Could be a virtual or direct method
+ * (including constructors).  Used for reflection.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "invokeObj" will be null for a static method.
+ *
+ * If the invocation returns with an exception raised, we have to wrap it.
+ */
+Object* dvmInvokeMethod(Object* obj, const Method* method,
+    ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+    bool noAccessCheck)
+{
+    ClassObject* clazz;
+    Object* retObj = NULL;
+    Thread* self = dvmThreadSelf();
+    s4* ins;
+    int verifyCount, argListLength;
+    JValue retval;
+    bool needPop = false;
+
+    /* verify arg count */
+    if (argList != NULL)
+        argListLength = argList->length;
+    else
+        argListLength = 0;
+    if (argListLength != (int) params->length) {
+        dvmThrowExceptionFmt(gDvm.exIllegalArgumentException,
+            "wrong number of arguments; expected %d, got %d",
+            params->length, argListLength);
+        return NULL;
+    }
+
+    clazz = callPrep(self, method, obj, !noAccessCheck);
+    if (clazz == NULL)
+        return NULL;
+    needPop = true;
+
+    /* "ins" for new frame start at frame pointer plus locals */
+    ins = ((s4*)self->interpSave.curFrame) +
+        (method->registersSize - method->insSize);
+    verifyCount = 0;
+
+    //ALOGD("  FP is %p, INs live at >= %p", self->interpSave.curFrame, ins);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+        assert(obj != NULL);
+        *ins++ = (s4) obj;
+        verifyCount++;
+    }
+
+    /*
+     * Copy the args onto the stack.  Primitive types are converted when
+     * necessary, and object types are verified.
+     */
+    DataObject** args = (DataObject**)(void*)argList->contents;
+    ClassObject** types = (ClassObject**)(void*)params->contents;
+    for (int i = 0; i < argListLength; i++) {
+        int width = dvmConvertArgument(*args++, *types++, ins);
+        if (width < 0) {
+            dvmPopFrame(self);      // throw wants to pull PC out of stack
+            needPop = false;
+            throwArgumentTypeMismatch(i, *(types-1), *(args-1));
+            goto bail;
+        }
+
+        ins += width;
+        verifyCount += width;
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+#endif
+
+    if (dvmIsNativeMethod(method)) {
+        TRACE_METHOD_ENTER(self, method);
+        /*
+         * Because we leave no space for local variables, "curFrame" points
+         * directly at the method arguments.
+         */
+        (*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval,
+                              method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, &retval);
+    }
+
+    /*
+     * Pop the frame immediately.  The "wrap" calls below can cause
+     * allocations, and we don't want the GC to walk the now-dead frame.
+     */
+    dvmPopFrame(self);
+    needPop = false;
+
+    /*
+     * If an exception is raised, wrap and replace.  This is necessary
+     * because the invoked method could have thrown a checked exception
+     * that the caller wasn't prepared for.
+     *
+     * We might be able to do this up in the interpreted code, but that will
+     * leave us with a shortened stack trace in the top-level exception.
+     */
+    if (dvmCheckException(self)) {
+        dvmWrapException("Ljava/lang/reflect/InvocationTargetException;");
+    } else {
+        /*
+         * If this isn't a void method or constructor, convert the return type
+         * to an appropriate object.
+         *
+         * We don't do this when an exception is raised because the value
+         * in "retval" is undefined.
+         */
+        if (returnType != NULL) {
+            retObj = (Object*)dvmBoxPrimitive(retval, returnType);
+            dvmReleaseTrackedAlloc(retObj, NULL);
+        }
+    }
+
+bail:
+    if (needPop) {
+        dvmPopFrame(self);
+    }
+    return retObj;
+}
+
+struct LineNumFromPcContext {
+    u4 address;
+    u4 lineNum;
+};
+
+static int lineNumForPcCb(void *cnxt, u4 address, u4 lineNum)
+{
+    LineNumFromPcContext *pContext = (LineNumFromPcContext *)cnxt;
+
+    // We know that this callback will be called in
+    // ascending address order, so keep going until we find
+    // a match or we've just gone past it.
+
+    if (address > pContext->address) {
+        // The line number from the previous positions callback
+        // wil be the final result.
+        return 1;
+    }
+
+    pContext->lineNum = lineNum;
+
+    return (address == pContext->address) ? 1 : 0;
+}
+
+/*
+ * Determine the source file line number based on the program counter.
+ * "pc" is an offset, in 16-bit units, from the start of the method's code.
+ *
+ * Returns -1 if no match was found (possibly because the source files were
+ * compiled without "-g", so no line number information is present).
+ * Returns -2 for native methods (as expected in exception traces).
+ */
+int dvmLineNumFromPC(const Method* method, u4 relPc)
+{
+    const DexCode* pDexCode = dvmGetMethodCode(method);
+
+    if (pDexCode == NULL) {
+        if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method))
+            return -2;
+        return -1;      /* can happen for abstract method stub */
+    }
+
+    LineNumFromPcContext context;
+    memset(&context, 0, sizeof(context));
+    context.address = relPc;
+    // A method with no line number info should return -1
+    context.lineNum = -1;
+
+    dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode,
+            method->clazz->descriptor,
+            method->prototype.protoIdx,
+            method->accessFlags,
+            lineNumForPcCb, NULL, &context);
+
+    return context.lineNum;
+}
+
+/*
+ * Compute the frame depth.
+ *
+ * Excludes "break" frames.
+ */
+int dvmComputeExactFrameDepth(const void* fp)
+{
+    int count = 0;
+
+    for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
+        if (!dvmIsBreakFrame((u4*)fp))
+            count++;
+    }
+
+    return count;
+}
+
+/*
+ * Compute the "vague" frame depth, which is just a pointer subtraction.
+ * The result is NOT an overly generous assessment of the number of
+ * frames; the only meaningful use is to compare against the result of
+ * an earlier invocation.
+ *
+ * Useful for implementing single-step debugger modes, which may need to
+ * call this for every instruction.
+ */
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp)
+{
+    const u1* interpStackStart = thread->interpStackStart;
+
+    assert((u1*) fp >= interpStackStart - thread->interpStackSize);
+    assert((u1*) fp < interpStackStart);
+    return interpStackStart - (u1*) fp;
+}
+
+/*
+ * Get the calling frame.  Pass in the current fp.
+ *
+ * Skip "break" frames and reflection invoke frames.
+ */
+void* dvmGetCallerFP(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    StackSaveArea* saveArea;
+
+retry:
+    if (dvmIsBreakFrame((u4*)caller)) {
+        /* pop up one more */
+        caller = SAVEAREA_FROM_FP(caller)->prevFrame;
+        if (caller == NULL)
+            return NULL;        /* hit the top */
+
+        /*
+         * If we got here by java.lang.reflect.Method.invoke(), we don't
+         * want to return Method's class loader.  Shift up one and try
+         * again.
+         */
+        saveArea = SAVEAREA_FROM_FP(caller);
+        if (dvmIsReflectionMethod(saveArea->method)) {
+            caller = saveArea->prevFrame;
+            assert(caller != NULL);
+            goto retry;
+        }
+    }
+
+    return caller;
+}
+
+/*
+ * Get the caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame)
+{
+    void* caller;
+
+    caller = dvmGetCallerFP(curFrame);
+    if (caller == NULL)
+        return NULL;
+
+    return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    void* callerCaller;
+
+    /* at the top? */
+    if (dvmIsBreakFrame((u4*)caller) &&
+        SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+        return NULL;
+
+    /* go one more */
+    callerCaller = dvmGetCallerFP(caller);
+    if (callerCaller == NULL)
+        return NULL;
+
+    return SAVEAREA_FROM_FP(callerCaller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame)
+{
+    void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+    int i;
+
+    /* at the top? */
+    if (dvmIsBreakFrame((u4*)caller) &&
+        SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+        return NULL;
+
+    /* Walk up two frames if possible. */
+    for (i = 0; i < 2; i++) {
+        caller = dvmGetCallerFP(caller);
+        if (caller == NULL)
+            return NULL;
+    }
+
+    return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Fill a flat array of methods that comprise the current interpreter
+ * stack trace.  Pass in the current frame ptr.  Break frames are
+ * skipped, but reflection invocations are not.
+ *
+ * The current frame will be in element 0.
+ */
+void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length)
+{
+    assert(fp != NULL);
+    assert(array != NULL);
+    size_t i = 0;
+    while (fp != NULL) {
+        if (!dvmIsBreakFrame((u4*)fp)) {
+            assert(i < length);
+            array[i++] = SAVEAREA_FROM_FP(fp)->method;
+        }
+        fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+    }
+}
+
+/*
+ * Open up the reserved area and throw an exception.  The reserved area
+ * should only be needed to create and initialize the exception itself.
+ *
+ * If we already opened it and we're continuing to overflow, abort the VM.
+ *
+ * We have to leave the "reserved" area open until the "catch" handler has
+ * finished doing its processing.  This is because the catch handler may
+ * need to resolve classes, which requires calling into the class loader if
+ * the classes aren't already in the "initiating loader" list.
+ */
+void dvmHandleStackOverflow(Thread* self, const Method* method)
+{
+    /*
+     * Can we make the reserved area available?
+     */
+    if (self->stackOverflowed) {
+        /*
+         * Already did, nothing to do but bail.
+         */
+        ALOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting",
+            self->threadId);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    /* open it up to the full range */
+    ALOGI("threadid=%d: stack overflow on call to %s.%s:%s",
+        self->threadId,
+        method->clazz->descriptor, method->name, method->shorty);
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+    ALOGI("  method requires %d+%d+%d=%d bytes, fp is %p (%d left)",
+        method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4,
+        (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea),
+        saveArea, (u1*) saveArea - self->interpStackEnd);
+    ALOGI("  expanding stack end (%p to %p)", self->interpStackEnd,
+        self->interpStackStart - self->interpStackSize);
+    //dvmDumpThread(self, false);
+    self->interpStackEnd = self->interpStackStart - self->interpStackSize;
+    self->stackOverflowed = true;
+
+    /*
+     * If we were trying to throw an exception when the stack overflowed,
+     * we will blow up when doing the class lookup on StackOverflowError
+     * because of the pending exception.  So, we clear it and make it
+     * the cause of the SOE.
+     */
+    Object* excep = dvmGetException(self);
+    if (excep != NULL) {
+        ALOGW("Stack overflow while throwing exception");
+        dvmClearException(self);
+    }
+    dvmThrowChainedException(gDvm.exStackOverflowError, NULL, excep);
+}
+
+/*
+ * Reduce the available stack size.  By this point we should have finished
+ * our overflow processing.
+ */
+void dvmCleanupStackOverflow(Thread* self, const Object* exception)
+{
+    const u1* newStackEnd;
+
+    assert(self->stackOverflowed);
+
+    if (exception->clazz != gDvm.exStackOverflowError) {
+        /* exception caused during SOE, not the SOE itself */
+        return;
+    }
+
+    newStackEnd = (self->interpStackStart - self->interpStackSize)
+        + STACK_OVERFLOW_RESERVE;
+    if ((u1*)self->interpSave.curFrame <= newStackEnd) {
+        ALOGE("Can't shrink stack: curFrame is in reserved area (%p %p)",
+            self->interpStackEnd, self->interpSave.curFrame);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    self->interpStackEnd = newStackEnd;
+    self->stackOverflowed = false;
+
+    ALOGI("Shrank stack (to %p, curFrame is %p)", self->interpStackEnd,
+        self->interpSave.curFrame);
+}
+
+
+/*
+ * Extract the object that is the target of a monitor-enter instruction
+ * in the top stack frame of "thread".
+ *
+ * The other thread might be alive, so this has to work carefully.
+ *
+ * The thread list lock must be held.
+ *
+ * Returns "true" if we successfully recover the object.  "*pOwner" will
+ * be NULL if we can't determine the owner for some reason (e.g. race
+ * condition on ownership transfer).
+ */
+static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj,
+    Thread** pOwner)
+{
+    void* framePtr = thread->interpSave.curFrame;
+
+    if (framePtr == NULL || dvmIsBreakFrame((u4*)framePtr))
+        return false;
+
+    const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+    const Method* method = saveArea->method;
+    const u2* currentPc = saveArea->xtra.currentPc;
+
+    /* check Method* */
+    if (!dvmLinearAllocContains(method, sizeof(Method))) {
+        ALOGD("ExtrMon: method %p not valid", method);
+        return false;
+    }
+
+    /* check currentPc */
+    u4 insnsSize = dvmGetMethodInsnsSize(method);
+    if (currentPc < method->insns ||
+        currentPc >= method->insns + insnsSize)
+    {
+        ALOGD("ExtrMon: insns %p not valid (%p - %p)",
+            currentPc, method->insns, method->insns + insnsSize);
+        return false;
+    }
+
+    /* check the instruction */
+    if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {
+        ALOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)",
+            currentPc, *currentPc & 0xff);
+        return false;
+    }
+
+    /* get and check the register index */
+    unsigned int reg = *currentPc >> 8;
+    if (reg >= method->registersSize) {
+        ALOGD("ExtrMon: invalid register %d (max %d)",
+            reg, method->registersSize);
+        return false;
+    }
+
+    /* get and check the object in that register */
+    u4* fp = (u4*) framePtr;
+    Object* obj = (Object*) fp[reg];
+    if (obj != NULL && !dvmIsHeapAddress(obj)) {
+        ALOGD("ExtrMon: invalid object %p at %p[%d]", obj, fp, reg);
+        return false;
+    }
+    *pLockObj = obj;
+
+    /*
+     * Try to determine the object's lock holder; it's okay if this fails.
+     *
+     * We're assuming the thread list lock is already held by this thread.
+     * If it's not, we may be living dangerously if we have to scan through
+     * the thread list to find a match.  (The VM will generally be in a
+     * suspended state when executing here, so this is a minor concern
+     * unless we're dumping while threads are running, in which case there's
+     * a good chance of stuff blowing up anyway.)
+     */
+    *pOwner = dvmGetObjectLockHolder(obj);
+
+    return true;
+}
+
+static void printWaitMessage(const DebugOutputTarget* target, const char* detail, Object* obj,
+        Thread* thread)
+{
+    std::string msg(StringPrintf("  - waiting %s <%p> ", detail, obj));
+
+    if (obj->clazz != gDvm.classJavaLangClass) {
+        // I(16573)   - waiting on <0xf5feda38> (a java.util.LinkedList)
+        // I(16573)   - waiting on <0xf5ed54f8> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+        msg += "(a " + dvmHumanReadableType(obj) + ")";
+    }
+
+    if (thread != NULL) {
+        std::string threadName(dvmGetThreadName(thread));
+        StringAppendF(&msg, " held by tid=%d (%s)", thread->threadId, threadName.c_str());
+    }
+
+    dvmPrintDebugMessage(target, "%s\n", msg.c_str());
+}
+
+/*
+ * Dump stack frames, starting from the specified frame and moving down.
+ *
+ * Each frame holds a pointer to the currently executing method, and the
+ * saved program counter from the caller ("previous" frame).  This means
+ * we don't have the PC for the current method on the stack, which is
+ * pretty reasonable since it's in the "PC register" for the VM.  Because
+ * exceptions need to show the correct line number we actually *do* have
+ * an updated version in the fame's "xtra.currentPc", but it's unreliable.
+ *
+ * Note "framePtr" could be NULL in rare circumstances.
+ */
+static void dumpFrames(const DebugOutputTarget* target, void* framePtr,
+    Thread* thread)
+{
+    const StackSaveArea* saveArea;
+    const Method* method;
+    int checkCount = 0;
+    const u2* currentPc = NULL;
+    bool first = true;
+
+    /*
+     * We call functions that require us to be holding the thread list lock.
+     * It's probable that the caller has already done so, but it's not
+     * guaranteed.  If it's not locked, lock it now.
+     */
+    bool needThreadUnlock = dvmTryLockThreadList();
+
+    /*
+     * The "currentPc" is updated whenever we execute an instruction that
+     * might throw an exception.  Show it here.
+     */
+    if (framePtr != NULL && !dvmIsBreakFrame((u4*)framePtr)) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+
+        if (saveArea->xtra.currentPc != NULL)
+            currentPc = saveArea->xtra.currentPc;
+    }
+
+    while (framePtr != NULL) {
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = saveArea->method;
+
+        if (dvmIsBreakFrame((u4*)framePtr)) {
+            //dvmPrintDebugMessage(target, "  (break frame)\n");
+        } else {
+            int relPc;
+
+            if (currentPc != NULL)
+                relPc = currentPc - saveArea->method->insns;
+            else
+                relPc = -1;
+
+            std::string methodName(dvmHumanReadableMethod(method, false));
+            if (dvmIsNativeMethod(method)) {
+                dvmPrintDebugMessage(target, "  at %s(Native Method)\n",
+                        methodName.c_str());
+            } else {
+                dvmPrintDebugMessage(target, "  at %s(%s:%s%d)\n",
+                        methodName.c_str(), dvmGetMethodSourceFile(method),
+                        (relPc >= 0 && first) ? "~" : "",
+                        relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
+            }
+
+            if (first) {
+                /*
+                 * Decorate WAIT and MONITOR threads with some detail on
+                 * the first frame.
+                 *
+                 * warning: wait status not stable, even in suspend
+                 */
+                if (thread->status == THREAD_WAIT ||
+                    thread->status == THREAD_TIMED_WAIT)
+                {
+                    Monitor* mon = thread->waitMonitor;
+                    Object* obj = dvmGetMonitorObject(mon);
+                    if (obj != NULL) {
+                        Thread* joinThread = NULL;
+                        if (obj->clazz == gDvm.classJavaLangVMThread) {
+                            joinThread = dvmGetThreadFromThreadObject(obj);
+                        }
+                        if (joinThread == NULL) {
+                            joinThread = dvmGetObjectLockHolder(obj);
+                        }
+                        printWaitMessage(target, "on", obj, joinThread);
+                    }
+                } else if (thread->status == THREAD_MONITOR) {
+                    Object* obj;
+                    Thread* owner;
+                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
+                        printWaitMessage(target, "to lock", obj, owner);
+                    }
+                }
+            }
+        }
+
+        /*
+         * Get saved PC for previous frame.  There's no savedPc in a "break"
+         * frame, because that represents native or interpreted code
+         * invoked by the VM.  The saved PC is sitting in the "PC register",
+         * a local variable on the native stack.
+         */
+        currentPc = saveArea->savedPc;
+
+        first = false;
+
+        if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {
+            ALOGW("Warning: loop in stack trace at frame %d (%p -> %p)",
+                checkCount, framePtr, saveArea->prevFrame);
+            break;
+        }
+        framePtr = saveArea->prevFrame;
+
+        checkCount++;
+        if (checkCount > 300) {
+            dvmPrintDebugMessage(target,
+                "  ***** printed %d frames, not showing any more\n",
+                checkCount);
+            break;
+        }
+    }
+
+    if (needThreadUnlock) {
+        dvmUnlockThreadList();
+    }
+}
+
+
+/*
+ * Dump the stack for the specified thread.
+ */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+    dumpFrames(target, thread->interpSave.curFrame, thread);
+}
+
+/*
+ * Dump the stack for the specified thread, which is still running.
+ *
+ * This is very dangerous, because stack frames are being pushed on and
+ * popped off, and if the thread exits we'll be looking at freed memory.
+ * The plan here is to take a snapshot of the stack and then dump that
+ * to try to minimize the chances of catching it mid-update.  This should
+ * work reasonably well on a single-CPU system.
+ *
+ * There is a small chance that calling here will crash the VM.
+ */
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+    StackSaveArea* saveArea;
+    const u1* origStack;
+    u1* stackCopy = NULL;
+    int origSize, fpOffset;
+    void* fp;
+    int depthLimit = 200;
+
+    if (thread == NULL || thread->interpSave.curFrame == NULL) {
+        dvmPrintDebugMessage(target,
+            "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n",
+            thread, (thread != NULL) ? thread->threadId : 0);
+        return;
+    }
+
+    /* wait for a full quantum */
+    sched_yield();
+
+    /* copy the info we need, then the stack itself */
+    origSize = thread->interpStackSize;
+    origStack = (const u1*) thread->interpStackStart - origSize;
+    stackCopy = (u1*) malloc(origSize);
+    fpOffset = (u1*) thread->interpSave.curFrame - origStack;
+    memcpy(stackCopy, origStack, origSize);
+
+    /*
+     * Run through the stack and rewrite the "prev" pointers.
+     */
+    //ALOGI("DR: fpOff=%d (from %p %p)",fpOffset, origStack,
+    //     thread->interpSave.curFrame);
+    fp = stackCopy + fpOffset;
+    while (true) {
+        int prevOffset;
+
+        if (depthLimit-- < 0) {
+            /* we're probably screwed */
+            dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n");
+            dvmAbort();
+        }
+        saveArea = SAVEAREA_FROM_FP(fp);
+        if (saveArea->prevFrame == NULL)
+            break;
+
+        prevOffset = (u1*) saveArea->prevFrame - origStack;
+        if (prevOffset < 0 || prevOffset > origSize) {
+            dvmPrintDebugMessage(target,
+                "DumpRunning: bad offset found: %d (from %p %p)\n",
+                prevOffset, origStack, saveArea->prevFrame);
+            saveArea->prevFrame = NULL;
+            break;
+        }
+
+        saveArea->prevFrame = (u4*)(stackCopy + prevOffset);
+        fp = saveArea->prevFrame;
+    }
+
+    /*
+     * We still need to pass the Thread for some monitor wait stuff.
+     */
+    dumpFrames(target, stackCopy + fpOffset, thread);
+    free(stackCopy);
+}
+
+/*
+ * Dump the native stack for the specified thread.
+ */
+void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid)
+{
+#ifdef HAVE_ANDROID_OS
+    UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+    if (!backtrace->Unwind(0) || backtrace->NumFrames() == 0) {
+        dvmPrintDebugMessage(target, "  (native backtrace unavailable)\n");
+    } else {
+        for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+            dvmPrintDebugMessage(target, "  %s\n",
+                                 backtrace->FormatFrameData(i).c_str());
+        }
+    }
+#endif
+}
diff --git a/vm/interp/Stack.h b/vm/interp/Stack.h
new file mode 100644
index 0000000..06e7ec5
--- /dev/null
+++ b/vm/interp/Stack.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Stack frames, and uses thereof.
+ */
+#ifndef DALVIK_INTERP_STACK_H_
+#define DALVIK_INTERP_STACK_H_
+
+#include "jni.h"
+#include <stdarg.h>
+
+/*
+Stack layout
+
+In what follows, the "top" of the stack is at a low position in memory,
+and the "bottom" of the stack is in a high position (put more simply,
+they grow downward).  They may be merged with the native stack at a
+later date.  The interpreter assumes that they have a fixed size,
+determined when the thread is created.
+
+Dalvik's registers (of which there can be up to 64K) map to the "ins"
+(method arguments) and "locals" (local variables).  The "outs" (arguments
+to called methods) are specified by the "invoke" operand.  The return
+value, which is passed through the interpreter rather than on the stack,
+is retrieved with a "move-result" instruction.
+
+    Low addresses (0x00000000)
+
+                     +- - - - - - - - -+
+                     -  out0           -
+                     +-----------------+  <-- stack ptr (top of stack)
+                     +  VM-specific    +
+                     +  internal goop  +
+                     +-----------------+  <-- curFrame: FP for cur function
+                     +  v0 == local0   +
++-----------------+  +-----------------+
++  out0           +  +  v1 == in0      +
++-----------------+  +-----------------+
++  out1           +  +  v2 == in1      +
++-----------------+  +-----------------+
++  VM-specific    +
++  internal goop  +
++-----------------+  <-- frame ptr (FP) for previous function
++  v0 == local0   +
++-----------------+
++  v1 == local1   +
++-----------------+
++  v2 == in0      +
++-----------------+
++  v3 == in1      +
++-----------------+
++  v4 == in2      +
++-----------------+
+-                 -
+-                 -
+-                 -
++-----------------+  <-- interpStackStart
+
+    High addresses (0xffffffff)
+
+Note the "ins" and "outs" overlap -- values pushed into the "outs" area
+become the parameters to the called method.  The VM guarantees that there
+will be enough room for all possible "outs" on the stack before calling
+into a method.
+
+All "V registers" are 32 bits, and all stack entries are 32-bit aligned.
+Registers are accessed as a positive offset from the frame pointer,
+e.g. register v2 is fp[2].  64-bit quantities are stored in two adjacent
+registers, addressed by the lower-numbered register, and are in host order.
+64-bit quantities do not need to start in an even-numbered register.
+
+We push two stack frames on when calling an interpreted or native method
+directly from the VM (e.g. invoking <clinit> or via reflection "invoke()").
+The first is a "break" frame, which allows us to tell when a call return or
+exception unroll has reached the VM call site.  Without the break frame the
+stack might look like an uninterrupted series of interpreted method calls.
+The second frame is for the method itself.
+
+The "break" frame is used as an alternative to adding additional fields
+to the StackSaveArea struct itself.  They are recognized by having a
+NULL method pointer.
+
+When calling a native method from interpreted code, the stack setup is
+essentially identical to calling an interpreted method.  Because it's a
+native method, though, there are never any "locals" or "outs".
+
+For native calls into JNI, we want to store a table of local references
+on the stack.  The GC needs to scan them while the native code is running,
+and we want to trivially discard them when the method returns.  See JNI.c
+for a discussion of how this is managed.  In particular note that it is
+possible to push additional call frames on without calling a method.
+*/
+
+
+struct StackSaveArea;
+
+//#define PAD_SAVE_AREA       /* help debug stack trampling */
+
+/*
+ * The VM-specific internal goop.
+ *
+ * The idea is to mimic a typical native stack frame, with copies of the
+ * saved PC and FP.  At some point we'd like to have interpreted and
+ * native code share the same stack, though this makes portability harder.
+ */
+struct StackSaveArea {
+#ifdef PAD_SAVE_AREA
+    u4          pad0, pad1, pad2;
+#endif
+
+#ifdef EASY_GDB
+    /* make it easier to trek through stack frames in GDB */
+    StackSaveArea* prevSave;
+#endif
+
+    /* saved frame pointer for previous frame, or NULL if this is at bottom */
+    u4*         prevFrame;
+
+    /* saved program counter (from method in caller's frame) */
+    const u2*   savedPc;
+
+    /* pointer to method we're *currently* executing; handy for exceptions */
+    const Method* method;
+
+    union {
+        /* for JNI native methods: bottom of local reference segment */
+        u4          localRefCookie;
+
+        /* for interpreted methods: saved current PC, for exception stack
+         * traces and debugger traces */
+        const u2*   currentPc;
+    } xtra;
+
+    /* Native return pointer for JIT, or 0 if interpreted */
+    const u2* returnAddr;
+#ifdef PAD_SAVE_AREA
+    u4          pad3, pad4, pad5;
+#endif
+};
+
+/* move between the stack save area and the frame pointer */
+#define SAVEAREA_FROM_FP(_fp)   ((StackSaveArea*)(_fp) -1)
+#define FP_FROM_SAVEAREA(_save) ((u4*) ((StackSaveArea*)(_save) +1))
+
+/* when calling a function, get a pointer to outs[0] */
+#define OUTS_FROM_FP(_fp, _argCount) \
+    ((u4*) ((u1*)SAVEAREA_FROM_FP(_fp) - sizeof(u4) * (_argCount)))
+
+/* reserve this many bytes for handling StackOverflowError */
+#define STACK_OVERFLOW_RESERVE  768
+
+/*
+ * Determine if the frame pointer points to a "break frame".
+ */
+INLINE bool dvmIsBreakFrame(const u4* fp)
+{
+    return SAVEAREA_FROM_FP(fp)->method == NULL;
+}
+
+/*
+ * Initialize the interp stack (call this after allocating storage and
+ * setting thread->interpStackStart).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize);
+
+/*
+ * Push a native method frame directly onto the stack.  Used to push the
+ * "fake" native frames at the top of each thread stack.
+ */
+bool dvmPushJNIFrame(Thread* thread, const Method* method);
+
+/*
+ * JNI local frame management.
+ */
+bool dvmPushLocalFrame(Thread* thread, const Method* method);
+bool dvmPopLocalFrame(Thread* thread);
+
+/*
+ * Call an interpreted method from native code.  If this is being called
+ * from a JNI function, references in the argument list will be converted
+ * back to pointers.
+ *
+ * "obj" should be NULL for "direct" methods.
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+    JValue* pResult, ...);
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, va_list args);
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+    bool fromJni, JValue* pResult, const jvalue* args);
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * a reflection interface.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "obj" should be null for a static method.
+ *
+ * "params" and "returnType" come from the Method object, so we don't have
+ * to re-generate them from the method signature.  "returnType" should be
+ * NULL if we're invoking a constructor.
+ */
+Object* dvmInvokeMethod(Object* invokeObj, const Method* meth,
+    ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+    bool noAccessCheck);
+
+/*
+ * Determine the source file line number, given the program counter offset
+ * into the specified method.  Returns -2 for native methods, -1 if no
+ * match was found.
+ */
+extern "C" int dvmLineNumFromPC(const Method* method, u4 relPc);
+
+/*
+ * Given a frame pointer, compute the current call depth.  The value can be
+ * "exact" (a count of non-break frames) or "vague" (just subtracting
+ * pointers to give relative values).
+ */
+int dvmComputeExactFrameDepth(const void* fp);
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp);
+
+/*
+ * Get the frame pointer for the caller's stack frame.
+ */
+void* dvmGetCallerFP(const void* curFrame);
+
+/*
+ * Get the class of the method that called us.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame);
+
+/*
+ * Get the caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame);
+
+/*
+ * Get the caller's caller's caller's class.  Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame);
+
+/*
+ * Fill an array of method pointers representing the current stack
+ * trace (element 0 is current frame).
+ */
+void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length);
+
+/*
+ * Common handling for stack overflow.
+ */
+extern "C" void dvmHandleStackOverflow(Thread* self, const Method* method);
+extern "C" void dvmCleanupStackOverflow(Thread* self, const Object* exception);
+
+/* debugging; dvmDumpThread() is probably a better starting point */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread);
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread);
+void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid);
+
+#endif  // DALVIK_INTERP_STACK_H_
diff --git a/vm/jdwp/ExpandBuf.cpp b/vm/jdwp/ExpandBuf.cpp
new file mode 100644
index 0000000..cc3557e
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Implementation of an expandable byte buffer.  Designed for serializing
+ * primitive values, e.g. JDWP replies.
+ */
+#include "jdwp/ExpandBuf.h"
+#include "Bits.h"
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Data structure used to track buffer use.
+ */
+struct ExpandBuf {
+    u1*     storage;
+    int     curLen;
+    int     maxLen;
+};
+
+#define kInitialStorage 64
+
+/*
+ * Allocate a JdwpBuf and some initial storage.
+ */
+ExpandBuf* expandBufAlloc()
+{
+    ExpandBuf* newBuf;
+
+    newBuf = (ExpandBuf*) malloc(sizeof(*newBuf));
+    newBuf->storage = (u1*) malloc(kInitialStorage);
+    newBuf->curLen = 0;
+    newBuf->maxLen = kInitialStorage;
+
+    return newBuf;
+}
+
+/*
+ * Free a JdwpBuf and associated storage.
+ */
+void expandBufFree(ExpandBuf* pBuf)
+{
+    if (pBuf == NULL)
+        return;
+
+    free(pBuf->storage);
+    free(pBuf);
+}
+
+/*
+ * Get a pointer to the start of the buffer.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf)
+{
+    return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+size_t expandBufGetLength(ExpandBuf* pBuf)
+{
+    return pBuf->curLen;
+}
+
+
+/*
+ * Ensure that the buffer has enough space to hold incoming data.  If it
+ * doesn't, resize the buffer.
+ */
+static void ensureSpace(ExpandBuf* pBuf, int newCount)
+{
+    u1* newPtr;
+
+    if (pBuf->curLen + newCount <= pBuf->maxLen)
+        return;
+
+    while (pBuf->curLen + newCount > pBuf->maxLen)
+        pBuf->maxLen *= 2;
+
+    newPtr = (u1*) realloc(pBuf->storage, pBuf->maxLen);
+    if (newPtr == NULL) {
+        ALOGE("realloc(%d) failed", pBuf->maxLen);
+        abort();
+    }
+
+    pBuf->storage = newPtr;
+}
+
+/*
+ * Allocate some space in the buffer.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize)
+{
+    u1* gapStart;
+
+    ensureSpace(pBuf, gapSize);
+    gapStart = pBuf->storage + pBuf->curLen;
+    /* do we want to garbage-fill the gap for debugging? */
+    pBuf->curLen += gapSize;
+
+    return gapStart;
+}
+
+/*
+ * Append a byte.
+ */
+void expandBufAdd1(ExpandBuf* pBuf, u1 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    *(pBuf->storage + pBuf->curLen) = val;
+    pBuf->curLen++;
+}
+
+/*
+ * Append two big-endian bytes.
+ */
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set2BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append four big-endian bytes.
+ */
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set4BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append eight big-endian bytes.
+ */
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val)
+{
+    ensureSpace(pBuf, sizeof(val));
+    set8BE(pBuf->storage + pBuf->curLen, val);
+    pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Add a UTF8 string as a 4-byte length followed by a non-NULL-terminated
+ * string.
+ *
+ * Because these strings are coming out of the VM, it's safe to assume that
+ * they can be null-terminated (either they don't have null bytes or they
+ * have stored null bytes in a multi-byte encoding).
+ */
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str)
+{
+    int strLen = strlen((const char*)str);
+
+    ensureSpace(pBuf, sizeof(u4) + strLen);
+    setUtf8String(pBuf->storage + pBuf->curLen, str);
+    pBuf->curLen += sizeof(u4) + strLen;
+}
diff --git a/vm/jdwp/ExpandBuf.h b/vm/jdwp/ExpandBuf.h
new file mode 100644
index 0000000..0bbc0c6
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Expanding byte buffer, with primitives for appending basic data types.
+ */
+#ifndef DALVIK_JDWP_EXPANDBUF_H_
+#define DALVIK_JDWP_EXPANDBUF_H_
+
+#include "Common.h"     // need u1/u2/u4/u8 types
+
+struct ExpandBuf;   /* private */
+
+/* create a new struct */
+ExpandBuf* expandBufAlloc(void);
+/* free storage */
+void expandBufFree(ExpandBuf* pBuf);
+
+/*
+ * Accessors.  The buffer pointer and length will only be valid until more
+ * data is added.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf);
+size_t expandBufGetLength(ExpandBuf* pBuf);
+
+/*
+ * The "add" operations allocate additional storage and append the data.
+ *
+ * There are no "get" operations included with this "class", other than
+ * GetBuffer().  If you want to get or set data from a position other
+ * than the end, get a pointer to the buffer and use the inline functions
+ * defined elsewhere.
+ *
+ * expandBufAddSpace() returns a pointer to the *start* of the region
+ * added.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize);
+void expandBufAdd1(ExpandBuf* pBuf, u1 val);
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val);
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val);
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str);
+
+#endif  // DALVIK_JDWP_EXPANDBUF_H_
diff --git a/vm/jdwp/Jdwp.h b/vm/jdwp/Jdwp.h
new file mode 100644
index 0000000..467cecd
--- /dev/null
+++ b/vm/jdwp/Jdwp.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * JDWP "public" interface.  The main body of the VM should only use JDWP
+ * structures and functions declared here.
+ *
+ * The JDWP code follows the DalvikVM rules for naming conventions, but
+ * attempts to remain independent of VM innards (e.g. it doesn't access VM
+ * data structures directly).  All calls go through Debugger.c.
+ */
+#ifndef DALVIK_JDWP_JDWP_H_
+#define DALVIK_JDWP_JDWP_H_
+
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+#include "Common.h"
+#include "Bits.h"
+#include <pthread.h>
+
+struct JdwpState;       /* opaque */
+
+/*
+ * Fundamental types.
+ *
+ * ObjectId and RefTypeId must be the same size.
+ */
+typedef u4 FieldId;     /* static or instance field */
+typedef u4 MethodId;    /* any kind of method, including constructors */
+typedef u8 ObjectId;    /* any object (threadID, stringID, arrayID, etc) */
+typedef u8 RefTypeId;   /* like ObjectID, but unique for Class objects */
+typedef u8 FrameId;     /* short-lived stack frame ID */
+
+/*
+ * Match these with the type sizes.  This way we don't have to pass
+ * a value and a length.
+ */
+INLINE FieldId dvmReadFieldId(const u1** pBuf)      { return read4BE(pBuf); }
+INLINE MethodId dvmReadMethodId(const u1** pBuf)    { return read4BE(pBuf); }
+INLINE ObjectId dvmReadObjectId(const u1** pBuf)    { return read8BE(pBuf); }
+INLINE RefTypeId dvmReadRefTypeId(const u1** pBuf)  { return read8BE(pBuf); }
+INLINE FrameId dvmReadFrameId(const u1** pBuf)      { return read8BE(pBuf); }
+INLINE void dvmSetFieldId(u1* buf, FieldId val)     { return set4BE(buf, val); }
+INLINE void dvmSetMethodId(u1* buf, MethodId val)   { return set4BE(buf, val); }
+INLINE void dvmSetObjectId(u1* buf, ObjectId val)   { return set8BE(buf, val); }
+INLINE void dvmSetRefTypeId(u1* buf, RefTypeId val) { return set8BE(buf, val); }
+INLINE void dvmSetFrameId(u1* buf, FrameId val)     { return set8BE(buf, val); }
+INLINE void expandBufAddFieldId(ExpandBuf* pReply, FieldId id) {
+    expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddMethodId(ExpandBuf* pReply, MethodId id) {
+    expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddObjectId(ExpandBuf* pReply, ObjectId id) {
+    expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddRefTypeId(ExpandBuf* pReply, RefTypeId id) {
+    expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddFrameId(ExpandBuf* pReply, FrameId id) {
+    expandBufAdd8BE(pReply, id);
+}
+
+
+/*
+ * Holds a JDWP "location".
+ */
+struct JdwpLocation {
+    u1          typeTag;        /* class or interface? */
+    RefTypeId   classId;        /* method->clazz */
+    MethodId    methodId;       /* method in which "idx" resides */
+    u8          idx;            /* relative index into code block */
+};
+
+/*
+ * How we talk to the debugger.
+ */
+enum JdwpTransportType {
+    kJdwpTransportUnknown = 0,
+    kJdwpTransportSocket,       /* transport=dt_socket */
+    kJdwpTransportAndroidAdb,   /* transport=dt_android_adb */
+};
+
+/*
+ * Holds collection of JDWP initialization parameters.
+ */
+struct JdwpStartupParams {
+    JdwpTransportType transport;
+    bool        server;
+    bool        suspend;
+    char        host[64];
+    short       port;
+    /* more will be here someday */
+};
+
+/*
+ * Perform one-time initialization.
+ *
+ * Among other things, this binds to a port to listen for a connection from
+ * the debugger.
+ *
+ * Returns a newly-allocated JdwpState struct on success, or NULL on failure.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* params);
+
+/*
+ * Shut everything down.
+ */
+void dvmJdwpShutdown(JdwpState* state);
+
+/*
+ * Returns "true" if a debugger or DDM is connected.
+ */
+bool dvmJdwpIsActive(JdwpState* state);
+
+/*
+ * Return the debugger thread's handle, or 0 if the debugger thread isn't
+ * running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state);
+
+/*
+ * Get time, in milliseconds, since the last debugger activity.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state);
+
+/*
+ * When we hit a debugger event that requires suspension, it's important
+ * that we wait for the thread to suspend itself before processing any
+ * additional requests.  (Otherwise, if the debugger immediately sends a
+ * "resume thread" command, the resume might arrive before the thread has
+ * suspended itself.)
+ *
+ * The thread should call the "set" function before sending the event to
+ * the debugger.  The main JDWP handler loop calls "get" before processing
+ * an event, and will wait for thread suspension if it's set.  Once the
+ * thread has suspended itself, the JDWP handler calls "clear" and
+ * continues processing the current event.  This works in the suspend-all
+ * case because the event thread doesn't suspend itself until everything
+ * else has suspended.
+ *
+ * It's possible that multiple threads could encounter thread-suspending
+ * events at the same time, so we grab a mutex in the "set" call, and
+ * release it in the "clear" call.
+ */
+//ObjectId dvmJdwpGetWaitForEventThread(JdwpState* state);
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId);
+void dvmJdwpClearWaitForEventThread(JdwpState* state);
+
+/*
+ * Network functions.
+ */
+bool dvmJdwpCheckConnection(JdwpState* state);
+bool dvmJdwpAcceptConnection(JdwpState* state);
+bool dvmJdwpEstablishConnection(JdwpState* state);
+void dvmJdwpCloseConnection(JdwpState* state);
+bool dvmJdwpProcessIncoming(JdwpState* state);
+
+
+/*
+ * These notify the debug code that something interesting has happened.  This
+ * could be a thread starting or ending, an exception, or an opportunity
+ * for a breakpoint.  These calls do not mean that an event the debugger
+ * is interested has happened, just that something has happened that the
+ * debugger *might* be interested in.
+ *
+ * The item of interest may trigger multiple events, some or all of which
+ * are grouped together in a single response.
+ *
+ * The event may cause the current thread or all threads (except the
+ * JDWP support thread) to be suspended.
+ */
+
+/*
+ * The VM has finished initializing.  Only called when the debugger is
+ * connected at the time initialization completes.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend);
+
+/*
+ * A location of interest has been reached.  This is used for breakpoints,
+ * single-stepping, and method entry/exit.  (JDWP requires that these four
+ * events are grouped together in a single response.)
+ *
+ * In some cases "*pLoc" will just have a method and class name, e.g. when
+ * issuing a MethodEntry on a native method.
+ *
+ * "eventFlags" indicates the types of events that have occurred.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+    ObjectId thisPtr, int eventFlags);
+
+/*
+ * An exception has been thrown.
+ *
+ * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+    ObjectId excepId, RefTypeId excepClassId, const JdwpLocation* pCatchLoc,
+    ObjectId thisPtr);
+
+/*
+ * A thread has started or stopped.
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start);
+
+/*
+ * Class has been prepared.
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+    const char* signature, int status);
+
+/*
+ * The VM is about to stop.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state);
+
+/*
+ * Send up a chunk of DDM data.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+    int iovcnt);
+
+#endif  // DALVIK_JDWP_JDWP_H_
diff --git a/vm/jdwp/JdwpAdb.cpp b/vm/jdwp/JdwpAdb.cpp
new file mode 100644
index 0000000..8fb5391
--- /dev/null
+++ b/vm/jdwp/JdwpAdb.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+/*
+ * The JDWP <-> ADB transport protocol is explained in detail
+ * in system/core/adb/jdwp_service.c. Here's a summary.
+ *
+ * 1/ when the JDWP thread starts, it tries to connect to a Unix
+ *    domain stream socket (@jdwp-control) that is opened by the
+ *    ADB daemon.
+ *
+ * 2/ it then sends the current process PID as a string of 4 hexadecimal
+ *    chars (no terminating zero)
+ *
+ * 3/ then, it uses recvmsg to receive file descriptors from the
+ *    daemon. each incoming file descriptor is a pass-through to
+ *    a given JDWP debugger, that can be used to read the usual
+ *    JDWP-handshake, etc...
+ */
+
+#define kInputBufferSize    8192
+
+#define kMagicHandshake     "JDWP-Handshake"
+#define kMagicHandshakeLen  (sizeof(kMagicHandshake)-1)
+
+#define kJdwpControlName    "\0jdwp-control"
+#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
+
+struct JdwpNetState : public JdwpNetStateBase {
+    int                 controlSock;
+    bool                awaitingHandshake;
+    bool                shuttingDown;
+    int                 wakeFds[2];
+
+    int                 inputCount;
+    unsigned char       inputBuffer[kInputBufferSize];
+
+    socklen_t           controlAddrLen;
+    union {
+        struct sockaddr_un  controlAddrUn;
+        struct sockaddr     controlAddrPlain;
+    } controlAddr;
+
+    JdwpNetState()
+    {
+        controlSock = -1;
+        awaitingHandshake = false;
+        shuttingDown = false;
+        wakeFds[0] = -1;
+        wakeFds[1] = -1;
+
+        inputCount = 0;
+
+        controlAddr.controlAddrUn.sun_family = AF_UNIX;
+        controlAddrLen = sizeof(controlAddr.controlAddrUn.sun_family) +
+                kJdwpControlNameLen;
+        memcpy(controlAddr.controlAddrUn.sun_path, kJdwpControlName,
+                kJdwpControlNameLen);
+    }
+};
+
+static void
+adbStateFree( JdwpNetState*  netState )
+{
+    if (netState == NULL)
+        return;
+
+    if (netState->clientSock >= 0) {
+        shutdown(netState->clientSock, SHUT_RDWR);
+        close(netState->clientSock);
+    }
+    if (netState->controlSock >= 0) {
+        shutdown(netState->controlSock, SHUT_RDWR);
+        close(netState->controlSock);
+    }
+    if (netState->wakeFds[0] >= 0) {
+        close(netState->wakeFds[0]);
+        netState->wakeFds[0] = -1;
+    }
+    if (netState->wakeFds[1] >= 0) {
+        close(netState->wakeFds[1]);
+        netState->wakeFds[1] = -1;
+    }
+
+    delete netState;
+}
+
+/*
+ * Do initial prep work, e.g. binding to ports and opening files.  This
+ * runs in the main thread, before the JDWP thread starts, so it shouldn't
+ * do anything that might block forever.
+ */
+static bool startup(struct JdwpState* state, const JdwpStartupParams* pParams)
+{
+    JdwpNetState*  netState;
+
+    ALOGV("ADB transport startup");
+
+    state->netState = netState = new JdwpNetState;
+    if (netState == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Receive a file descriptor from ADB.  The fd can be used to communicate
+ * directly with a debugger or DDMS.
+ *
+ * Returns the file descriptor on success.  On failure, returns -1 and
+ * closes netState->controlSock.
+ */
+static int  receiveClientFd(JdwpNetState*  netState)
+{
+    struct msghdr    msg;
+    struct cmsghdr*  cmsg;
+    struct iovec     iov;
+    char             dummy = '!';
+    union {
+        struct cmsghdr cm;
+        char buffer[CMSG_SPACE(sizeof(int))];
+    } cm_un;
+    int              ret;
+
+    iov.iov_base       = &dummy;
+    iov.iov_len        = 1;
+    msg.msg_name       = NULL;
+    msg.msg_namelen    = 0;
+    msg.msg_iov        = &iov;
+    msg.msg_iovlen     = 1;
+    msg.msg_flags      = 0;
+    msg.msg_control    = cm_un.buffer;
+    msg.msg_controllen = sizeof(cm_un.buffer);
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_len   = msg.msg_controllen;
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type  = SCM_RIGHTS;
+    ((int*)(void*)CMSG_DATA(cmsg))[0] = -1;
+
+    do {
+        ret = recvmsg(netState->controlSock, &msg, 0);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret <= 0) {
+        if (ret < 0) {
+            ALOGW("receiving file descriptor from ADB failed (socket %d): %s",
+                 netState->controlSock, strerror(errno));
+        }
+        close(netState->controlSock);
+        netState->controlSock = -1;
+        return -1;
+    }
+
+    return ((int*)(void*)CMSG_DATA(cmsg))[0];
+}
+
+/*
+ * Block forever, waiting for a debugger to connect to us.  Called from the
+ * JDWP thread.
+ *
+ * This needs to un-block and return "false" if the VM is shutting down.  It
+ * should return "true" when it successfully accepts a connection.
+ */
+static bool acceptConnection(struct JdwpState* state)
+{
+    JdwpNetState*  netState = state->netState;
+    int retryCount = 0;
+
+    /* first, ensure that we get a connection to the ADB daemon */
+
+retry:
+    if (netState->shuttingDown)
+        return false;
+
+    if (netState->controlSock < 0) {
+        int        sleep_ms     = 500;
+        const int  sleep_max_ms = 2*1000;
+        char       buff[5];
+
+        netState->controlSock = socket(PF_UNIX, SOCK_STREAM, 0);
+        if (netState->controlSock < 0) {
+            ALOGE("Could not create ADB control socket:%s",
+                 strerror(errno));
+            return false;
+        }
+
+        if (pipe(netState->wakeFds) < 0) {
+            ALOGE("pipe failed");
+            return false;
+        }
+
+        snprintf(buff, sizeof(buff), "%04x", getpid());
+        buff[4] = 0;
+
+        for (;;) {
+            /*
+             * If adbd isn't running, because USB debugging was disabled or
+             * perhaps the system is restarting it for "adb root", the
+             * connect() will fail.  We loop here forever waiting for it
+             * to come back.
+             *
+             * Waking up and polling every couple of seconds is generally a
+             * bad thing to do, but we only do this if the application is
+             * debuggable *and* adbd isn't running.  Still, for the sake
+             * of battery life, we should consider timing out and giving
+             * up after a few minutes in case somebody ships an app with
+             * the debuggable flag set.
+             */
+            int  ret = connect(netState->controlSock,
+                               &netState->controlAddr.controlAddrPlain,
+                               netState->controlAddrLen);
+            if (!ret) {
+                if (!socket_peer_is_trusted(netState->controlSock)) {
+                    if (shutdown(netState->controlSock, SHUT_RDWR)) {
+                        ALOGE("trouble shutting down socket: %s", strerror(errno));
+                    }
+                    return false;
+                }
+
+                /* now try to send our pid to the ADB daemon */
+                do {
+                    ret = send( netState->controlSock, buff, 4, 0 );
+                } while (ret < 0 && errno == EINTR);
+
+                if (ret >= 0) {
+                    ALOGV("PID sent as '%.*s' to ADB", 4, buff);
+                    break;
+                }
+
+                ALOGE("Weird, can't send JDWP process pid to ADB: %s",
+                     strerror(errno));
+                return false;
+            }
+            ALOGV("Can't connect to ADB control socket:%s",
+                 strerror(errno));
+
+            usleep( sleep_ms*1000 );
+
+            sleep_ms += (sleep_ms >> 1);
+            if (sleep_ms > sleep_max_ms)
+                sleep_ms = sleep_max_ms;
+
+            if (netState->shuttingDown)
+                return false;
+        }
+    }
+
+    ALOGV("trying to receive file descriptor from ADB");
+    /* now we can receive a client file descriptor */
+    netState->clientSock = receiveClientFd(netState);
+    if (netState->shuttingDown)
+        return false;       // suppress logs and additional activity
+
+    if (netState->clientSock < 0) {
+        if (++retryCount > 5) {
+            ALOGE("adb connection max retries exceeded");
+            return false;
+        }
+        goto retry;
+    } else {
+        ALOGV("received file descriptor %d from ADB", netState->clientSock);
+        netState->awaitingHandshake = 1;
+        netState->inputCount = 0;
+        return true;
+    }
+}
+
+/*
+ * Connect out to a debugger (for server=n).  Not required.
+ */
+static bool establishConnection(struct JdwpState* state)
+{
+    return false;
+}
+
+/*
+ * Close a connection from a debugger (which may have already dropped us).
+ * Only called from the JDWP thread.
+ */
+static void closeConnection(struct JdwpState* state)
+{
+    JdwpNetState* netState;
+
+    assert(state != NULL && state->netState != NULL);
+
+    netState = state->netState;
+    if (netState->clientSock < 0)
+        return;
+
+    ALOGV("+++ closed JDWP <-> ADB connection");
+
+    close(netState->clientSock);
+    netState->clientSock = -1;
+}
+
+/*
+ * Close all network stuff, including the socket we use to listen for
+ * new connections.
+ *
+ * May be called from a non-JDWP thread, e.g. when the VM is shutting down.
+ */
+static void adbStateShutdown(struct JdwpNetState* netState)
+{
+    int  controlSock;
+    int  clientSock;
+
+    if (netState == NULL)
+        return;
+
+    netState->shuttingDown = true;
+
+    clientSock = netState->clientSock;
+    if (clientSock >= 0) {
+        shutdown(clientSock, SHUT_RDWR);
+        netState->clientSock = -1;
+    }
+
+    controlSock = netState->controlSock;
+    if (controlSock >= 0) {
+        shutdown(controlSock, SHUT_RDWR);
+        netState->controlSock = -1;
+    }
+
+    if (netState->wakeFds[1] >= 0) {
+        ALOGV("+++ writing to wakePipe");
+        TEMP_FAILURE_RETRY(write(netState->wakeFds[1], "", 1));
+    }
+}
+
+static void netShutdown(JdwpState* state)
+{
+    adbStateShutdown(state->netState);
+}
+
+/*
+ * Free up anything we put in state->netState.  This is called after
+ * "netShutdown", after the JDWP thread has stopped.
+ */
+static void netFree(struct JdwpState* state)
+{
+    JdwpNetState*  netState = state->netState;
+
+    adbStateFree(netState);
+}
+
+/*
+ * Is a debugger connected to us?
+ */
+static bool isConnected(struct JdwpState* state)
+{
+    return (state->netState != NULL   &&
+            state->netState->clientSock >= 0);
+}
+
+/*
+ * Are we still waiting for the JDWP handshake?
+ */
+static bool awaitingHandshake(struct JdwpState* state)
+{
+    return state->netState->awaitingHandshake;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+    long length;
+
+    if (netState->awaitingHandshake)
+        return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+    if (netState->inputCount < 4)
+        return false;
+
+    length = get4BE(netState->inputBuffer);
+    return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer.  However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+    assert(count > 0);
+    assert(count <= netState->inputCount);
+
+    if (count == netState->inputCount) {
+        netState->inputCount = 0;
+        return;
+    }
+
+    memmove(netState->inputBuffer, netState->inputBuffer + count,
+        netState->inputCount - count);
+    netState->inputCount -= count;
+}
+
+/*
+ * Handle a packet.  Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    const unsigned char* buf = netState->inputBuffer;
+    JdwpReqHeader hdr;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0;       // shut up gcc
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    assert((int) length <= netState->inputCount);
+    dataLen = length - (buf - netState->inputBuffer);
+
+    if (!reply) {
+        ExpandBuf* pReply = expandBufAlloc();
+
+        hdr.length = length;
+        hdr.id = id;
+        hdr.cmdSet = cmdSet;
+        hdr.cmd = cmd;
+        dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+        if (expandBufGetLength(pReply) > 0) {
+            ssize_t cc = netState->writePacket(pReply);
+
+            if (cc != (ssize_t) expandBufGetLength(pReply)) {
+                ALOGE("Failed sending reply to debugger: %s", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        ALOGV("reply?!");
+        assert(false);
+    }
+
+    ALOGV("----------");
+
+    consumeBytes(netState, length);
+    return true;
+}
+
+/*
+ * Process incoming data.  If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached.  If we don't, the
+ * debugger will just mysteriously hang until it times out.  We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    int readCount;
+
+    assert(netState->clientSock >= 0);
+
+    if (!haveFullPacket(netState)) {
+        /* read some more, looping until we have data */
+        errno = 0;
+        while (1) {
+            int selCount;
+            fd_set readfds;
+            int maxfd = -1;
+            int fd;
+
+            FD_ZERO(&readfds);
+
+            /* configure fds; note these may get zapped by another thread */
+            fd = netState->controlSock;
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            }
+            fd = netState->clientSock;
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            }
+            fd = netState->wakeFds[0];
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+                if (maxfd < fd)
+                    maxfd = fd;
+            } else {
+                ALOGI("NOTE: entering select w/o wakepipe");
+            }
+
+            if (maxfd < 0) {
+                ALOGV("+++ all fds are closed");
+                return false;
+            }
+
+            /*
+             * Select blocks until it sees activity on the file descriptors.
+             * Closing the local file descriptor does not count as activity,
+             * so we can't rely on that to wake us up (it works for read()
+             * and accept(), but not select()).
+             *
+             * We can do one of three things: (1) send a signal and catch
+             * EINTR, (2) open an additional fd ("wakePipe") and write to
+             * it when it's time to exit, or (3) time out periodically and
+             * re-issue the select.  We're currently using #2, as it's more
+             * reliable than #1 and generally better than #3.  Wastes two fds.
+             */
+            selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+            if (selCount < 0) {
+                if (errno == EINTR)
+                    continue;
+                ALOGE("select failed: %s", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakeFds[0] >= 0 &&
+                FD_ISSET(netState->wakeFds[0], &readfds))
+            {
+                ALOGD("Got wake-up signal, bailing out of select");
+                goto fail;
+            }
+            if (netState->controlSock >= 0 &&
+                FD_ISSET(netState->controlSock, &readfds))
+            {
+                int  sock = receiveClientFd(netState);
+                if (sock >= 0) {
+                    ALOGI("Ignoring second debugger -- accepting and dropping");
+                    close(sock);
+                } else {
+                    assert(netState->controlSock < 0);
+                    /*
+                     * Remote side most likely went away, so our next read
+                     * on netState->clientSock will fail and throw us out
+                     * of the loop.
+                     */
+                }
+            }
+            if (netState->clientSock >= 0 &&
+                FD_ISSET(netState->clientSock, &readfds))
+            {
+                readCount = read(netState->clientSock,
+                                netState->inputBuffer + netState->inputCount,
+                    sizeof(netState->inputBuffer) - netState->inputCount);
+                if (readCount < 0) {
+                    /* read failed */
+                    if (errno != EINTR)
+                        goto fail;
+                    ALOGD("+++ EINTR hit");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    ALOGV("+++ peer disconnected");
+                    goto fail;
+                } else
+                    break;
+            }
+        }
+
+        netState->inputCount += readCount;
+        if (!haveFullPacket(netState))
+            return true;        /* still not there yet */
+    }
+
+    /*
+     * Special-case the initial handshake.  For some bizarre reason we're
+     * expected to emulate bad tty settings by echoing the request back
+     * exactly as it was sent.  Note the handshake is always initiated by
+     * the debugger, no matter who connects to whom.
+     *
+     * Other than this one case, the protocol [claims to be] stateless.
+     */
+    if (netState->awaitingHandshake) {
+        int cc;
+
+        if (memcmp(netState->inputBuffer,
+                kMagicHandshake, kMagicHandshakeLen) != 0)
+        {
+            ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer,
+                                      kMagicHandshakeLen));
+        if (cc != kMagicHandshakeLen) {
+            ALOGE("Failed writing handshake bytes: %s (%d of %d)",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        ALOGV("+++ handshake complete");
+        return true;
+    }
+
+    /*
+     * Handle this packet.
+     */
+    return handlePacket(state);
+
+fail:
+    closeConnection(state);
+    return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    errno = 0;
+
+    ssize_t cc = netState->writePacket(pReq);
+
+    if (cc != (ssize_t) expandBufGetLength(pReq)) {
+        ALOGE("Failed sending req to debugger: %s (%d of %d)",
+            strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+    int iovcnt)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
+
+    if ((size_t)actual != expected) {
+        ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
+            strerror(errno), (int) actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Our functions.
+ */
+static const JdwpTransport socketTransport = {
+    startup,
+    acceptConnection,
+    establishConnection,
+    closeConnection,
+    netShutdown,
+    netFree,
+    isConnected,
+    awaitingHandshake,
+    processIncoming,
+    sendRequest,
+    sendBufferedRequest
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpAndroidAdbTransport()
+{
+    return &socketTransport;
+}
diff --git a/vm/jdwp/JdwpConstants.cpp b/vm/jdwp/JdwpConstants.cpp
new file mode 100644
index 0000000..e98ff43
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * String constants to go along with enumerated values.  (Pity we don't
+ * have enumerated constant reflection in C.)  These are only needed for
+ * making the output human-readable.
+ */
+#include "jdwp/JdwpConstants.h"
+
+/*
+ * Return a string for the error code.
+ */
+const char* dvmJdwpErrorStr(JdwpError error)
+{
+    switch (error) {
+    case ERR_NONE:
+        return "NONE";
+    case ERR_INVALID_THREAD:
+        return "INVALID_THREAD";
+    case ERR_INVALID_THREAD_GROUP:
+        return "INVALID_THREAD_GROUP";
+    case ERR_INVALID_PRIORITY:
+        return "INVALID_PRIORITY";
+    case ERR_THREAD_NOT_SUSPENDED:
+        return "THREAD_NOT_SUSPENDED";
+    case ERR_THREAD_SUSPENDED:
+        return "THREAD_SUSPENDED";
+    case ERR_INVALID_OBJECT:
+        return "INVALID_OBJEC";
+    case ERR_INVALID_CLASS:
+        return "INVALID_CLASS";
+    case ERR_CLASS_NOT_PREPARED:
+        return "CLASS_NOT_PREPARED";
+    case ERR_INVALID_METHODID:
+        return "INVALID_METHODID";
+    case ERR_INVALID_LOCATION:
+        return "INVALID_LOCATION";
+    case ERR_INVALID_FIELDID:
+        return "INVALID_FIELDID";
+    case ERR_INVALID_FRAMEID:
+        return "INVALID_FRAMEID";
+    case ERR_NO_MORE_FRAMES:
+        return "NO_MORE_FRAMES";
+    case ERR_OPAQUE_FRAME:
+        return "OPAQUE_FRAME";
+    case ERR_NOT_CURRENT_FRAME:
+        return "NOT_CURRENT_FRAME";
+    case ERR_TYPE_MISMATCH:
+        return "TYPE_MISMATCH";
+    case ERR_INVALID_SLOT:
+        return "INVALID_SLOT";
+    case ERR_DUPLICATE:
+        return "DUPLICATE";
+    case ERR_NOT_FOUND:
+        return "NOT_FOUND";
+    case ERR_INVALID_MONITOR:
+        return "INVALID_MONITOR";
+    case ERR_NOT_MONITOR_OWNER:
+        return "NOT_MONITOR_OWNER";
+    case ERR_INTERRUPT:
+        return "INTERRUPT";
+    case ERR_INVALID_CLASS_FORMAT:
+        return "INVALID_CLASS_FORMAT";
+    case ERR_CIRCULAR_CLASS_DEFINITION:
+        return "CIRCULAR_CLASS_DEFINITION";
+    case ERR_FAILS_VERIFICATION:
+        return "FAILS_VERIFICATION";
+    case ERR_ADD_METHOD_NOT_IMPLEMENTED:
+        return "ADD_METHOD_NOT_IMPLEMENTED";
+    case ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED:
+        return "SCHEMA_CHANGE_NOT_IMPLEMENTED";
+    case ERR_INVALID_TYPESTATE:
+        return "INVALID_TYPESTATE";
+    case ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED:
+        return "HIERARCHY_CHANGE_NOT_IMPLEMENTED";
+    case ERR_DELETE_METHOD_NOT_IMPLEMENTED:
+        return "DELETE_METHOD_NOT_IMPLEMENTED";
+    case ERR_UNSUPPORTED_VERSION:
+        return "UNSUPPORTED_VERSION";
+    case ERR_NAMES_DONT_MATCH:
+        return "NAMES_DONT_MATCH";
+    case ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+        return "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+    case ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+        return "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+    case ERR_NOT_IMPLEMENTED:
+        return "NOT_IMPLEMENTED";
+    case ERR_NULL_POINTER:
+        return "NULL_POINTER";
+    case ERR_ABSENT_INFORMATION:
+        return "ABSENT_INFORMATION";
+    case ERR_INVALID_EVENT_TYPE:
+        return "INVALID_EVENT_TYPE";
+    case ERR_ILLEGAL_ARGUMENT:
+        return "ILLEGAL_ARGUMENT";
+    case ERR_OUT_OF_MEMORY:
+        return "OUT_OF_MEMORY";
+    case ERR_ACCESS_DENIED:
+        return "ACCESS_DENIED";
+    case ERR_VM_DEAD:
+        return "VM_DEAD";
+    case ERR_INTERNAL:
+        return "INTERNAL";
+    case ERR_UNATTACHED_THREAD:
+        return "UNATTACHED_THREAD";
+    case ERR_INVALID_TAG:
+        return "INVALID_TAG";
+    case ERR_ALREADY_INVOKING:
+        return "ALREADY_INVOKING";
+    case ERR_INVALID_INDEX:
+        return "INVALID_INDEX";
+    case ERR_INVALID_LENGTH:
+        return "INVALID_LENGTH";
+    case ERR_INVALID_STRING:
+        return "INVALID_STRING";
+    case ERR_INVALID_CLASS_LOADER:
+        return "INVALID_CLASS_LOADER";
+    case ERR_INVALID_ARRAY:
+        return "INVALID_ARRAY";
+    case ERR_TRANSPORT_LOAD:
+        return "TRANSPORT_LOAD";
+    case ERR_TRANSPORT_INIT:
+        return "TRANSPORT_INIT";
+    case ERR_NATIVE_METHOD:
+        return "NATIVE_METHOD";
+    case ERR_INVALID_COUNT:
+        return "INVALID_COUNT";
+    default:
+        return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the EventKind.
+ */
+const char* dvmJdwpEventKindStr(JdwpEventKind kind)
+{
+    switch (kind) {
+    case EK_SINGLE_STEP:        return "SINGLE_STEP";
+    case EK_BREAKPOINT:         return "BREAKPOINT";
+    case EK_FRAME_POP:          return "FRAME_POP";
+    case EK_EXCEPTION:          return "EXCEPTION";
+    case EK_USER_DEFINED:       return "USER_DEFINED";
+    case EK_THREAD_START:       return "THREAD_START";
+    /*case EK_THREAD_END:         return "THREAD_END";*/
+    case EK_CLASS_PREPARE:      return "CLASS_PREPARE";
+    case EK_CLASS_UNLOAD:       return "CLASS_UNLOAD";
+    case EK_CLASS_LOAD:         return "CLASS_LOAD";
+    case EK_FIELD_ACCESS:       return "FIELD_ACCESS";
+    case EK_FIELD_MODIFICATION: return "FIELD_MODIFICATION";
+    case EK_EXCEPTION_CATCH:    return "EXCEPTION_CATCH";
+    case EK_METHOD_ENTRY:       return "METHOD_ENTRY";
+    case EK_METHOD_EXIT:        return "METHOD_EXIT";
+    case EK_VM_INIT:            return "VM_INIT";
+    case EK_VM_DEATH:           return "VM_DEATH";
+    case EK_VM_DISCONNECTED:    return "VM_DISCONNECTED";
+    /*case EK_VM_START:           return "VM_START";*/
+    case EK_THREAD_DEATH:       return "THREAD_DEATH";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the ModKind.
+ */
+const char* dvmJdwpModKindStr(JdwpModKind kind)
+{
+    switch (kind) {
+    case MK_COUNT:              return "COUNT";
+    case MK_CONDITIONAL:        return "CONDITIONAL";
+    case MK_THREAD_ONLY:        return "THREAD_ONLY";
+    case MK_CLASS_ONLY:         return "CLASS_ONLY";
+    case MK_CLASS_MATCH:        return "CLASS_MATCH";
+    case MK_CLASS_EXCLUDE:      return "CLASS_EXCLUDE";
+    case MK_LOCATION_ONLY:      return "LOCATION_ONLY";
+    case MK_EXCEPTION_ONLY:     return "EXCEPTION_ONLY";
+    case MK_FIELD_ONLY:         return "FIELD_ONLY";
+    case MK_STEP:               return "STEP";
+    case MK_INSTANCE_ONLY:      return "INSTANCE_ONLY";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the StepDepth.
+ */
+const char* dvmJdwpStepDepthStr(JdwpStepDepth depth)
+{
+    switch (depth) {
+    case SD_INTO:               return "INTO";
+    case SD_OVER:               return "OVER";
+    case SD_OUT:                return "OUT";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the StepSize.
+ */
+const char* dvmJdwpStepSizeStr(JdwpStepSize size)
+{
+    switch (size) {
+    case SS_MIN:                return "MIN";
+    case SS_LINE:               return "LINE";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the SuspendPolicy.
+ */
+const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy)
+{
+    switch (policy) {
+    case SP_NONE:               return "NONE";
+    case SP_EVENT_THREAD:       return "EVENT_THREAD";
+    case SP_ALL:                return "ALL";
+    default:                    return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the SuspendStatus.
+ */
+const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status)
+{
+    switch (status) {
+    case SUSPEND_STATUS_NOT_SUSPENDED: return "Not SUSPENDED";
+    case SUSPEND_STATUS_SUSPENDED:     return "SUSPENDED";
+    default:                           return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the ThreadStatus.
+ */
+const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status)
+{
+    switch (status) {
+    case TS_ZOMBIE:             return "ZOMBIE";
+    case TS_RUNNING:            return "RUNNING";
+    case TS_SLEEPING:           return "SLEEPING";
+    case TS_MONITOR:            return "MONITOR";
+    case TS_WAIT:               return "WAIT";
+    default:                    return "?UNKNOWN?";
+    }
+};
diff --git a/vm/jdwp/JdwpConstants.h b/vm/jdwp/JdwpConstants.h
new file mode 100644
index 0000000..9913759
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * These come out of the JDWP documentation.
+ */
+#ifndef DALVIK_JDWP_JDWPCONSTANTS_H_
+#define DALVIK_JDWP_JDWPCONSTANTS_H_
+
+/*
+ * Error constants.
+ */
+enum JdwpError {
+    ERR_NONE                                        = 0,
+    ERR_INVALID_THREAD                              = 10,
+    ERR_INVALID_THREAD_GROUP                        = 11,
+    ERR_INVALID_PRIORITY                            = 12,
+    ERR_THREAD_NOT_SUSPENDED                        = 13,
+    ERR_THREAD_SUSPENDED                            = 14,
+    ERR_INVALID_OBJECT                              = 20,
+    ERR_INVALID_CLASS                               = 21,
+    ERR_CLASS_NOT_PREPARED                          = 22,
+    ERR_INVALID_METHODID                            = 23,
+    ERR_INVALID_LOCATION                            = 24,
+    ERR_INVALID_FIELDID                             = 25,
+    ERR_INVALID_FRAMEID                             = 30,
+    ERR_NO_MORE_FRAMES                              = 31,
+    ERR_OPAQUE_FRAME                                = 32,
+    ERR_NOT_CURRENT_FRAME                           = 33,
+    ERR_TYPE_MISMATCH                               = 34,
+    ERR_INVALID_SLOT                                = 35,
+    ERR_DUPLICATE                                   = 40,
+    ERR_NOT_FOUND                                   = 41,
+    ERR_INVALID_MONITOR                             = 50,
+    ERR_NOT_MONITOR_OWNER                           = 51,
+    ERR_INTERRUPT                                   = 52,
+    ERR_INVALID_CLASS_FORMAT                        = 60,
+    ERR_CIRCULAR_CLASS_DEFINITION                   = 61,
+    ERR_FAILS_VERIFICATION                          = 62,
+    ERR_ADD_METHOD_NOT_IMPLEMENTED                  = 63,
+    ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED               = 64,
+    ERR_INVALID_TYPESTATE                           = 65,
+    ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED            = 66,
+    ERR_DELETE_METHOD_NOT_IMPLEMENTED               = 67,
+    ERR_UNSUPPORTED_VERSION                         = 68,
+    ERR_NAMES_DONT_MATCH                            = 69,
+    ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED      = 70,
+    ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED     = 71,
+    ERR_NOT_IMPLEMENTED                             = 99,
+    ERR_NULL_POINTER                                = 100,
+    ERR_ABSENT_INFORMATION                          = 101,
+    ERR_INVALID_EVENT_TYPE                          = 102,
+    ERR_ILLEGAL_ARGUMENT                            = 103,
+    ERR_OUT_OF_MEMORY                               = 110,
+    ERR_ACCESS_DENIED                               = 111,
+    ERR_VM_DEAD                                     = 112,
+    ERR_INTERNAL                                    = 113,
+    ERR_UNATTACHED_THREAD                           = 115,
+    ERR_INVALID_TAG                                 = 500,
+    ERR_ALREADY_INVOKING                            = 502,
+    ERR_INVALID_INDEX                               = 503,
+    ERR_INVALID_LENGTH                              = 504,
+    ERR_INVALID_STRING                              = 506,
+    ERR_INVALID_CLASS_LOADER                        = 507,
+    ERR_INVALID_ARRAY                               = 508,
+    ERR_TRANSPORT_LOAD                              = 509,
+    ERR_TRANSPORT_INIT                              = 510,
+    ERR_NATIVE_METHOD                               = 511,
+    ERR_INVALID_COUNT                               = 512,
+};
+const char* dvmJdwpErrorStr(JdwpError error);
+
+
+/*
+ * ClassStatus constants.  These are bit flags that can be ORed together.
+ */
+enum JdwpClassStatus {
+    CS_VERIFIED             = 0x01,
+    CS_PREPARED             = 0x02,
+    CS_INITIALIZED          = 0x04,
+    CS_ERROR                = 0x08,
+};
+
+/*
+ * EventKind constants.
+ */
+enum JdwpEventKind {
+    EK_SINGLE_STEP          = 1,
+    EK_BREAKPOINT           = 2,
+    EK_FRAME_POP            = 3,
+    EK_EXCEPTION            = 4,
+    EK_USER_DEFINED         = 5,
+    EK_THREAD_START         = 6,
+    EK_THREAD_END           = 7,
+    EK_CLASS_PREPARE        = 8,
+    EK_CLASS_UNLOAD         = 9,
+    EK_CLASS_LOAD           = 10,
+    EK_FIELD_ACCESS         = 20,
+    EK_FIELD_MODIFICATION   = 21,
+    EK_EXCEPTION_CATCH      = 30,
+    EK_METHOD_ENTRY         = 40,
+    EK_METHOD_EXIT          = 41,
+    EK_VM_INIT              = 90,
+    EK_VM_DEATH             = 99,
+    EK_VM_DISCONNECTED      = 100,  /* "Never sent across JDWP */
+    EK_VM_START             = EK_VM_INIT,
+    EK_THREAD_DEATH         = EK_THREAD_END,
+};
+const char* dvmJdwpEventKindStr(JdwpEventKind kind);
+
+/*
+ * Values for "modKind" in EventRequest.Set.
+ */
+enum JdwpModKind {
+    MK_COUNT                = 1,
+    MK_CONDITIONAL          = 2,
+    MK_THREAD_ONLY          = 3,
+    MK_CLASS_ONLY           = 4,
+    MK_CLASS_MATCH          = 5,
+    MK_CLASS_EXCLUDE        = 6,
+    MK_LOCATION_ONLY        = 7,
+    MK_EXCEPTION_ONLY       = 8,
+    MK_FIELD_ONLY           = 9,
+    MK_STEP                 = 10,
+    MK_INSTANCE_ONLY        = 11,
+};
+const char* dvmJdwpModKindStr(JdwpModKind kind);
+
+/*
+ * InvokeOptions constants (bit flags).
+ */
+enum JdwpInvokeOptions {
+    INVOKE_SINGLE_THREADED  = 0x01,
+    INVOKE_NONVIRTUAL       = 0x02,
+};
+
+/*
+ * StepDepth constants.
+ */
+enum JdwpStepDepth {
+    SD_INTO                 = 0,    /* step into method calls */
+    SD_OVER                 = 1,    /* step over method calls */
+    SD_OUT                  = 2,    /* step out of current method */
+};
+const char* dvmJdwpStepDepthStr(JdwpStepDepth depth);
+
+/*
+ * StepSize constants.
+ */
+enum JdwpStepSize {
+    SS_MIN                  = 0,    /* step by minimum (e.g. 1 bytecode inst) */
+    SS_LINE                 = 1,    /* if possible, step to next line */
+};
+const char* dvmJdwpStepSizeStr(JdwpStepSize size);
+
+/*
+ * SuspendPolicy constants.
+ */
+enum JdwpSuspendPolicy {
+    SP_NONE                 = 0,    /* suspend no threads */
+    SP_EVENT_THREAD         = 1,    /* suspend event thread */
+    SP_ALL                  = 2,    /* suspend all threads */
+};
+const char* dvmJdwpSuspendPolicyStr(JdwpSuspendPolicy policy);
+
+/*
+ * SuspendStatus constants.
+ */
+enum JdwpSuspendStatus {
+    SUSPEND_STATUS_NOT_SUSPENDED = 0,
+    SUSPEND_STATUS_SUSPENDED     = 1,
+};
+const char* dvmJdwpSuspendStatusStr(JdwpSuspendStatus status);
+
+/*
+ * ThreadStatus constants.
+ */
+enum JdwpThreadStatus {
+    TS_ZOMBIE               = 0,
+    TS_RUNNING              = 1,        // RUNNING
+    TS_SLEEPING             = 2,        // (in Thread.sleep())
+    TS_MONITOR              = 3,        // WAITING (monitor wait)
+    TS_WAIT                 = 4,        // (in Object.wait())
+};
+const char* dvmJdwpThreadStatusStr(JdwpThreadStatus status);
+
+/*
+ * TypeTag constants.
+ */
+enum JdwpTypeTag {
+    TT_CLASS                = 1,
+    TT_INTERFACE            = 2,
+    TT_ARRAY                = 3,
+};
+
+/*
+ * Tag constants.
+ */
+enum JdwpType {
+    JT_ARRAY                 = '[',
+    JT_BYTE                  = 'B',
+    JT_CHAR                  = 'C',
+    JT_OBJECT                = 'L',
+    JT_FLOAT                 = 'F',
+    JT_DOUBLE                = 'D',
+    JT_INT                   = 'I',
+    JT_LONG                  = 'J',
+    JT_SHORT                 = 'S',
+    JT_VOID                  = 'V',
+    JT_BOOLEAN               = 'Z',
+    JT_STRING                = 's',
+    JT_THREAD                = 't',
+    JT_THREAD_GROUP          = 'g',
+    JT_CLASS_LOADER          = 'l',
+    JT_CLASS_OBJECT          = 'c',
+};
+
+#endif  // DALVIK_JDWP_JDWPCONSTANTS_H_
diff --git a/vm/jdwp/JdwpEvent.cpp b/vm/jdwp/JdwpEvent.cpp
new file mode 100644
index 0000000..2793487
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.cpp
@@ -0,0 +1,1278 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Send events to the debugger.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/ExpandBuf.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>     /* for offsetof() */
+#include <unistd.h>
+
+/*
+General notes:
+
+The event add/remove stuff usually happens from the debugger thread,
+in response to requests from the debugger, but can also happen as the
+result of an event in an arbitrary thread (e.g. an event with a "count"
+mod expires).  It's important to keep the event list locked when processing
+events.
+
+Event posting can happen from any thread.  The JDWP thread will not usually
+post anything but VM start/death, but if a JDWP request causes a class
+to be loaded, the ClassPrepare event will come from the JDWP thread.
+
+
+We can have serialization issues when we post an event to the debugger.
+For example, a thread could send an "I hit a breakpoint and am suspending
+myself" message to the debugger.  Before it manages to suspend itself, the
+debugger's response ("not interested, resume thread") arrives and is
+processed.  We try to resume a thread that hasn't yet suspended.
+
+This means that, after posting an event to the debugger, we need to wait
+for the event thread to suspend itself (and, potentially, all other threads)
+before processing any additional requests from the debugger.  While doing
+so we need to be aware that multiple threads may be hitting breakpoints
+or other events simultaneously, so we either need to wait for all of them
+or serialize the events with each other.
+
+The current mechanism works like this:
+  Event thread:
+   - If I'm going to suspend, grab the "I am posting an event" token.  Wait
+     for it if it's not currently available.
+   - Post the event to the debugger.
+   - If appropriate, suspend others and then myself.  As part of suspending
+     myself, release the "I am posting" token.
+  JDWP thread:
+   - When an event arrives, see if somebody is posting an event.  If so,
+     sleep until we can acquire the "I am posting an event" token.  Release
+     it immediately and continue processing -- the event we have already
+     received should not interfere with other events that haven't yet
+     been posted.
+
+Some care must be taken to avoid deadlock:
+
+ - thread A and thread B exit near-simultaneously, and post thread-death
+   events with a "suspend all" clause
+ - thread A gets the event token, thread B sits and waits for it
+ - thread A wants to suspend all other threads, but thread B is waiting
+   for the token and can't be suspended
+
+So we need to mark thread B in such a way that thread A doesn't wait for it.
+
+If we just bracket the "grab event token" call with a change to VMWAIT
+before sleeping, the switch back to RUNNING state when we get the token
+will cause thread B to suspend (remember, thread A's global suspend is
+still in force, even after it releases the token).  Suspending while
+holding the event token is very bad, because it prevents the JDWP thread
+from processing incoming messages.
+
+We need to change to VMWAIT state at the *start* of posting an event,
+and stay there until we either finish posting the event or decide to
+put ourselves to sleep.  That way we don't interfere with anyone else and
+don't allow anyone else to interfere with us.
+*/
+
+
+#define kJdwpEventCommandSet    64
+#define kJdwpCompositeCommand   100
+
+/*
+ * Stuff to compare against when deciding if a mod matches.  Only the
+ * values for mods valid for the event being evaluated will be filled in.
+ * The rest will be zeroed.
+ */
+struct ModBasket {
+    const JdwpLocation* pLoc;           /* LocationOnly */
+    const char*         className;      /* ClassMatch/ClassExclude */
+    ObjectId            threadId;       /* ThreadOnly */
+    RefTypeId           classId;        /* ClassOnly */
+    RefTypeId           excepClassId;   /* ExceptionOnly */
+    bool                caught;         /* ExceptionOnly */
+    FieldId             field;          /* FieldOnly */
+    ObjectId            thisPtr;        /* InstanceOnly */
+    /* nothing for StepOnly -- handled differently */
+};
+
+/*
+ * Get the next "request" serial number.  We use this when sending
+ * packets to the debugger.
+ */
+u4 dvmJdwpNextRequestSerial(JdwpState* state)
+{
+    dvmDbgLockMutex(&state->serialLock);
+    u4 result = state->requestSerial++;
+    dvmDbgUnlockMutex(&state->serialLock);
+
+    return result;
+}
+
+/*
+ * Get the next "event" serial number.  We use this in the response to
+ * message type EventRequest.Set.
+ */
+u4 dvmJdwpNextEventSerial(JdwpState* state)
+{
+    dvmDbgLockMutex(&state->serialLock);
+    u4 result = state->eventSerial++;
+    dvmDbgUnlockMutex(&state->serialLock);
+
+    return result;
+}
+
+/*
+ * Lock the "event" mutex, which guards the list of registered events.
+ */
+static void lockEventMutex(JdwpState* state)
+{
+    //dvmDbgThreadWaiting();
+    dvmDbgLockMutex(&state->eventLock);
+    //dvmDbgThreadRunning();
+}
+
+/*
+ * Unlock the "event" mutex.
+ */
+static void unlockEventMutex(JdwpState* state)
+{
+    dvmDbgUnlockMutex(&state->eventLock);
+}
+
+/*
+ * Dump an event to the log file.
+ */
+static void dumpEvent(const JdwpEvent* pEvent)
+{
+    ALOGI("Event id=0x%4x %p (prev=%p next=%p):",
+        pEvent->requestId, pEvent, pEvent->prev, pEvent->next);
+    ALOGI("  kind=%s susp=%s modCount=%d",
+        dvmJdwpEventKindStr(pEvent->eventKind),
+        dvmJdwpSuspendPolicyStr(pEvent->suspendPolicy),
+        pEvent->modCount);
+
+    for (int i = 0; i < pEvent->modCount; i++) {
+        const JdwpEventMod* pMod = &pEvent->mods[i];
+        JdwpModKind kind = static_cast<JdwpModKind>(pMod->modKind);
+        ALOGI("  %s", dvmJdwpModKindStr(kind));
+        /* TODO - show details */
+    }
+}
+
+/*
+ * Add an event to the list.  Ordering is not important.
+ *
+ * If something prevents the event from being registered, e.g. it's a
+ * single-step request on a thread that doesn't exist, the event will
+ * not be added to the list, and an appropriate error will be returned.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+    lockEventMutex(state);
+
+    assert(state != NULL);
+    assert(pEvent != NULL);
+    assert(pEvent->prev == NULL);
+    assert(pEvent->next == NULL);
+
+    /*
+     * If one or more "break"-type mods are used, register them with
+     * the interpreter.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        const JdwpEventMod* pMod = &pEvent->mods[i];
+        if (pMod->modKind == MK_LOCATION_ONLY) {
+            /* should only be for Breakpoint, Step, and Exception */
+            dvmDbgWatchLocation(&pMod->locationOnly.loc);
+        } else if (pMod->modKind == MK_STEP) {
+            /* should only be for EK_SINGLE_STEP; should only be one */
+            JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
+            JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth);
+            dvmDbgConfigureStep(pMod->step.threadId, size, depth);
+        } else if (pMod->modKind == MK_FIELD_ONLY) {
+            /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */
+            dumpEvent(pEvent);  /* TODO - need for field watches */
+        }
+    }
+
+    /*
+     * Add to list.
+     */
+    if (state->eventList != NULL) {
+        pEvent->next = state->eventList;
+        state->eventList->prev = pEvent;
+    }
+    state->eventList = pEvent;
+    state->numEvents++;
+
+    unlockEventMutex(state);
+
+    return ERR_NONE;
+}
+
+/*
+ * Remove an event from the list.  This will also remove the event from
+ * any optimization tables, e.g. breakpoints.
+ *
+ * Does not free the JdwpEvent.
+ *
+ * Grab the eventLock before calling here.
+ */
+static void unregisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+    if (pEvent->prev == NULL) {
+        /* head of the list */
+        assert(state->eventList == pEvent);
+
+        state->eventList = pEvent->next;
+    } else {
+        pEvent->prev->next = pEvent->next;
+    }
+
+    if (pEvent->next != NULL) {
+        pEvent->next->prev = pEvent->prev;
+        pEvent->next = NULL;
+    }
+    pEvent->prev = NULL;
+
+    /*
+     * Unhook us from the interpreter, if necessary.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        JdwpEventMod* pMod = &pEvent->mods[i];
+        if (pMod->modKind == MK_LOCATION_ONLY) {
+            /* should only be for Breakpoint, Step, and Exception */
+            dvmDbgUnwatchLocation(&pMod->locationOnly.loc);
+        }
+        if (pMod->modKind == MK_STEP) {
+            /* should only be for EK_SINGLE_STEP; should only be one */
+            dvmDbgUnconfigureStep(pMod->step.threadId);
+        }
+    }
+
+    state->numEvents--;
+    assert(state->numEvents != 0 || state->eventList == NULL);
+}
+
+/*
+ * Remove the event with the given ID from the list.
+ *
+ * Failure to find the event isn't really an error, but it is a little
+ * weird.  (It looks like Eclipse will try to be extra careful and will
+ * explicitly remove one-off single-step events.)
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId)
+{
+    lockEventMutex(state);
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        if (pEvent->requestId == requestId) {
+            unregisterEvent(state, pEvent);
+            dvmJdwpEventFree(pEvent);
+            goto done;      /* there can be only one with a given ID */
+        }
+
+        pEvent = pEvent->next;
+    }
+
+    //ALOGD("Odd: no match when removing event reqId=0x%04x", requestId);
+
+done:
+    unlockEventMutex(state);
+}
+
+/*
+ * Remove all entries from the event list.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state)
+{
+    lockEventMutex(state);
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        JdwpEvent* pNextEvent = pEvent->next;
+
+        unregisterEvent(state, pEvent);
+        dvmJdwpEventFree(pEvent);
+        pEvent = pNextEvent;
+    }
+
+    state->eventList = NULL;
+
+    unlockEventMutex(state);
+}
+
+
+
+/*
+ * Allocate a JdwpEvent struct with enough space to hold the specified
+ * number of mod records.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods)
+{
+    JdwpEvent* newEvent;
+    int allocSize = offsetof(JdwpEvent, mods) +
+                    numMods * sizeof(newEvent->mods[0]);
+
+    newEvent = (JdwpEvent*)calloc(1, allocSize);
+    return newEvent;
+}
+
+/*
+ * Free a JdwpEvent.
+ *
+ * Do not call this until the event has been removed from the list.
+ */
+void dvmJdwpEventFree(JdwpEvent* pEvent)
+{
+    if (pEvent == NULL)
+        return;
+
+    /* make sure it was removed from the list */
+    assert(pEvent->prev == NULL);
+    assert(pEvent->next == NULL);
+    /* want to assert state->eventList != pEvent */
+
+    /*
+     * Free any hairy bits in the mods.
+     */
+    for (int i = 0; i < pEvent->modCount; i++) {
+        if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
+            free(pEvent->mods[i].classMatch.classPattern);
+            pEvent->mods[i].classMatch.classPattern = NULL;
+        }
+        if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
+            free(pEvent->mods[i].classExclude.classPattern);
+            pEvent->mods[i].classExclude.classPattern = NULL;
+        }
+    }
+
+    free(pEvent);
+}
+
+/*
+ * Allocate storage for matching events.  To keep things simple we
+ * use an array with enough storage for the entire list.
+ *
+ * The state->eventLock should be held before calling.
+ */
+static JdwpEvent** allocMatchList(JdwpState* state)
+{
+    return (JdwpEvent**) malloc(sizeof(JdwpEvent*) * state->numEvents);
+}
+
+/*
+ * Run through the list and remove any entries with an expired "count" mod
+ * from the event list, then free the match list.
+ */
+static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList,
+    int matchCount)
+{
+    JdwpEvent** ppEvent = matchList;
+
+    while (matchCount--) {
+        JdwpEvent* pEvent = *ppEvent;
+
+        for (int i = 0; i < pEvent->modCount; i++) {
+            if (pEvent->mods[i].modKind == MK_COUNT &&
+                pEvent->mods[i].count.count == 0)
+            {
+                ALOGV("##### Removing expired event");
+                unregisterEvent(state, pEvent);
+                dvmJdwpEventFree(pEvent);
+                break;
+            }
+        }
+
+        ppEvent++;
+    }
+
+    free(matchList);
+}
+
+/*
+ * Match a string against a "restricted regular expression", which is just
+ * a string that may start or end with '*' (e.g. "*.Foo" or "java.*").
+ *
+ * ("Restricted name globbing" might have been a better term.)
+ */
+static bool patternMatch(const char* pattern, const char* target)
+{
+    int patLen = strlen(pattern);
+
+    if (pattern[0] == '*') {
+        int targetLen = strlen(target);
+        patLen--;
+        // TODO: remove printf when we find a test case to verify this
+        ALOGE(">>> comparing '%s' to '%s'",
+            pattern+1, target + (targetLen-patLen));
+
+        if (targetLen < patLen)
+            return false;
+        return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+    } else if (pattern[patLen-1] == '*') {
+        return strncmp(pattern, target, patLen-1) == 0;
+    } else {
+        return strcmp(pattern, target) == 0;
+    }
+}
+
+/*
+ * See if two locations are equal.
+ *
+ * It's tempting to do a bitwise compare ("struct ==" or memcmp), but if
+ * the storage wasn't zeroed out there could be undefined values in the
+ * padding.  Besides, the odds of "idx" being equal while the others aren't
+ * is very small, so this is usually just a simple integer comparison.
+ */
+static inline bool locationMatch(const JdwpLocation* pLoc1,
+    const JdwpLocation* pLoc2)
+{
+    return pLoc1->idx == pLoc2->idx &&
+           pLoc1->methodId == pLoc2->methodId &&
+           pLoc1->classId == pLoc2->classId &&
+           pLoc1->typeTag == pLoc2->typeTag;
+}
+
+/*
+ * See if the event's mods match up with the contents of "basket".
+ *
+ * If we find a Count mod before rejecting an event, we decrement it.  We
+ * need to do this even if later mods cause us to ignore the event.
+ */
+static bool modsMatch(JdwpState* state, JdwpEvent* pEvent, ModBasket* basket)
+{
+    JdwpEventMod* pMod = pEvent->mods;
+
+    for (int i = pEvent->modCount; i > 0; i--, pMod++) {
+        switch (pMod->modKind) {
+        case MK_COUNT:
+            assert(pMod->count.count > 0);
+            pMod->count.count--;
+            break;
+        case MK_CONDITIONAL:
+            assert(false);  // should not be getting these
+            break;
+        case MK_THREAD_ONLY:
+            if (pMod->threadOnly.threadId != basket->threadId)
+                return false;
+            break;
+        case MK_CLASS_ONLY:
+            if (!dvmDbgMatchType(basket->classId, pMod->classOnly.refTypeId))
+                return false;
+            break;
+        case MK_CLASS_MATCH:
+            if (!patternMatch(pMod->classMatch.classPattern,
+                    basket->className))
+                return false;
+            break;
+        case MK_CLASS_EXCLUDE:
+            if (patternMatch(pMod->classMatch.classPattern,
+                    basket->className))
+                return false;
+            break;
+        case MK_LOCATION_ONLY:
+            if (!locationMatch(&pMod->locationOnly.loc, basket->pLoc))
+                return false;
+            break;
+        case MK_EXCEPTION_ONLY:
+            if (pMod->exceptionOnly.refTypeId != 0 &&
+                !dvmDbgMatchType(basket->excepClassId,
+                                 pMod->exceptionOnly.refTypeId))
+                return false;
+            if ((basket->caught && !pMod->exceptionOnly.caught) ||
+                (!basket->caught && !pMod->exceptionOnly.uncaught))
+                return false;
+            break;
+        case MK_FIELD_ONLY:
+            if (!dvmDbgMatchType(basket->classId, pMod->fieldOnly.refTypeId) ||
+                    pMod->fieldOnly.fieldId != basket->field)
+                return false;
+            break;
+        case MK_STEP:
+            if (pMod->step.threadId != basket->threadId)
+                return false;
+            break;
+        case MK_INSTANCE_ONLY:
+            if (pMod->instanceOnly.objectId != basket->thisPtr)
+                return false;
+            break;
+        default:
+            ALOGE("unhandled mod kind %d", pMod->modKind);
+            assert(false);
+            break;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Find all events of type "eventKind" with mods that match up with the
+ * rest of the arguments.
+ *
+ * Found events are appended to "matchList", and "*pMatchCount" is advanced,
+ * so this may be called multiple times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+static void findMatchingEvents(JdwpState* state, JdwpEventKind eventKind,
+    ModBasket* basket, JdwpEvent** matchList, int* pMatchCount)
+{
+    /* start after the existing entries */
+    matchList += *pMatchCount;
+
+    JdwpEvent* pEvent = state->eventList;
+    while (pEvent != NULL) {
+        if (pEvent->eventKind == eventKind && modsMatch(state, pEvent, basket))
+        {
+            *matchList++ = pEvent;
+            (*pMatchCount)++;
+        }
+
+        pEvent = pEvent->next;
+    }
+}
+
+/*
+ * Scan through the list of matches and determine the most severe
+ * suspension policy.
+ */
+static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList,
+    int matchCount)
+{
+    JdwpSuspendPolicy policy = SP_NONE;
+
+    while (matchCount--) {
+        if ((*matchList)->suspendPolicy > policy)
+            policy = (*matchList)->suspendPolicy;
+        matchList++;
+    }
+
+    return policy;
+}
+
+/*
+ * Three possibilities:
+ *  SP_NONE - do nothing
+ *  SP_EVENT_THREAD - suspend ourselves
+ *  SP_ALL - suspend everybody except JDWP support thread
+ */
+static void suspendByPolicy(JdwpState* state, JdwpSuspendPolicy suspendPolicy)
+{
+    if (suspendPolicy == SP_NONE)
+        return;
+
+    if (suspendPolicy == SP_ALL) {
+        dvmDbgSuspendVM(true);
+    } else {
+        assert(suspendPolicy == SP_EVENT_THREAD);
+    }
+
+    /* this is rare but possible -- see CLASS_PREPARE handling */
+    if (dvmDbgGetThreadSelfId() == state->debugThreadId) {
+        ALOGI("NOTE: suspendByPolicy not suspending JDWP thread");
+        return;
+    }
+
+    DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+    while (true) {
+        pReq->ready = true;
+        dvmDbgSuspendSelf();
+        pReq->ready = false;
+
+        /*
+         * The JDWP thread has told us (and possibly all other threads) to
+         * resume.  See if it has left anything in our DebugInvokeReq mailbox.
+         */
+        if (!pReq->invokeNeeded) {
+            /*LOGD("suspendByPolicy: no invoke needed");*/
+            break;
+        }
+
+        /* grab this before posting/suspending again */
+        dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());
+
+        /* leave pReq->invokeNeeded raised so we can check reentrancy */
+        ALOGV("invoking method...");
+        dvmDbgExecuteMethod(pReq);
+
+        pReq->err = ERR_NONE;
+
+        /* clear this before signaling */
+        pReq->invokeNeeded = false;
+
+        ALOGV("invoke complete, signaling and self-suspending");
+        dvmDbgLockMutex(&pReq->lock);
+        dvmDbgCondSignal(&pReq->cv);
+        dvmDbgUnlockMutex(&pReq->lock);
+    }
+}
+
+/*
+ * Determine if there is a method invocation in progress in the current
+ * thread.
+ *
+ * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
+ * state.  If set, we're in the process of invoking a method.
+ */
+static bool invokeInProgress(JdwpState* state)
+{
+    DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+    return pReq->invokeNeeded;
+}
+
+/*
+ * We need the JDWP thread to hold off on doing stuff while we post an
+ * event and then suspend ourselves.
+ *
+ * Call this with a threadId of zero if you just want to wait for the
+ * current thread operation to complete.
+ *
+ * This could go to sleep waiting for another thread, so it's important
+ * that the thread be marked as VMWAIT before calling here.
+ */
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId)
+{
+    bool waited = false;
+
+    /* this is held for very brief periods; contention is unlikely */
+    dvmDbgLockMutex(&state->eventThreadLock);
+
+    /*
+     * If another thread is already doing stuff, wait for it.  This can
+     * go to sleep indefinitely.
+     */
+    while (state->eventThreadId != 0) {
+        ALOGV("event in progress (0x%llx), 0x%llx sleeping",
+            state->eventThreadId, threadId);
+        waited = true;
+        dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
+    }
+
+    if (waited || threadId != 0)
+        ALOGV("event token grabbed (0x%llx)", threadId);
+    if (threadId != 0)
+        state->eventThreadId = threadId;
+
+    dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+/*
+ * Clear the threadId and signal anybody waiting.
+ */
+void dvmJdwpClearWaitForEventThread(JdwpState* state)
+{
+    /*
+     * Grab the mutex.  Don't try to go in/out of VMWAIT mode, as this
+     * function is called by dvmSuspendSelf(), and the transition back
+     * to RUNNING would confuse it.
+     */
+    dvmDbgLockMutex(&state->eventThreadLock);
+
+    assert(state->eventThreadId != 0);
+    ALOGV("cleared event token (0x%llx)", state->eventThreadId);
+
+    state->eventThreadId = 0;
+
+    dvmDbgCondSignal(&state->eventThreadCond);
+
+    dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+
+/*
+ * Prep an event.  Allocates storage for the message and leaves space for
+ * the header.
+ */
+static ExpandBuf* eventPrep()
+{
+    ExpandBuf* pReq = expandBufAlloc();
+    expandBufAddSpace(pReq, kJDWPHeaderLen);
+
+    return pReq;
+}
+
+/*
+ * Write the header into the buffer and send the packet off to the debugger.
+ *
+ * Takes ownership of "pReq" (currently discards it).
+ */
+static void eventFinish(JdwpState* state, ExpandBuf* pReq)
+{
+    u1* buf = expandBufGetBuffer(pReq);
+
+    set4BE(buf, expandBufGetLength(pReq));
+    set4BE(buf+4, dvmJdwpNextRequestSerial(state));
+    set1(buf+8, 0);     /* flags */
+    set1(buf+9, kJdwpEventCommandSet);
+    set1(buf+10, kJdwpCompositeCommand);
+
+    dvmJdwpSendRequest(state, pReq);
+
+    expandBufFree(pReq);
+}
+
+
+/*
+ * Tell the debugger that we have finished initializing.  This is always
+ * sent, even if the debugger hasn't requested it.
+ *
+ * This should be sent "before the main thread is started and before
+ * any application code has been executed".  The thread ID in the message
+ * must be for the main thread.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend)
+{
+    JdwpSuspendPolicy suspendPolicy;
+    ObjectId threadId = dvmDbgGetThreadSelfId();
+
+    if (suspend)
+        suspendPolicy = SP_ALL;
+    else
+        suspendPolicy = SP_NONE;
+
+    /* probably don't need this here */
+    lockEventMutex(state);
+
+    ExpandBuf* pReq = NULL;
+    if (true) {
+        ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_START));
+        ALOGV("  suspendPolicy=%s", dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, 1);
+
+        expandBufAdd1(pReq, EK_VM_START);
+        expandBufAdd4BE(pReq, 0);       /* requestId */
+        expandBufAdd8BE(pReq, threadId);
+    }
+
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    return true;
+}
+
+/*
+ * A location of interest has been reached.  This handles:
+ *   Breakpoint
+ *   SingleStep
+ *   MethodEntry
+ *   MethodExit
+ * These four types must be grouped together in a single response.  The
+ * "eventFlags" indicates the type of event(s) that have happened.
+ *
+ * Valid mods:
+ *   Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
+ *   LocationOnly (for breakpoint/step only)
+ *   Step (for step only)
+ *
+ * Interesting test cases:
+ *  - Put a breakpoint on a native method.  Eclipse creates METHOD_ENTRY
+ *    and METHOD_EXIT events with a ClassOnly mod on the method's class.
+ *  - Use "run to line".  Eclipse creates a BREAKPOINT with Count=1.
+ *  - Single-step to a line with a breakpoint.  Should get a single
+ *    event message with both events in it.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+    ObjectId thisPtr, int eventFlags)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.pLoc = pLoc;
+    basket.classId = pLoc->classId;
+    basket.thisPtr = thisPtr;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(pLoc->classId));
+
+    /*
+     * On rare occasions we may need to execute interpreted code in the VM
+     * while handling a request from the debugger.  Don't fire breakpoints
+     * while doing so.  (I don't think we currently do this at all, so
+     * this is mostly paranoia.)
+     */
+    if (basket.threadId == state->debugThreadId) {
+        ALOGV("Ignoring location event in JDWP thread");
+        free(nameAlloc);
+        return false;
+    }
+
+    /*
+     * The debugger variable display tab may invoke the interpreter to format
+     * complex objects.  We want to ignore breakpoints and method entry/exit
+     * traps while working on behalf of the debugger.
+     *
+     * If we don't ignore them, the VM will get hung up, because we'll
+     * suspend on a breakpoint while the debugger is still waiting for its
+     * method invocation to complete.
+     */
+    if (invokeInProgress(state)) {
+        ALOGV("Not checking breakpoints during invoke (%s)", basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    if ((eventFlags & DBG_BREAKPOINT) != 0)
+        findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_SINGLE_STEP) != 0)
+        findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_METHOD_ENTRY) != 0)
+        findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList,
+            &matchCount);
+    if ((eventFlags & DBG_METHOD_EXIT) != 0)
+        findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList,
+            &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.className,
+            dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
+            basket.threadId, pLoc->idx);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+            dvmJdwpAddLocation(pReq, pLoc);
+        }
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * A thread is starting or stopping.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+
+    assert(threadId == dvmDbgGetThreadSelfId());
+
+    /*
+     * I don't think this can happen.
+     */
+    if (invokeInProgress(state)) {
+        ALOGW("Not posting thread change during invoke");
+        return false;
+    }
+
+    ModBasket basket;
+    memset(&basket, 0, sizeof(basket));
+    basket.threadId = threadId;
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    if (start)
+        findMatchingEvents(state, EK_THREAD_START, &basket, matchList,
+            &matchCount);
+    else
+        findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList,
+            &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+        }
+
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    return matchCount != 0;
+}
+
+/*
+ * Send a polite "VM is dying" message to the debugger.
+ *
+ * Skips the usual "event token" stuff.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state)
+{
+    ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_DEATH));
+
+    ExpandBuf* pReq = eventPrep();
+    expandBufAdd1(pReq, SP_NONE);
+    expandBufAdd4BE(pReq, 1);
+
+    expandBufAdd1(pReq, EK_VM_DEATH);
+    expandBufAdd4BE(pReq, 0);
+    eventFinish(state, pReq);
+    return true;
+}
+
+
+/*
+ * An exception has been thrown.  It may or may not have been caught.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
+ *    ExceptionOnly, InstanceOnly
+ *
+ * The "exceptionId" has not been added to the GC-visible object registry,
+ * because there's a pretty good chance that we're not going to send it
+ * up the debugger.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+    ObjectId exceptionId, RefTypeId exceptionClassId,
+    const JdwpLocation* pCatchLoc, ObjectId thisPtr)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.pLoc = pThrowLoc;
+    basket.classId = pThrowLoc->classId;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+    basket.excepClassId = exceptionClassId;
+    basket.caught = (pCatchLoc->classId != 0);
+    basket.thisPtr = thisPtr;
+
+    /* don't try to post an exception caused by the debugger */
+    if (invokeInProgress(state)) {
+        ALOGV("Not posting exception hit during invoke (%s)",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId, exceptionId, basket.caught);
+        ALOGV("  throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
+            pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
+            dvmDbgGetClassDescriptor(pThrowLoc->classId),
+            dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+        if (pCatchLoc->classId == 0) {
+            ALOGV("  catch: (not caught)");
+        } else {
+            ALOGV("  catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
+                pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
+                dvmDbgGetClassDescriptor(pCatchLoc->classId),
+                dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+        }
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+
+            dvmJdwpAddLocation(pReq, pThrowLoc);
+            expandBufAdd1(pReq, JT_OBJECT);
+            expandBufAdd8BE(pReq, exceptionId);
+            dvmJdwpAddLocation(pReq, pCatchLoc);
+        }
+
+        /* don't let the GC discard it */
+        dvmDbgRegisterObjectId(exceptionId);
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * Announce that a class has been loaded.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+    const char* signature, int status)
+{
+    JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    char* nameAlloc = NULL;
+
+    memset(&basket, 0, sizeof(basket));
+    basket.classId = refTypeId;
+    basket.threadId = dvmDbgGetThreadSelfId();
+    basket.className = nameAlloc =
+        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+
+    /* suppress class prep caused by debugger */
+    if (invokeInProgress(state)) {
+        ALOGV("Not posting class prep caused by invoke (%s)",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    JdwpEvent** matchList = allocMatchList(state);
+    int matchCount = 0;
+
+    findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
+        &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        ALOGV("EVENT: %s(%d total) thread=%llx)",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        ALOGV("  suspendPolicy=%s",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        if (basket.threadId == state->debugThreadId) {
+            /*
+             * JDWP says that, for a class prep in the debugger thread, we
+             * should set threadId to null and if any threads were supposed
+             * to be suspended then we suspend all other threads.
+             */
+            ALOGV("  NOTE: class prepare in debugger thread!");
+            basket.threadId = 0;
+            if (suspendPolicy == SP_EVENT_THREAD)
+                suspendPolicy = SP_ALL;
+        }
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (int i = 0; i < matchCount; i++) {
+            expandBufAdd1(pReq, matchList[i]->eventKind);
+            expandBufAdd4BE(pReq, matchList[i]->requestId);
+            expandBufAdd8BE(pReq, basket.threadId);
+
+            expandBufAdd1(pReq, tag);
+            expandBufAdd8BE(pReq, refTypeId);
+            expandBufAddUtf8String(pReq, (const u1*) signature);
+            expandBufAdd4BE(pReq, status);
+        }
+    }
+
+    cleanupMatchList(state, matchList, matchCount);
+
+    unlockEventMutex(state);
+
+    /* send request and possibly suspend ourselves */
+    if (pReq != NULL) {
+        int oldStatus = dvmDbgThreadWaiting();
+        if (suspendPolicy != SP_NONE)
+            dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+        eventFinish(state, pReq);
+
+        suspendByPolicy(state, suspendPolicy);
+        dvmDbgThreadContinuing(oldStatus);
+    }
+
+    free(nameAlloc);
+    return matchCount != 0;
+}
+
+/*
+ * Unload a class.
+ *
+ * Valid mods:
+ *  Count, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassUnload(JdwpState* state, RefTypeId refTypeId)
+{
+    assert(false);      // TODO
+    return false;
+}
+
+/*
+ * Get or set a field.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, FieldOnly,
+ *    InstanceOnly
+ */
+bool dvmJdwpPostFieldAccess(JdwpState* state, int STUFF, ObjectId thisPtr,
+    bool modified, JValue newValue)
+{
+    assert(false);      // TODO
+    return false;
+}
+
+/*
+ * Send up a chunk of DDM data.
+ *
+ * While this takes the form of a JDWP "event", it doesn't interact with
+ * other debugger traffic, and can't suspend the VM, so we skip all of
+ * the fun event token gymnastics.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+    int iovcnt)
+{
+    u1 header[kJDWPHeaderLen + 8];
+    size_t dataLen = 0;
+
+    assert(iov != NULL);
+    assert(iovcnt > 0 && iovcnt < 10);
+
+    /*
+     * "Wrap" the contents of the iovec with a JDWP/DDMS header.  We do
+     * this by creating a new copy of the vector with space for the header.
+     */
+    struct iovec wrapiov[iovcnt+1];
+    for (int i = 0; i < iovcnt; i++) {
+        wrapiov[i+1].iov_base = iov[i].iov_base;
+        wrapiov[i+1].iov_len = iov[i].iov_len;
+        dataLen += iov[i].iov_len;
+    }
+
+    /* form the header (JDWP plus DDMS) */
+    set4BE(header, sizeof(header) + dataLen);
+    set4BE(header+4, dvmJdwpNextRequestSerial(state));
+    set1(header+8, 0);     /* flags */
+    set1(header+9, kJDWPDdmCmdSet);
+    set1(header+10, kJDWPDdmCmd);
+    set4BE(header+11, type);
+    set4BE(header+15, dataLen);
+
+    wrapiov[0].iov_base = header;
+    wrapiov[0].iov_len = sizeof(header);
+
+    /*
+     * Make sure we're in VMWAIT in case the write blocks.
+     */
+    int oldStatus = dvmDbgThreadWaiting();
+    dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
+    dvmDbgThreadContinuing(oldStatus);
+}
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
new file mode 100644
index 0000000..c7d3e3c
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Handle registration of events, and debugger event notification.
+ */
+#ifndef DALVIK_JDWP_JDWPEVENT_H_
+#define DALVIK_JDWP_JDWPEVENT_H_
+
+#include "JdwpConstants.h"
+#include "ExpandBuf.h"
+
+/*
+ * Event modifiers.  A JdwpEvent may have zero or more of these.
+ */
+union JdwpEventMod {
+    u1      modKind;                /* JdwpModKind */
+    struct {
+        u1          modKind;
+        int         count;
+    } count;
+    struct {
+        u1          modKind;
+        u4          exprId;
+    } conditional;
+    struct {
+        u1          modKind;
+        ObjectId    threadId;
+    } threadOnly;
+    struct {
+        u1          modKind;
+        RefTypeId   refTypeId;
+    } classOnly;
+    struct {
+        u1          modKind;
+        char*       classPattern;
+    } classMatch;
+    struct {
+        u1          modKind;
+        char*       classPattern;
+    } classExclude;
+    struct {
+        u1          modKind;
+        JdwpLocation loc;
+    } locationOnly;
+    struct {
+        u1          modKind;
+        u1          caught;
+        u1          uncaught;
+        RefTypeId   refTypeId;
+    } exceptionOnly;
+    struct {
+        u1          modKind;
+        RefTypeId   refTypeId;
+        FieldId     fieldId;
+    } fieldOnly;
+    struct {
+        u1          modKind;
+        ObjectId    threadId;
+        int         size;           /* JdwpStepSize */
+        int         depth;          /* JdwpStepDepth */
+    } step;
+    struct {
+        u1          modKind;
+        ObjectId    objectId;
+    } instanceOnly;
+};
+
+/*
+ * One of these for every registered event.
+ *
+ * We over-allocate the struct to hold the modifiers.
+ */
+struct JdwpEvent {
+    JdwpEvent* prev;           /* linked list */
+    JdwpEvent* next;
+
+    JdwpEventKind eventKind;      /* what kind of event is this? */
+    JdwpSuspendPolicy suspendPolicy;  /* suspend all, none, or self? */
+    int modCount;       /* #of entries in mods[] */
+    u4 requestId;      /* serial#, reported to debugger */
+
+    JdwpEventMod mods[1];        /* MUST be last field in struct */
+};
+
+/*
+ * Allocate an event structure with enough space.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods);
+void dvmJdwpEventFree(JdwpEvent* pEvent);
+
+/*
+ * Register an event by adding it to the event list.
+ *
+ * "*pEvent" must be storage allocated with jdwpEventAlloc().  The caller
+ * may discard its pointer after calling this.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent);
+
+/*
+ * Unregister an event, given the requestId.
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId);
+
+/*
+ * Unregister all events.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state);
+
+/*
+ * Send an event, formatted into "pReq", to the debugger.
+ *
+ * (Messages are sent asynchronously, and do not receive a reply.)
+ */
+bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq);
+
+#endif  // DALVIK_JDWP_JDWPEVENT_H_
diff --git a/vm/jdwp/JdwpHandler.cpp b/vm/jdwp/JdwpHandler.cpp
new file mode 100644
index 0000000..112ac4a
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.cpp
@@ -0,0 +1,1973 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Handle messages from debugger.
+ *
+ * GENERAL NOTE: we're not currently testing the message length for
+ * correctness.  This is usually a bad idea, but here we can probably
+ * get away with it so long as the debugger isn't broken.  We can
+ * change the "read" macros to use "dataLen" to avoid wandering into
+ * bad territory, and have a single "is dataLen correct" check at the
+ * end of each function.  Not needed at this time.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+
+#include "Bits.h"
+#include "Atomic.h"
+#include "DalvikVersion.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Helper function: read a "location" from an input buffer.
+ */
+static void jdwpReadLocation(const u1** pBuf, JdwpLocation* pLoc)
+{
+    memset(pLoc, 0, sizeof(*pLoc));     /* allows memcmp() later */
+    pLoc->typeTag = read1(pBuf);
+    pLoc->classId = dvmReadObjectId(pBuf);
+    pLoc->methodId = dvmReadMethodId(pBuf);
+    pLoc->idx = read8BE(pBuf);
+}
+
+/*
+ * Helper function: write a "location" into the reply buffer.
+ */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc)
+{
+    expandBufAdd1(pReply, pLoc->typeTag);
+    expandBufAddObjectId(pReply, pLoc->classId);
+    expandBufAddMethodId(pReply, pLoc->methodId);
+    expandBufAdd8BE(pReply, pLoc->idx);
+}
+
+/*
+ * Helper function: read a variable-width value from the input buffer.
+ */
+static u8 jdwpReadValue(const u1** pBuf, int width)
+{
+    u8 value;
+
+    switch (width) {
+    case 1:     value = read1(pBuf);                break;
+    case 2:     value = read2BE(pBuf);              break;
+    case 4:     value = read4BE(pBuf);              break;
+    case 8:     value = read8BE(pBuf);              break;
+    default:    value = (u8) -1; assert(false);     break;
+    }
+
+    return value;
+}
+
+/*
+ * Helper function: write a variable-width value into the output input buffer.
+ */
+static void jdwpWriteValue(ExpandBuf* pReply, int width, u8 value)
+{
+    switch (width) {
+    case 1:     expandBufAdd1(pReply, value);       break;
+    case 2:     expandBufAdd2BE(pReply, value);     break;
+    case 4:     expandBufAdd4BE(pReply, value);     break;
+    case 8:     expandBufAdd8BE(pReply, value);     break;
+    default:    assert(false);                      break;
+    }
+}
+
+/*
+ * Common code for *_InvokeMethod requests.
+ *
+ * If "isConstructor" is set, this returns "objectId" rather than the
+ * expected-to-be-void return value of the called function.
+ */
+static JdwpError finishInvoke(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply,
+    ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
+    bool isConstructor)
+{
+    assert(!isConstructor || objectId != 0);
+
+    u4 numArgs = read4BE(&buf);
+
+    ALOGV("    --> threadId=%llx objectId=%llx", threadId, objectId);
+    ALOGV("        classId=%llx methodId=%x %s.%s",
+        classId, methodId,
+        dvmDbgGetClassDescriptor(classId),
+        dvmDbgGetMethodName(classId, methodId));
+    ALOGV("        %d args:", numArgs);
+
+    u8* argArray = NULL;
+    if (numArgs > 0)
+        argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
+
+    for (u4 i = 0; i < numArgs; i++) {
+        u1 typeTag = read1(&buf);
+        int width = dvmDbgGetTagWidth(typeTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("          '%c'(%d): 0x%llx", typeTag, width, value);
+        argArray[i] = value;
+    }
+
+    u4 options = read4BE(&buf);  /* enum InvokeOptions bit flags */
+    ALOGV("        options=0x%04x%s%s", options,
+        (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
+        (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
+
+
+    u1 resultTag;
+    u8 resultValue;
+    ObjectId exceptObjId;
+    JdwpError err = dvmDbgInvokeMethod(threadId, objectId, classId, methodId,
+            numArgs, argArray, options,
+            &resultTag, &resultValue, &exceptObjId);
+    if (err != ERR_NONE)
+        goto bail;
+
+    if (err == ERR_NONE) {
+        if (isConstructor) {
+            expandBufAdd1(pReply, JT_OBJECT);
+            expandBufAddObjectId(pReply, objectId);
+        } else {
+            int width = dvmDbgGetTagWidth(resultTag);
+
+            expandBufAdd1(pReply, resultTag);
+            if (width != 0)
+                jdwpWriteValue(pReply, width, resultValue);
+        }
+        expandBufAdd1(pReply, JT_OBJECT);
+        expandBufAddObjectId(pReply, exceptObjId);
+
+        ALOGV("  --> returned '%c' 0x%llx (except=%08llx)",
+            resultTag, resultValue, exceptObjId);
+
+        /* show detailed debug output */
+        if (resultTag == JT_STRING && exceptObjId == 0) {
+            if (resultValue != 0) {
+                char* str = dvmDbgStringToUtf8(resultValue);
+                ALOGV("      string '%s'", str);
+                free(str);
+            } else {
+                ALOGV("      string (null)");
+            }
+        }
+    }
+
+bail:
+    free(argArray);
+    return err;
+}
+
+
+/*
+ * Request for version info.
+ */
+static JdwpError handleVM_Version(JdwpState* state, const u1* buf,
+    int dataLen, ExpandBuf* pReply)
+{
+    char tmpBuf[128];
+
+    /* text information on VM version */
+    sprintf(tmpBuf, "Android DalvikVM %d.%d.%d",
+        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    expandBufAddUtf8String(pReply, (const u1*) tmpBuf);
+    /* JDWP version numbers */
+    expandBufAdd4BE(pReply, 1);        // major
+    expandBufAdd4BE(pReply, 5);        // minor
+    /* VM JRE version */
+    expandBufAddUtf8String(pReply, (const u1*) "1.5.0");  /* e.g. 1.5.0_04 */
+    /* target VM name */
+    expandBufAddUtf8String(pReply, (const u1*) "DalvikVM");
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a class JNI signature (e.g. "Ljava/lang/Error;"), return the
+ * referenceTypeID.  We need to send back more than one if the class has
+ * been loaded by multiple class loaders.
+ */
+static JdwpError handleVM_ClassesBySignature(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    size_t strLen;
+    char* classDescriptor = readNewUtf8String(&buf, &strLen);
+    ALOGV("  Req for class by signature '%s'", classDescriptor);
+
+    /*
+     * TODO: if a class with the same name has been loaded multiple times
+     * (by different class loaders), we're supposed to return each of them.
+     *
+     * NOTE: this may mangle "className".
+     */
+    u4 numClasses;
+    RefTypeId refTypeId;
+    if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
+        /* not currently loaded */
+        ALOGV("    --> no match!");
+        numClasses = 0;
+    } else {
+        /* just the one */
+        numClasses = 1;
+    }
+
+    expandBufAdd4BE(pReply, numClasses);
+
+    if (numClasses > 0) {
+        u1 typeTag;
+        u4 status;
+
+        /* get class vs. interface and status flags */
+        dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+
+        expandBufAdd1(pReply, typeTag);
+        expandBufAddRefTypeId(pReply, refTypeId);
+        expandBufAdd4BE(pReply, status);
+    }
+
+    free(classDescriptor);
+
+    return ERR_NONE;
+}
+
+/*
+ * Handle request for the thread IDs of all running threads.
+ *
+ * We exclude ourselves from the list, because we don't allow ourselves
+ * to be suspended, and that violates some JDWP expectations.
+ */
+static JdwpError handleVM_AllThreads(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId* pThreadIds;
+    u4 threadCount;
+    dvmDbgGetAllThreads(&pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    ObjectId* walker = pThreadIds;
+    for (u4 i = 0; i < threadCount; i++) {
+        expandBufAddObjectId(pReply, *walker++);
+    }
+
+    free(pThreadIds);
+
+    return ERR_NONE;
+}
+
+/*
+ * List all thread groups that do not have a parent.
+ */
+static JdwpError handleVM_TopLevelThreadGroups(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    /*
+     * TODO: maintain a list of parentless thread groups in the VM.
+     *
+     * For now, just return "system".  Application threads are created
+     * in "main", which is a child of "system".
+     */
+    u4 groups = 1;
+    expandBufAdd4BE(pReply, groups);
+    //threadGroupId = debugGetMainThreadGroup();
+    //expandBufAdd8BE(pReply, threadGroupId);
+    ObjectId threadGroupId = dvmDbgGetSystemThreadGroupId();
+    expandBufAddObjectId(pReply, threadGroupId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Respond with the sizes of the basic debugger types.
+ *
+ * All IDs are 8 bytes.
+ */
+static JdwpError handleVM_IDSizes(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd4BE(pReply, sizeof(FieldId));
+    expandBufAdd4BE(pReply, sizeof(MethodId));
+    expandBufAdd4BE(pReply, sizeof(ObjectId));
+    expandBufAdd4BE(pReply, sizeof(RefTypeId));
+    expandBufAdd4BE(pReply, sizeof(FrameId));
+    return ERR_NONE;
+}
+
+/*
+ * The debugger is politely asking to disconnect.  We're good with that.
+ *
+ * We could resume threads and clean up pinned references, but we can do
+ * that when the TCP connection drops.
+ */
+static JdwpError handleVM_Dispose(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    return ERR_NONE;
+}
+
+/*
+ * Suspend the execution of the application running in the VM (i.e. suspend
+ * all threads).
+ *
+ * This needs to increment the "suspend count" on all threads.
+ */
+static JdwpError handleVM_Suspend(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    dvmDbgSuspendVM(false);
+    return ERR_NONE;
+}
+
+/*
+ * Resume execution.  Decrements the "suspend count" of all threads.
+ */
+static JdwpError handleVM_Resume(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    dvmDbgResumeVM();
+    return ERR_NONE;
+}
+
+/*
+ * The debugger wants the entire VM to exit.
+ */
+static JdwpError handleVM_Exit(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u4 exitCode = get4BE(buf);
+
+    ALOGW("Debugger is telling the VM to exit with code=%d", exitCode);
+
+    dvmDbgExit(exitCode);
+    return ERR_NOT_IMPLEMENTED;     // shouldn't get here
+}
+
+/*
+ * Create a new string in the VM and return its ID.
+ *
+ * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
+ * string "java.util.Arrays".)
+ */
+static JdwpError handleVM_CreateString(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    size_t strLen;
+    char* str = readNewUtf8String(&buf, &strLen);
+
+    ALOGV("  Req to create string '%s'", str);
+
+    ObjectId stringId = dvmDbgCreateString(str);
+    free(str);
+    if (stringId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    expandBufAddObjectId(pReply, stringId);
+    return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_Capabilities(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
+    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
+    expandBufAdd1(pReply, false);   /* canGetBytecodes */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
+    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
+    return ERR_NONE;
+}
+
+/*
+ * Return classpath and bootclasspath.
+ */
+static JdwpError handleVM_ClassPaths(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    char baseDir[2] = "/";
+
+    /*
+     * TODO: make this real.  Not important for remote debugging, but
+     * might be useful for local debugging.
+     */
+    u4 classPaths = 1;
+    u4 bootClassPaths = 0;
+
+    expandBufAddUtf8String(pReply, (const u1*) baseDir);
+    expandBufAdd4BE(pReply, classPaths);
+    for (u4 i = 0; i < classPaths; i++) {
+        expandBufAddUtf8String(pReply, (const u1*) ".");
+    }
+
+    expandBufAdd4BE(pReply, bootClassPaths);
+    for (u4 i = 0; i < classPaths; i++) {
+        /* add bootclasspath components as strings */
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Release a list of object IDs.  (Seen in jdb.)
+ *
+ * Currently does nothing.
+ */
+static JdwpError HandleVM_DisposeObjects(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_CapabilitiesNew(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
+    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
+    expandBufAdd1(pReply, false);   /* canGetBytecodes */
+    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
+    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
+    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
+    expandBufAdd1(pReply, false);   /* canRedefineClasses */
+    expandBufAdd1(pReply, false);   /* canAddMethod */
+    expandBufAdd1(pReply, false);   /* canUnrestrictedlyRedefineClasses */
+    expandBufAdd1(pReply, false);   /* canPopFrames */
+    expandBufAdd1(pReply, false);   /* canUseInstanceFilters */
+    expandBufAdd1(pReply, false);   /* canGetSourceDebugExtension */
+    expandBufAdd1(pReply, false);   /* canRequestVMDeathEvent */
+    expandBufAdd1(pReply, false);   /* canSetDefaultStratum */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetInstanceInfo */
+    expandBufAdd1(pReply, false);   /* 1.6: canRequestMonitorEvents */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetMonitorFrameInfo */
+    expandBufAdd1(pReply, false);   /* 1.6: canUseSourceNameFilters */
+    expandBufAdd1(pReply, false);   /* 1.6: canGetConstantPool */
+    expandBufAdd1(pReply, false);   /* 1.6: canForceEarlyReturn */
+
+    /* fill in reserved22 through reserved32; note count started at 1 */
+    for (int i = 22; i <= 32; i++)
+        expandBufAdd1(pReply, false);   /* reservedN */
+    return ERR_NONE;
+}
+
+/*
+ * Cough up the complete list of classes.
+ */
+static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u4 numClasses = 0;
+    RefTypeId* classRefBuf = NULL;
+
+    dvmDbgGetClassList(&numClasses, &classRefBuf);
+
+    expandBufAdd4BE(pReply, numClasses);
+
+    for (u4 i = 0; i < numClasses; i++) {
+        static const u1 genericSignature[1] = "";
+        u1 refTypeTag;
+        const char* signature;
+        u4 status;
+
+        dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
+
+        expandBufAdd1(pReply, refTypeTag);
+        expandBufAddRefTypeId(pReply, classRefBuf[i]);
+        expandBufAddUtf8String(pReply, (const u1*) signature);
+        expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, status);
+    }
+
+    free(classRefBuf);
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a string with the JNI reference type
+ * signature (e.g. "Ljava/lang/Error;").
+ */
+static JdwpError handleRT_Signature(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
+    const char* signature = dvmDbgGetSignature(refTypeId);
+    expandBufAddUtf8String(pReply, (const u1*) signature);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the modifiers (a/k/a access flags) for a reference type.
+ */
+static JdwpError handleRT_Modifiers(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    u4 modBits = dvmDbgGetAccessFlags(refTypeId);
+
+    expandBufAdd4BE(pReply, modBits);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get values from static fields in a reference type.
+ */
+static JdwpError handleRT_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  RT_GetValues %u:", numFields);
+
+    expandBufAdd4BE(pReply, numFields);
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetStaticFieldValue(refTypeId, fieldId, pReply);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Get the name of the source file in which a reference type was declared.
+ */
+static JdwpError handleRT_SourceFile(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    const char* fileName = dvmDbgGetSourceFile(refTypeId);
+    if (fileName != NULL) {
+        expandBufAddUtf8String(pReply, (const u1*) fileName);
+        return ERR_NONE;
+    } else {
+        return ERR_ABSENT_INFORMATION;
+    }
+}
+
+/*
+ * Return the current status of the reference type.
+ */
+static JdwpError handleRT_Status(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    /* get status flags */
+    u1 typeTag;
+    u4 status;
+    dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+    expandBufAdd4BE(pReply, status);
+    return ERR_NONE;
+}
+
+/*
+ * Return interfaces implemented directly by this class.
+ */
+static JdwpError handleRT_Interfaces(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for interfaces in %llx (%s)", refTypeId,
+        dvmDbgGetClassDescriptor(refTypeId));
+
+    dvmDbgOutputAllInterfaces(refTypeId, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the class object corresponding to this type.
+ */
+static JdwpError handleRT_ClassObject(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    ObjectId classObjId = dvmDbgGetClassObject(refTypeId);
+
+    ALOGV("  RefTypeId %llx -> ObjectId %llx", refTypeId, classObjId);
+
+    expandBufAddObjectId(pReply, classObjId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the value of the SourceDebugExtension attribute.
+ *
+ * JDB seems interested, but DEX files don't currently support this.
+ */
+static JdwpError handleRT_SourceDebugExtension(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    /* referenceTypeId in, string out */
+    return ERR_ABSENT_INFORMATION;
+}
+
+/*
+ * Like RT_Signature but with the possibility of a "generic signature".
+ */
+static JdwpError handleRT_SignatureWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    static const u1 genericSignature[1] = "";
+
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
+    const char* signature = dvmDbgGetSignature(refTypeId);
+    if (signature != NULL) {
+        expandBufAddUtf8String(pReply, (const u1*) signature);
+    } else {
+        ALOGW("No signature for refTypeId=0x%llx", refTypeId);
+        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");
+    }
+    expandBufAddUtf8String(pReply, genericSignature);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the instance of java.lang.ClassLoader that loaded the specified
+ * reference type, or null if it was loaded by the system loader.
+ */
+static JdwpError handleRT_ClassLoader(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    expandBufAddObjectId(pReply, dvmDbgGetClassLoader(refTypeId));
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeId, return a block of stuff that describes the
+ * fields declared by a class.
+ */
+static JdwpError handleRT_FieldsWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    ALOGV("  Req for fields in refTypeId=0x%llx", refTypeId);
+    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
+
+    dvmDbgOutputAllFields(refTypeId, true, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a block of goodies describing the
+ * methods declared by a class.
+ */
+static JdwpError handleRT_MethodsWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for methods in refTypeId=0x%llx", refTypeId);
+    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
+
+    dvmDbgOutputAllMethods(refTypeId, true, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the immediate superclass of a class.
+ */
+static JdwpError handleCT_Superclass(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+
+    RefTypeId superClassId = dvmDbgGetSuperclass(classId);
+
+    expandBufAddRefTypeId(pReply, superClassId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Set static class values.
+ */
+static JdwpError handleCT_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    u4 values = read4BE(&buf);
+
+    ALOGV("  Req to set %d values in classId=%llx", values, classId);
+
+    for (u4 i = 0; i < values; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        u1 fieldTag = dvmDbgGetStaticFieldBasicTag(classId, fieldId);
+        int width = dvmDbgGetTagWidth(fieldTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> field=%x tag=%c -> %lld", fieldId, fieldTag, value);
+        dvmDbgSetStaticFieldValue(classId, fieldId, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Invoke a static method.
+ *
+ * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
+ * values in the "variables" display.
+ */
+static JdwpError handleCT_InvokeMethod(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, 0, classId, methodId, false);
+}
+
+/*
+ * Create a new object of the requested type, and invoke the specified
+ * constructor.
+ *
+ * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
+ * see the contents of a byte[] as a string.
+ */
+static JdwpError handleCT_NewInstance(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("Creating instance of %s", dvmDbgGetClassDescriptor(classId));
+    ObjectId objectId = dvmDbgCreateObject(classId);
+    if (objectId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, objectId, classId, methodId, true);
+}
+
+/*
+ * Create a new array object of the requested type and length.
+ */
+static JdwpError handleAT_newInstance(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId arrayTypeId = dvmReadRefTypeId(&buf);
+    u4 length = read4BE(&buf);
+
+    ALOGV("Creating array %s[%u]",
+        dvmDbgGetClassDescriptor(arrayTypeId), length);
+    ObjectId objectId = dvmDbgCreateArrayObject(arrayTypeId, length);
+    if (objectId == 0)
+        return ERR_OUT_OF_MEMORY;
+
+    expandBufAdd1(pReply, JT_ARRAY);
+    expandBufAddObjectId(pReply, objectId);
+    return ERR_NONE;
+}
+
+/*
+ * Return line number information for the method, if present.
+ */
+static JdwpError handleM_LineTable(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("  Req for line table in %s.%s",
+        dvmDbgGetClassDescriptor(refTypeId),
+        dvmDbgGetMethodName(refTypeId,methodId));
+
+    dvmDbgOutputLineTable(refTypeId, methodId, pReply);
+
+    return ERR_NONE;
+}
+
+/*
+ * Pull out the LocalVariableTable goodies.
+ */
+static JdwpError handleM_VariableTableWithGeneric(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    ALOGV("  Req for LocalVarTab in class=%s method=%s",
+        dvmDbgGetClassDescriptor(classId),
+        dvmDbgGetMethodName(classId, methodId));
+
+    /*
+     * We could return ERR_ABSENT_INFORMATION here if the DEX file was
+     * built without local variable information.  That will cause Eclipse
+     * to make a best-effort attempt at displaying local variables
+     * anonymously.  However, the attempt isn't very good, so we're probably
+     * better off just not showing anything.
+     */
+    dvmDbgOutputVariableTable(classId, methodId, true, pReply);
+    return ERR_NONE;
+}
+
+/*
+ * Given an object reference, return the runtime type of the object
+ * (class or array).
+ *
+ * This can get called on different things, e.g. threadId gets
+ * passed in here.
+ */
+static JdwpError handleOR_ReferenceType(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    ALOGV("  Req for type of objectId=0x%llx", objectId);
+
+    u1 refTypeTag;
+    RefTypeId typeId;
+    dvmDbgGetObjectType(objectId, &refTypeTag, &typeId);
+
+    expandBufAdd1(pReply, refTypeTag);
+    expandBufAddRefTypeId(pReply, typeId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get values from the fields of an object.
+ */
+static JdwpError handleOR_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  Req for %d fields from objectId=0x%llx", numFields, objectId);
+
+    expandBufAdd4BE(pReply, numFields);
+
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+        dvmDbgGetFieldValue(objectId, fieldId, pReply);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set values in the fields of an object.
+ */
+static JdwpError handleOR_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    u4 numFields = read4BE(&buf);
+
+    ALOGV("  Req to set %d fields in objectId=0x%llx", numFields, objectId);
+
+    for (u4 i = 0; i < numFields; i++) {
+        FieldId fieldId = dvmReadFieldId(&buf);
+
+        u1 fieldTag = dvmDbgGetFieldBasicTag(objectId, fieldId);
+        int width = dvmDbgGetTagWidth(fieldTag);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> fieldId=%x tag='%c'(%d) value=%lld",
+            fieldId, fieldTag, width, value);
+
+        dvmDbgSetFieldValue(objectId, fieldId, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Invoke an instance method.  The invocation must occur in the specified
+ * thread, which must have been suspended by an event.
+ *
+ * The call is synchronous.  All threads in the VM are resumed, unless the
+ * SINGLE_THREADED flag is set.
+ *
+ * If you ask Eclipse to "inspect" an object (or ask JDB to "print" an
+ * object), it will try to invoke the object's toString() function.  This
+ * feature becomes crucial when examining ArrayLists with Eclipse.
+ */
+static JdwpError handleOR_InvokeMethod(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId = dvmReadObjectId(&buf);
+    ObjectId threadId = dvmReadObjectId(&buf);
+    RefTypeId classId = dvmReadRefTypeId(&buf);
+    MethodId methodId = dvmReadMethodId(&buf);
+
+    return finishInvoke(state, buf, dataLen, pReply,
+            threadId, objectId, classId, methodId, false);
+}
+
+/*
+ * Disable garbage collection of the specified object.
+ */
+static JdwpError handleOR_DisableCollection(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    // this is currently a no-op
+    return ERR_NONE;
+}
+
+/*
+ * Enable garbage collection of the specified object.
+ */
+static JdwpError handleOR_EnableCollection(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    // this is currently a no-op
+    return ERR_NONE;
+}
+
+/*
+ * Determine whether an object has been garbage collected.
+ */
+static JdwpError handleOR_IsCollected(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId objectId;
+
+    objectId = dvmReadObjectId(&buf);
+    ALOGV("  Req IsCollected(0x%llx)", objectId);
+
+    // TODO: currently returning false; must integrate with GC
+    expandBufAdd1(pReply, 0);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the string value in a string object.
+ */
+static JdwpError handleSR_Value(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId stringObject = dvmReadObjectId(&buf);
+    char* str = dvmDbgStringToUtf8(stringObject);
+
+    ALOGV("  Req for str %llx --> '%s'", stringObject, str);
+
+    expandBufAddUtf8String(pReply, (u1*) str);
+    free(str);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return a thread's name.
+ */
+static JdwpError handleTR_Name(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    ALOGV("  Req for name of thread 0x%llx", threadId);
+    char* name = dvmDbgGetThreadName(threadId);
+    if (name == NULL)
+        return ERR_INVALID_THREAD;
+
+    expandBufAddUtf8String(pReply, (u1*) name);
+    free(name);
+
+    return ERR_NONE;
+}
+
+/*
+ * Suspend the specified thread.
+ *
+ * It's supposed to remain suspended even if interpreted code wants to
+ * resume it; only the JDI is allowed to resume it.
+ */
+static JdwpError handleTR_Suspend(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        ALOGI("  Warning: ignoring request to suspend self");
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+    ALOGV("  Req to suspend thread 0x%llx", threadId);
+
+    dvmDbgSuspendThread(threadId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Resume the specified thread.
+ */
+static JdwpError handleTR_Resume(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        ALOGI("  Warning: ignoring request to resume self");
+        return ERR_NONE;
+    }
+    ALOGV("  Req to resume thread 0x%llx", threadId);
+
+    dvmDbgResumeThread(threadId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return status of specified thread.
+ */
+static JdwpError handleTR_Status(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    ALOGV("  Req for status of thread 0x%llx", threadId);
+
+    u4 threadStatus;
+    u4 suspendStatus;
+    if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
+        return ERR_INVALID_THREAD;
+
+    ALOGV("    --> %s, %s",
+        dvmJdwpThreadStatusStr((JdwpThreadStatus) threadStatus),
+        dvmJdwpSuspendStatusStr((JdwpSuspendStatus) suspendStatus));
+
+    expandBufAdd4BE(pReply, threadStatus);
+    expandBufAdd4BE(pReply, suspendStatus);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the thread group that the specified thread is a member of.
+ */
+static JdwpError handleTR_ThreadGroup(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    /* currently not handling these */
+    ObjectId threadGroupId = dvmDbgGetThreadGroup(threadId);
+    expandBufAddObjectId(pReply, threadGroupId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the current call stack of a suspended thread.
+ *
+ * If the thread isn't suspended, the error code isn't defined, but should
+ * be THREAD_NOT_SUSPENDED.
+ */
+static JdwpError handleTR_Frames(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    u4 startFrame = read4BE(&buf);
+    u4 length = read4BE(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    int frameCount = dvmDbgGetThreadFrameCount(threadId);
+
+    ALOGV("  Request for frames: threadId=%llx start=%d length=%d [count=%d]",
+        threadId, startFrame, length, frameCount);
+    if (frameCount <= 0)
+        return ERR_THREAD_NOT_SUSPENDED;    /* == 0 means 100% native */
+
+    if (length == (u4) -1)
+        length = frameCount;
+    assert((int) startFrame >= 0 && (int) startFrame < frameCount);
+    assert((int) (startFrame + length) <= frameCount);
+
+    u4 frames = length;
+    expandBufAdd4BE(pReply, frames);
+    for (u4 i = startFrame; i < (startFrame+length); i++) {
+        FrameId frameId;
+        JdwpLocation loc;
+
+        dvmDbgGetThreadFrame(threadId, i, &frameId, &loc);
+
+        expandBufAdd8BE(pReply, frameId);
+        dvmJdwpAddLocation(pReply, &loc);
+
+        LOGVV("    Frame %d: id=%llx loc={type=%d cls=%llx mth=%x loc=%llx}",
+            i, frameId, loc.typeTag, loc.classId, loc.methodId, loc.idx);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the #of frames on the specified thread, which must be suspended.
+ */
+static JdwpError handleTR_FrameCount(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    int frameCount = dvmDbgGetThreadFrameCount(threadId);
+    if (frameCount < 0)
+        return ERR_INVALID_THREAD;
+    expandBufAdd4BE(pReply, (u4)frameCount);
+
+    return ERR_NONE;
+}
+
+/*
+ * Get the monitor that the thread is waiting on.
+ */
+static JdwpError handleTR_CurrentContendedMonitor(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId;
+
+    threadId = dvmReadObjectId(&buf);
+
+    // TODO: create an Object to represent the monitor (we're currently
+    // just using a raw Monitor struct in the VM)
+
+    return ERR_NOT_IMPLEMENTED;
+}
+
+/*
+ * Return the suspend count for the specified thread.
+ *
+ * (The thread *might* still be running -- it might not have examined
+ * its suspend count recently.)
+ */
+static JdwpError handleTR_SuspendCount(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+
+    u4 suspendCount = dvmDbgGetThreadSuspendCount(threadId);
+    expandBufAdd4BE(pReply, suspendCount);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the name of a thread group.
+ *
+ * The Eclipse debugger recognizes "main" and "system" as special.
+ */
+static JdwpError handleTGR_Name(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadGroupId = dvmReadObjectId(&buf);
+    ALOGV("  Req for name of threadGroupId=0x%llx", threadGroupId);
+
+    char* name = dvmDbgGetThreadGroupName(threadGroupId);
+    if (name != NULL)
+        expandBufAddUtf8String(pReply, (u1*) name);
+    else {
+        expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
+        ALOGW("bad thread group ID");
+    }
+
+    free(name);
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the thread group -- if any -- that contains the specified
+ * thread group.
+ */
+static JdwpError handleTGR_Parent(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId groupId = dvmReadObjectId(&buf);
+
+    ObjectId parentGroup = dvmDbgGetThreadGroupParent(groupId);
+    expandBufAddObjectId(pReply, parentGroup);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the active threads and thread groups that are part of the
+ * specified thread group.
+ */
+static JdwpError handleTGR_Children(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadGroupId = dvmReadObjectId(&buf);
+    ALOGV("  Req for threads in threadGroupId=0x%llx", threadGroupId);
+
+    ObjectId* pThreadIds;
+    u4 threadCount;
+    dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    for (u4 i = 0; i < threadCount; i++)
+        expandBufAddObjectId(pReply, pThreadIds[i]);
+    free(pThreadIds);
+
+    /*
+     * TODO: finish support for child groups
+     *
+     * For now, just show that "main" is a child of "system".
+     */
+    if (threadGroupId == dvmDbgGetSystemThreadGroupId()) {
+        expandBufAdd4BE(pReply, 1);
+        expandBufAddObjectId(pReply, dvmDbgGetMainThreadGroupId());
+    } else {
+        expandBufAdd4BE(pReply, 0);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the #of components in the array.
+ */
+static JdwpError handleAR_Length(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    ALOGV("  Req for length of array 0x%llx", arrayId);
+
+    u4 arrayLength = dvmDbgGetArrayLength(arrayId);
+
+    ALOGV("    --> %d", arrayLength);
+
+    expandBufAdd4BE(pReply, arrayLength);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the values from an array.
+ */
+static JdwpError handleAR_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    u4 firstIndex = read4BE(&buf);
+    u4 length = read4BE(&buf);
+
+    u1 tag = dvmDbgGetArrayElementTag(arrayId);
+    ALOGV("  Req for array values 0x%llx first=%d len=%d (elem tag=%c)",
+        arrayId, firstIndex, length, tag);
+
+    expandBufAdd1(pReply, tag);
+    expandBufAdd4BE(pReply, length);
+
+    if (!dvmDbgOutputArray(arrayId, firstIndex, length, pReply))
+        return ERR_INVALID_LENGTH;
+
+    return ERR_NONE;
+}
+
+/*
+ * Set values in an array.
+ */
+static JdwpError handleAR_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId arrayId = dvmReadObjectId(&buf);
+    u4 firstIndex = read4BE(&buf);
+    u4 values = read4BE(&buf);
+
+    ALOGV("  Req to set array values 0x%llx first=%d count=%d",
+        arrayId, firstIndex, values);
+
+    if (!dvmDbgSetArrayElements(arrayId, firstIndex, values, buf))
+        return ERR_INVALID_LENGTH;
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the set of classes visible to a class loader.  All classes which
+ * have the class loader as a defining or initiating loader are returned.
+ */
+static JdwpError handleCLR_VisibleClasses(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId classLoaderObject;
+    u4 numClasses = 0;
+    RefTypeId* classRefBuf = NULL;
+    int i;
+
+    classLoaderObject = dvmReadObjectId(&buf);
+
+    dvmDbgGetVisibleClassList(classLoaderObject, &numClasses, &classRefBuf);
+
+    expandBufAdd4BE(pReply, numClasses);
+    for (i = 0; i < (int) numClasses; i++) {
+        u1 refTypeTag;
+
+        refTypeTag = dvmDbgGetClassObjectType(classRefBuf[i]);
+
+        expandBufAdd1(pReply, refTypeTag);
+        expandBufAddRefTypeId(pReply, classRefBuf[i]);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set an event trigger.
+ *
+ * Reply with a requestID.
+ */
+static JdwpError handleER_Set(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    const u1* origBuf = buf;
+
+    u1 eventKind = read1(&buf);
+    u1 suspendPolicy = read1(&buf);
+    u4 modifierCount = read4BE(&buf);
+
+    LOGVV("  Set(kind=%s(%u) suspend=%s(%u) mods=%u)",
+        dvmJdwpEventKindStr(eventKind), eventKind,
+        dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
+        modifierCount);
+
+    assert(modifierCount < 256);    /* reasonableness check */
+
+    JdwpEvent* pEvent = dvmJdwpEventAlloc(modifierCount);
+    pEvent->eventKind = static_cast<JdwpEventKind>(eventKind);
+    pEvent->suspendPolicy = static_cast<JdwpSuspendPolicy>(suspendPolicy);
+    pEvent->modCount = modifierCount;
+
+    /*
+     * Read modifiers.  Ordering may be significant (see explanation of Count
+     * mods in JDWP doc).
+     */
+    for (u4 idx = 0; idx < modifierCount; idx++) {
+        u1 modKind = read1(&buf);
+
+        pEvent->mods[idx].modKind = modKind;
+
+        switch (modKind) {
+        case MK_COUNT:          /* report once, when "--count" reaches 0 */
+            {
+                u4 count = read4BE(&buf);
+                LOGVV("    Count: %u", count);
+                if (count == 0)
+                    return ERR_INVALID_COUNT;
+                pEvent->mods[idx].count.count = count;
+            }
+            break;
+        case MK_CONDITIONAL:    /* conditional on expression) */
+            {
+                u4 exprId = read4BE(&buf);
+                LOGVV("    Conditional: %d", exprId);
+                pEvent->mods[idx].conditional.exprId = exprId;
+            }
+            break;
+        case MK_THREAD_ONLY:    /* only report events in specified thread */
+            {
+                ObjectId threadId = dvmReadObjectId(&buf);
+                LOGVV("    ThreadOnly: %llx", threadId);
+                pEvent->mods[idx].threadOnly.threadId = threadId;
+            }
+            break;
+        case MK_CLASS_ONLY:     /* for ClassPrepare, MethodEntry */
+            {
+                RefTypeId clazzId = dvmReadRefTypeId(&buf);
+                LOGVV("    ClassOnly: %llx (%s)",
+                    clazzId, dvmDbgGetClassDescriptor(clazzId));
+                pEvent->mods[idx].classOnly.refTypeId = clazzId;
+            }
+            break;
+        case MK_CLASS_MATCH:    /* restrict events to matching classes */
+            {
+                char* pattern;
+                size_t strLen;
+
+                pattern = readNewUtf8String(&buf, &strLen);
+                LOGVV("    ClassMatch: '%s'", pattern);
+                /* pattern is "java.foo.*", we want "java/foo/ *" */
+                pEvent->mods[idx].classMatch.classPattern =
+                    dvmDotToSlash(pattern);
+                free(pattern);
+            }
+            break;
+        case MK_CLASS_EXCLUDE:  /* restrict events to non-matching classes */
+            {
+                char* pattern;
+                size_t strLen;
+
+                pattern = readNewUtf8String(&buf, &strLen);
+                LOGVV("    ClassExclude: '%s'", pattern);
+                pEvent->mods[idx].classExclude.classPattern =
+                    dvmDotToSlash(pattern);
+                free(pattern);
+            }
+            break;
+        case MK_LOCATION_ONLY:  /* restrict certain events based on loc */
+            {
+                JdwpLocation loc;
+
+                jdwpReadLocation(&buf, &loc);
+                LOGVV("    LocationOnly: typeTag=%d classId=%llx methodId=%x idx=%llx",
+                    loc.typeTag, loc.classId, loc.methodId, loc.idx);
+                pEvent->mods[idx].locationOnly.loc = loc;
+            }
+            break;
+        case MK_EXCEPTION_ONLY: /* modifies EK_EXCEPTION events */
+            {
+                RefTypeId exceptionOrNull;      /* null == all exceptions */
+                u1 caught, uncaught;
+
+                exceptionOrNull = dvmReadRefTypeId(&buf);
+                caught = read1(&buf);
+                uncaught = read1(&buf);
+                LOGVV("    ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
+                    exceptionOrNull, (exceptionOrNull == 0) ? "null"
+                        : dvmDbgGetClassDescriptor(exceptionOrNull),
+                    caught, uncaught);
+
+                pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
+                pEvent->mods[idx].exceptionOnly.caught = caught;
+                pEvent->mods[idx].exceptionOnly.uncaught = uncaught;
+            }
+            break;
+        case MK_FIELD_ONLY:     /* for field access/mod events */
+            {
+                RefTypeId declaring = dvmReadRefTypeId(&buf);
+                FieldId fieldId = dvmReadFieldId(&buf);
+                LOGVV("    FieldOnly: %llx %x", declaring, fieldId);
+                pEvent->mods[idx].fieldOnly.refTypeId = declaring;
+                pEvent->mods[idx].fieldOnly.fieldId = fieldId;
+            }
+            break;
+        case MK_STEP:           /* for use with EK_SINGLE_STEP */
+            {
+                ObjectId threadId;
+                u4 size, depth;
+
+                threadId = dvmReadObjectId(&buf);
+                size = read4BE(&buf);
+                depth = read4BE(&buf);
+                LOGVV("    Step: thread=%llx size=%s depth=%s",
+                    threadId, dvmJdwpStepSizeStr(size),
+                    dvmJdwpStepDepthStr(depth));
+
+                pEvent->mods[idx].step.threadId = threadId;
+                pEvent->mods[idx].step.size = size;
+                pEvent->mods[idx].step.depth = depth;
+            }
+            break;
+        case MK_INSTANCE_ONLY:  /* report events related to a specific obj */
+            {
+                ObjectId instance = dvmReadObjectId(&buf);
+                LOGVV("    InstanceOnly: %llx", instance);
+                pEvent->mods[idx].instanceOnly.objectId = instance;
+            }
+            break;
+        default:
+            ALOGW("GLITCH: unsupported modKind=%d", modKind);
+            break;
+        }
+    }
+
+    /*
+     * Make sure we consumed all data.  It is possible that the remote side
+     * has sent us bad stuff, but for now we blame ourselves.
+     */
+    if (buf != origBuf + dataLen) {
+        ALOGW("GLITCH: dataLen is %d, we have consumed %d", dataLen,
+            (int) (buf - origBuf));
+    }
+
+    /*
+     * We reply with an integer "requestID".
+     */
+    u4 requestId = dvmJdwpNextEventSerial(state);
+    expandBufAdd4BE(pReply, requestId);
+
+    pEvent->requestId = requestId;
+
+    ALOGV("    --> event requestId=%#x", requestId);
+
+    /* add it to the list */
+    JdwpError err = dvmJdwpRegisterEvent(state, pEvent);
+    if (err != ERR_NONE) {
+        /* registration failed, probably because event is bogus */
+        dvmJdwpEventFree(pEvent);
+        ALOGW("WARNING: event request rejected");
+    }
+    return err;
+}
+
+/*
+ * Clear an event.  Failure to find an event with a matching ID is a no-op
+ * and does not return an error.
+ */
+static JdwpError handleER_Clear(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u1 eventKind;
+    eventKind = read1(&buf);
+    u4 requestId = read4BE(&buf);
+
+    ALOGV("  Req to clear eventKind=%d requestId=%#x", eventKind, requestId);
+
+    dvmJdwpUnregisterEventById(state, requestId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the values of arguments and local variables.
+ */
+static JdwpError handleSF_GetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+    u4 slots = read4BE(&buf);
+
+    ALOGV("  Req for %d slots in threadId=%llx frameId=%llx",
+        slots, threadId, frameId);
+
+    expandBufAdd4BE(pReply, slots);     /* "int values" */
+    for (u4 i = 0; i < slots; i++) {
+        u4 slot = read4BE(&buf);
+        u1 reqSigByte = read1(&buf);
+
+        ALOGV("    --> slot %d '%c'", slot, reqSigByte);
+
+        int width = dvmDbgGetTagWidth(reqSigByte);
+        u1* ptr = expandBufAddSpace(pReply, width+1);
+        dvmDbgGetLocalValue(threadId, frameId, slot, reqSigByte, ptr, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Set the values of arguments and local variables.
+ */
+static JdwpError handleSF_SetValues(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+    u4 slots = read4BE(&buf);
+
+    ALOGV("  Req to set %d slots in threadId=%llx frameId=%llx",
+        slots, threadId, frameId);
+
+    for (u4 i = 0; i < slots; i++) {
+        u4 slot = read4BE(&buf);
+        u1 sigByte = read1(&buf);
+        int width = dvmDbgGetTagWidth(sigByte);
+        u8 value = jdwpReadValue(&buf, width);
+
+        ALOGV("    --> slot %d '%c' %llx", slot, sigByte, value);
+        dvmDbgSetLocalValue(threadId, frameId, slot, sigByte, value, width);
+    }
+
+    return ERR_NONE;
+}
+
+/*
+ * Returns the value of "this" for the specified frame.
+ */
+static JdwpError handleSF_ThisObject(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    ObjectId threadId = dvmReadObjectId(&buf);
+    FrameId frameId = dvmReadFrameId(&buf);
+
+    ObjectId objectId;
+    if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
+        return ERR_INVALID_FRAMEID;
+
+    u1 objectTag = dvmDbgGetObjectTag(objectId);
+    ALOGV("  Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'",
+        threadId, frameId, objectId, dvmDbgGetObjectTypeName(objectId),
+        (char)objectTag);
+
+    expandBufAdd1(pReply, objectTag);
+    expandBufAddObjectId(pReply, objectId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Return the reference type reflected by this class object.
+ *
+ * This appears to be required because ReferenceTypeId values are NEVER
+ * reused, whereas ClassIds can be recycled like any other object.  (Either
+ * that, or I have no idea what this is for.)
+ */
+static JdwpError handleCOR_ReflectedType(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    RefTypeId classObjectId = dvmReadRefTypeId(&buf);
+
+    ALOGV("  Req for refTypeId for class=%llx (%s)",
+        classObjectId, dvmDbgGetClassDescriptor(classObjectId));
+
+    /* just hand the type back to them */
+    if (dvmDbgIsInterface(classObjectId))
+        expandBufAdd1(pReply, TT_INTERFACE);
+    else
+        expandBufAdd1(pReply, TT_CLASS);
+    expandBufAddRefTypeId(pReply, classObjectId);
+
+    return ERR_NONE;
+}
+
+/*
+ * Handle a DDM packet with a single chunk in it.
+ */
+static JdwpError handleDDM_Chunk(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    u1* replyBuf = NULL;
+    int replyLen = -1;
+
+    ALOGV("  Handling DDM packet (%.4s)", buf);
+
+    /*
+     * On first DDM packet, notify all handlers that DDM is running.
+     */
+    if (!state->ddmActive) {
+        state->ddmActive = true;
+        dvmDbgDdmConnected();
+    }
+
+    /*
+     * If they want to send something back, we copy it into the buffer.
+     * A no-copy approach would be nicer.
+     *
+     * TODO: consider altering the JDWP stuff to hold the packet header
+     * in a separate buffer.  That would allow us to writev() DDM traffic
+     * instead of copying it into the expanding buffer.  The reduction in
+     * heap requirements is probably more valuable than the efficiency.
+     */
+    if (dvmDbgDdmHandlePacket(buf, dataLen, &replyBuf, &replyLen)) {
+        memcpy(expandBufAddSpace(pReply, replyLen), replyBuf, replyLen);
+        free(replyBuf);
+    }
+    return ERR_NONE;
+}
+
+/*
+ * Handler map decl.
+ */
+typedef JdwpError (*JdwpRequestHandler)(JdwpState* state,
+    const u1* buf, int dataLen, ExpandBuf* reply);
+
+struct JdwpHandlerMap {
+    u1  cmdSet;
+    u1  cmd;
+    JdwpRequestHandler  func;
+    const char* descr;
+};
+
+/*
+ * Map commands to functions.
+ *
+ * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
+ * and 128-256 are vendor-defined.
+ */
+static const JdwpHandlerMap gHandlerMap[] = {
+    /* VirtualMachine command set (1) */
+    { 1,    1,  handleVM_Version,       "VirtualMachine.Version" },
+    { 1,    2,  handleVM_ClassesBySignature,
+                                        "VirtualMachine.ClassesBySignature" },
+    //1,    3,  VirtualMachine.AllClasses
+    { 1,    4,  handleVM_AllThreads,    "VirtualMachine.AllThreads" },
+    { 1,    5,  handleVM_TopLevelThreadGroups,
+                                        "VirtualMachine.TopLevelThreadGroups" },
+    { 1,    6,  handleVM_Dispose,       "VirtualMachine.Dispose" },
+    { 1,    7,  handleVM_IDSizes,       "VirtualMachine.IDSizes" },
+    { 1,    8,  handleVM_Suspend,       "VirtualMachine.Suspend" },
+    { 1,    9,  handleVM_Resume,        "VirtualMachine.Resume" },
+    { 1,    10, handleVM_Exit,          "VirtualMachine.Exit" },
+    { 1,    11, handleVM_CreateString,  "VirtualMachine.CreateString" },
+    { 1,    12, handleVM_Capabilities,  "VirtualMachine.Capabilities" },
+    { 1,    13, handleVM_ClassPaths,    "VirtualMachine.ClassPaths" },
+    { 1,    14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
+    //1,    15, HoldEvents
+    //1,    16, ReleaseEvents
+    { 1,    17, handleVM_CapabilitiesNew,
+                                        "VirtualMachine.CapabilitiesNew" },
+    //1,    18, RedefineClasses
+    //1,    19, SetDefaultStratum
+    { 1,    20, handleVM_AllClassesWithGeneric,
+                                        "VirtualMachine.AllClassesWithGeneric"},
+    //1,    21, InstanceCounts
+
+    /* ReferenceType command set (2) */
+    { 2,    1,  handleRT_Signature,     "ReferenceType.Signature" },
+    { 2,    2,  handleRT_ClassLoader,   "ReferenceType.ClassLoader" },
+    { 2,    3,  handleRT_Modifiers,     "ReferenceType.Modifiers" },
+    //2,    4,  Fields
+    //2,    5,  Methods
+    { 2,    6,  handleRT_GetValues,     "ReferenceType.GetValues" },
+    { 2,    7,  handleRT_SourceFile,    "ReferenceType.SourceFile" },
+    //2,    8,  NestedTypes
+    { 2,    9,  handleRT_Status,        "ReferenceType.Status" },
+    { 2,    10, handleRT_Interfaces,    "ReferenceType.Interfaces" },
+    { 2,    11, handleRT_ClassObject,   "ReferenceType.ClassObject" },
+    { 2,    12, handleRT_SourceDebugExtension,
+                                        "ReferenceType.SourceDebugExtension" },
+    { 2,    13, handleRT_SignatureWithGeneric,
+                                        "ReferenceType.SignatureWithGeneric" },
+    { 2,    14, handleRT_FieldsWithGeneric,
+                                        "ReferenceType.FieldsWithGeneric" },
+    { 2,    15, handleRT_MethodsWithGeneric,
+                                        "ReferenceType.MethodsWithGeneric" },
+    //2,    16, Instances
+    //2,    17, ClassFileVersion
+    //2,    18, ConstantPool
+
+    /* ClassType command set (3) */
+    { 3,    1,  handleCT_Superclass,    "ClassType.Superclass" },
+    { 3,    2,  handleCT_SetValues,     "ClassType.SetValues" },
+    { 3,    3,  handleCT_InvokeMethod,  "ClassType.InvokeMethod" },
+    { 3,    4,  handleCT_NewInstance,   "ClassType.NewInstance" },
+
+    /* ArrayType command set (4) */
+    { 4,    1,  handleAT_newInstance,   "ArrayType.NewInstance" },
+
+    /* InterfaceType command set (5) */
+
+    /* Method command set (6) */
+    { 6,    1,  handleM_LineTable,      "Method.LineTable" },
+    //6,    2,  VariableTable
+    //6,    3,  Bytecodes
+    //6,    4,  IsObsolete
+    { 6,    5,  handleM_VariableTableWithGeneric,
+                                        "Method.VariableTableWithGeneric" },
+
+    /* Field command set (8) */
+
+    /* ObjectReference command set (9) */
+    { 9,    1,  handleOR_ReferenceType, "ObjectReference.ReferenceType" },
+    { 9,    2,  handleOR_GetValues,     "ObjectReference.GetValues" },
+    { 9,    3,  handleOR_SetValues,     "ObjectReference.SetValues" },
+    //9,    4,  (not defined)
+    //9,    5,  MonitorInfo
+    { 9,    6,  handleOR_InvokeMethod,  "ObjectReference.InvokeMethod" },
+    { 9,    7,  handleOR_DisableCollection,
+                                        "ObjectReference.DisableCollection" },
+    { 9,    8,  handleOR_EnableCollection,
+                                        "ObjectReference.EnableCollection" },
+    { 9,    9,  handleOR_IsCollected,   "ObjectReference.IsCollected" },
+    //9,    10, ReferringObjects
+
+    /* StringReference command set (10) */
+    { 10,   1,  handleSR_Value,         "StringReference.Value" },
+
+    /* ThreadReference command set (11) */
+    { 11,   1,  handleTR_Name,          "ThreadReference.Name" },
+    { 11,   2,  handleTR_Suspend,       "ThreadReference.Suspend" },
+    { 11,   3,  handleTR_Resume,        "ThreadReference.Resume" },
+    { 11,   4,  handleTR_Status,        "ThreadReference.Status" },
+    { 11,   5,  handleTR_ThreadGroup,   "ThreadReference.ThreadGroup" },
+    { 11,   6,  handleTR_Frames,        "ThreadReference.Frames" },
+    { 11,   7,  handleTR_FrameCount,    "ThreadReference.FrameCount" },
+    //11,   8,  OwnedMonitors
+    { 11,   9,  handleTR_CurrentContendedMonitor,
+                                    "ThreadReference.CurrentContendedMonitor" },
+    //11,   10, Stop
+    //11,   11, Interrupt
+    { 11,   12, handleTR_SuspendCount,  "ThreadReference.SuspendCount" },
+    //11,   13, OwnedMonitorsStackDepthInfo
+    //11,   14, ForceEarlyReturn
+
+    /* ThreadGroupReference command set (12) */
+    { 12,   1,  handleTGR_Name,         "ThreadGroupReference.Name" },
+    { 12,   2,  handleTGR_Parent,       "ThreadGroupReference.Parent" },
+    { 12,   3,  handleTGR_Children,     "ThreadGroupReference.Children" },
+
+    /* ArrayReference command set (13) */
+    { 13,   1,  handleAR_Length,        "ArrayReference.Length" },
+    { 13,   2,  handleAR_GetValues,     "ArrayReference.GetValues" },
+    { 13,   3,  handleAR_SetValues,     "ArrayReference.SetValues" },
+
+    /* ClassLoaderReference command set (14) */
+    { 14,   1,  handleCLR_VisibleClasses,
+                                        "ClassLoaderReference.VisibleClasses" },
+
+    /* EventRequest command set (15) */
+    { 15,   1,  handleER_Set,           "EventRequest.Set" },
+    { 15,   2,  handleER_Clear,         "EventRequest.Clear" },
+    //15,   3,  ClearAllBreakpoints
+
+    /* StackFrame command set (16) */
+    { 16,   1,  handleSF_GetValues,     "StackFrame.GetValues" },
+    { 16,   2,  handleSF_SetValues,     "StackFrame.SetValues" },
+    { 16,   3,  handleSF_ThisObject,    "StackFrame.ThisObject" },
+    //16,   4,  PopFrames
+
+    /* ClassObjectReference command set (17) */
+    { 17,   1,  handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },
+
+    /* Event command set (64) */
+    //64,  100, Composite   <-- sent from VM to debugger, never received by VM
+
+    { 199,  1,  handleDDM_Chunk,        "DDM.Chunk" },
+};
+
+
+/*
+ * Process a request from the debugger.
+ *
+ * On entry, the JDWP thread is in VMWAIT.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+    const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+    JdwpError result = ERR_NONE;
+    int i, respLen;
+
+    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+        /*
+         * Activity from a debugger, not merely ddms.  Mark us as having an
+         * active debugger session, and zero out the last-activity timestamp
+         * so waitForDebugger() doesn't return if we stall for a bit here.
+         */
+        dvmDbgActive();
+        dvmQuasiAtomicSwap64(0, &state->lastActivityWhen);
+    }
+
+    /*
+     * If a debugger event has fired in another thread, wait until the
+     * initiating thread has suspended itself before processing messages
+     * from the debugger.  Otherwise we (the JDWP thread) could be told to
+     * resume the thread before it has suspended.
+     *
+     * We call with an argument of zero to wait for the current event
+     * thread to finish, and then clear the block.  Depending on the thread
+     * suspend policy, this may allow events in other threads to fire,
+     * but those events have no bearing on what the debugger has sent us
+     * in the current request.
+     *
+     * Note that we MUST clear the event token before waking the event
+     * thread up, or risk waiting for the thread to suspend after we've
+     * told it to resume.
+     */
+    dvmJdwpSetWaitForEventThread(state, 0);
+
+    /*
+     * Tell the VM that we're running and shouldn't be interrupted by GC.
+     * Do this after anything that can stall indefinitely.
+     */
+    dvmDbgThreadRunning();
+
+    expandBufAddSpace(pReply, kJDWPHeaderLen);
+
+    for (i = 0; i < (int) NELEM(gHandlerMap); i++) {
+        if (gHandlerMap[i].cmdSet == pHeader->cmdSet &&
+            gHandlerMap[i].cmd == pHeader->cmd)
+        {
+            ALOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)",
+                gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
+                dataLen, pHeader->id);
+            result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
+            break;
+        }
+    }
+    if (i == NELEM(gHandlerMap)) {
+        ALOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)",
+            pHeader->cmdSet, pHeader->cmd, dataLen, pHeader->id);
+        if (dataLen > 0)
+            dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+        assert(!"command not implemented");      // make it *really* obvious
+        result = ERR_NOT_IMPLEMENTED;
+    }
+
+    /*
+     * Set up the reply header.
+     *
+     * If we encountered an error, only send the header back.
+     */
+    u1* replyBuf = expandBufGetBuffer(pReply);
+    set4BE(replyBuf + 4, pHeader->id);
+    set1(replyBuf + 8, kJDWPFlagReply);
+    set2BE(replyBuf + 9, result);
+    if (result == ERR_NONE)
+        set4BE(replyBuf + 0, expandBufGetLength(pReply));
+    else
+        set4BE(replyBuf + 0, kJDWPHeaderLen);
+
+    respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
+    IF_ALOG(LOG_VERBOSE, LOG_TAG) {
+        ALOGV("reply: dataLen=%d err=%s(%d)%s", respLen,
+            dvmJdwpErrorStr(result), result,
+            result != ERR_NONE ? " **FAILED**" : "");
+        if (respLen > 0)
+            dvmPrintHexDumpDbg(expandBufGetBuffer(pReply) + kJDWPHeaderLen,
+                respLen, LOG_TAG);
+    }
+
+    /*
+     * Update last-activity timestamp.  We really only need this during
+     * the initial setup.  Only update if this is a non-DDMS packet.
+     */
+    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+        dvmQuasiAtomicSwap64(dvmJdwpGetNowMsec(), &state->lastActivityWhen);
+    }
+
+    /* tell the VM that GC is okay again */
+    dvmDbgThreadWaiting();
+}
diff --git a/vm/jdwp/JdwpHandler.h b/vm/jdwp/JdwpHandler.h
new file mode 100644
index 0000000..6381270
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Handle requests.
+ */
+#ifndef DALVIK_JDWP_JDWPHANDLER_H_
+#define DALVIK_JDWP_JDWPHANDLER_H_
+
+#include "Common.h"
+#include "ExpandBuf.h"
+
+/*
+ * JDWP message header for a request.
+ */
+struct JdwpReqHeader {
+    u4  length;
+    u4  id;
+    u1  cmdSet;
+    u1  cmd;
+};
+
+/*
+ * Process a request from the debugger.
+ *
+ * "buf" points past the header, to the content of the message.  "dataLen"
+ * can therefore be zero.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+    const u1* buf, int dataLen, ExpandBuf* pReply);
+
+/* helper function */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc);
+
+#endif  // DALVIK_JDWP_JDWPHANDLER_H_
diff --git a/vm/jdwp/JdwpMain.cpp b/vm/jdwp/JdwpMain.cpp
new file mode 100644
index 0000000..55e278d
--- /dev/null
+++ b/vm/jdwp/JdwpMain.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * JDWP initialization.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "Dalvik.h"
+#include "Atomic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+
+static void* jdwpThreadStart(void* arg);
+
+/*
+ * JdwpNetStateBase class implementation
+ */
+JdwpNetStateBase::JdwpNetStateBase()
+{
+    clientSock = -1;
+    dvmDbgInitMutex(&socketLock);
+}
+
+/*
+ * Write a packet. Grabs a mutex to assure atomicity.
+ */
+ssize_t JdwpNetStateBase::writePacket(ExpandBuf* pReply)
+{
+    dvmDbgLockMutex(&socketLock);
+    ssize_t cc = TEMP_FAILURE_RETRY(write(clientSock, expandBufGetBuffer(pReply),
+                                          expandBufGetLength(pReply)));
+    dvmDbgUnlockMutex(&socketLock);
+
+    return cc;
+}
+
+/*
+ * Write a buffered packet. Grabs a mutex to assure atomicity.
+ */
+ssize_t JdwpNetStateBase::writeBufferedPacket(const struct iovec* iov,
+    int iovcnt)
+{
+    dvmDbgLockMutex(&socketLock);
+    ssize_t actual = TEMP_FAILURE_RETRY(writev(clientSock, iov, iovcnt));
+    dvmDbgUnlockMutex(&socketLock);
+
+    return actual;
+}
+
+/*
+ * Initialize JDWP.
+ *
+ * Does not return until JDWP thread is running, but may return before
+ * the thread is accepting network connections.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
+{
+    JdwpState* state = NULL;
+
+    /* comment this out when debugging JDWP itself */
+    android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);
+
+    state = (JdwpState*) calloc(1, sizeof(JdwpState));
+
+    state->params = *pParams;
+
+    state->requestSerial = 0x10000000;
+    state->eventSerial = 0x20000000;
+    dvmDbgInitMutex(&state->threadStartLock);
+    dvmDbgInitMutex(&state->attachLock);
+    dvmDbgInitMutex(&state->serialLock);
+    dvmDbgInitMutex(&state->eventLock);
+    state->eventThreadId = 0;
+    dvmDbgInitMutex(&state->eventThreadLock);
+    dvmDbgInitCond(&state->threadStartCond);
+    dvmDbgInitCond(&state->attachCond);
+    dvmDbgInitCond(&state->eventThreadCond);
+
+    switch (pParams->transport) {
+    case kJdwpTransportSocket:
+        // ALOGD("prepping for JDWP over TCP");
+        state->transport = dvmJdwpSocketTransport();
+        break;
+    case kJdwpTransportAndroidAdb:
+        // ALOGD("prepping for JDWP over ADB");
+        state->transport = dvmJdwpAndroidAdbTransport();
+        /* TODO */
+        break;
+    default:
+        ALOGE("Unknown transport %d", pParams->transport);
+        assert(false);
+        goto fail;
+    }
+
+    if (!dvmJdwpNetStartup(state, pParams))
+        goto fail;
+
+    /*
+     * Grab a mutex or two before starting the thread.  This ensures they
+     * won't signal the cond var before we're waiting.
+     */
+    dvmDbgLockMutex(&state->threadStartLock);
+    if (pParams->suspend)
+        dvmDbgLockMutex(&state->attachLock);
+
+    /*
+     * We have bound to a port, or are trying to connect outbound to a
+     * debugger.  Create the JDWP thread and let it continue the mission.
+     */
+    if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
+            jdwpThreadStart, state))
+    {
+        /* state is getting tossed, but unlock these anyway for cleanliness */
+        dvmDbgUnlockMutex(&state->threadStartLock);
+        if (pParams->suspend)
+            dvmDbgUnlockMutex(&state->attachLock);
+        goto fail;
+    }
+
+    /*
+     * Wait until the thread finishes basic initialization.
+     * TODO: cond vars should be waited upon in a loop
+     */
+    dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
+    dvmDbgUnlockMutex(&state->threadStartLock);
+
+
+    /*
+     * For suspend=y, wait for the debugger to connect to us or for us to
+     * connect to the debugger.
+     *
+     * The JDWP thread will signal us when it connects successfully or
+     * times out (for timeout=xxx), so we have to check to see what happened
+     * when we wake up.
+     */
+    if (pParams->suspend) {
+        dvmChangeStatus(NULL, THREAD_VMWAIT);
+        dvmDbgCondWait(&state->attachCond, &state->attachLock);
+        dvmDbgUnlockMutex(&state->attachLock);
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+
+        if (!dvmJdwpIsActive(state)) {
+            ALOGE("JDWP connection failed");
+            goto fail;
+        }
+
+        ALOGI("JDWP connected");
+
+        /*
+         * Ordinarily we would pause briefly to allow the debugger to set
+         * breakpoints and so on, but for "suspend=y" the VM init code will
+         * pause the VM when it sends the VM_START message.
+         */
+    }
+
+    return state;
+
+fail:
+    dvmJdwpShutdown(state);     // frees state
+    return NULL;
+}
+
+/*
+ * Reset all session-related state.  There should not be an active connection
+ * to the client at this point.  The rest of the VM still thinks there is
+ * a debugger attached.
+ *
+ * This includes freeing up the debugger event list.
+ */
+void dvmJdwpResetState(JdwpState* state)
+{
+    /* could reset the serial numbers, but no need to */
+
+    dvmJdwpUnregisterAll(state);
+    assert(state->eventList == NULL);
+
+    /*
+     * Should not have one of these in progress.  If the debugger went away
+     * mid-request, though, we could see this.
+     */
+    if (state->eventThreadId != 0) {
+        ALOGW("WARNING: resetting state while event in progress");
+        assert(false);
+    }
+}
+
+/*
+ * Tell the JDWP thread to shut down.  Frees "state".
+ */
+void dvmJdwpShutdown(JdwpState* state)
+{
+    void* threadReturn;
+
+    if (state == NULL)
+        return;
+
+    if (dvmJdwpIsTransportDefined(state)) {
+        if (dvmJdwpIsConnected(state))
+            dvmJdwpPostVMDeath(state);
+
+        /*
+         * Close down the network to inspire the thread to halt.
+         */
+        if (gDvm.verboseShutdown)
+            ALOGD("JDWP shutting down net...");
+        dvmJdwpNetShutdown(state);
+
+        if (state->debugThreadStarted) {
+            state->run = false;
+            if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
+                ALOGW("JDWP thread join failed");
+            }
+        }
+
+        if (gDvm.verboseShutdown)
+            ALOGD("JDWP freeing netstate...");
+        dvmJdwpNetFree(state);
+        state->netState = NULL;
+    }
+    assert(state->netState == NULL);
+
+    dvmJdwpResetState(state);
+    free(state);
+}
+
+/*
+ * Are we talking to a debugger?
+ */
+bool dvmJdwpIsActive(JdwpState* state)
+{
+    return dvmJdwpIsConnected(state);
+}
+
+/*
+ * Entry point for JDWP thread.  The thread was created through the VM
+ * mechanisms, so there is a java/lang/Thread associated with us.
+ */
+static void* jdwpThreadStart(void* arg)
+{
+    JdwpState* state = (JdwpState*) arg;
+
+    ALOGV("JDWP: thread running");
+
+    /*
+     * Finish initializing "state", then notify the creating thread that
+     * we're running.
+     */
+    state->debugThreadHandle = dvmThreadSelf()->handle;
+    state->run = true;
+    android_atomic_release_store(true, &state->debugThreadStarted);
+
+    dvmDbgLockMutex(&state->threadStartLock);
+    dvmDbgCondBroadcast(&state->threadStartCond);
+    dvmDbgUnlockMutex(&state->threadStartLock);
+
+    /* set the thread state to VMWAIT so GCs don't wait for us */
+    dvmDbgThreadWaiting();
+
+    /*
+     * Loop forever if we're in server mode, processing connections.  In
+     * non-server mode, we bail out of the thread when the debugger drops
+     * us.
+     *
+     * We broadcast a notification when a debugger attaches, after we
+     * successfully process the handshake.
+     */
+    while (state->run) {
+        bool first;
+
+        if (state->params.server) {
+            /*
+             * Block forever, waiting for a connection.  To support the
+             * "timeout=xxx" option we'll need to tweak this.
+             */
+            if (!dvmJdwpAcceptConnection(state))
+                break;
+        } else {
+            /*
+             * If we're not acting as a server, we need to connect out to the
+             * debugger.  To support the "timeout=xxx" option we need to
+             * have a timeout if the handshake reply isn't received in a
+             * reasonable amount of time.
+             */
+            if (!dvmJdwpEstablishConnection(state)) {
+                /* wake anybody who was waiting for us to succeed */
+                dvmDbgLockMutex(&state->attachLock);
+                dvmDbgCondBroadcast(&state->attachCond);
+                dvmDbgUnlockMutex(&state->attachLock);
+                break;
+            }
+        }
+
+        /* prep debug code to handle the new connection */
+        dvmDbgConnected();
+
+        /* process requests until the debugger drops */
+        first = true;
+        while (true) {
+            // sanity check -- shouldn't happen?
+            if (dvmThreadSelf()->status != THREAD_VMWAIT) {
+                ALOGE("JDWP thread no longer in VMWAIT (now %d); resetting",
+                    dvmThreadSelf()->status);
+                dvmDbgThreadWaiting();
+            }
+
+            if (!dvmJdwpProcessIncoming(state))     /* blocking read */
+                break;
+
+            if (first && !dvmJdwpAwaitingHandshake(state)) {
+                /* handshake worked, tell the interpreter that we're active */
+                first = false;
+
+                /* set thread ID; requires object registry to be active */
+                state->debugThreadId = dvmDbgGetThreadSelfId();
+
+                /* wake anybody who's waiting for us */
+                dvmDbgLockMutex(&state->attachLock);
+                dvmDbgCondBroadcast(&state->attachCond);
+                dvmDbgUnlockMutex(&state->attachLock);
+            }
+        }
+
+        dvmJdwpCloseConnection(state);
+
+        if (state->ddmActive) {
+            state->ddmActive = false;
+
+            /* broadcast the disconnect; must be in RUNNING state */
+            dvmDbgThreadRunning();
+            dvmDbgDdmDisconnected();
+            dvmDbgThreadWaiting();
+        }
+
+        /* release session state, e.g. remove breakpoint instructions */
+        dvmJdwpResetState(state);
+
+        /* tell the interpreter that the debugger is no longer around */
+        dvmDbgDisconnected();
+
+        /* if we had threads suspended, resume them now */
+        dvmUndoDebuggerSuspensions();
+
+        /* if we connected out, this was a one-shot deal */
+        if (!state->params.server)
+            state->run = false;
+    }
+
+    /* back to running, for thread shutdown */
+    dvmDbgThreadRunning();
+
+    ALOGV("JDWP: thread exiting");
+    return NULL;
+}
+
+
+/*
+ * Return the thread handle, or (pthread_t)0 if the debugger isn't running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state)
+{
+    if (state == NULL)
+        return 0;
+
+    return state->debugThreadHandle;
+}
+
+
+/*
+ * Support routines for waitForDebugger().
+ *
+ * We can't have a trivial "waitForDebugger" function that returns the
+ * instant the debugger connects, because we run the risk of executing code
+ * before the debugger has had a chance to configure breakpoints or issue
+ * suspend calls.  It would be nice to just sit in the suspended state, but
+ * most debuggers don't expect any threads to be suspended when they attach.
+ *
+ * There's no JDWP event we can post to tell the debugger, "we've stopped,
+ * and we like it that way".  We could send a fake breakpoint, which should
+ * cause the debugger to immediately send a resume, but the debugger might
+ * send the resume immediately or might throw an exception of its own upon
+ * receiving a breakpoint event that it didn't ask for.
+ *
+ * What we really want is a "wait until the debugger is done configuring
+ * stuff" event.  We can approximate this with a "wait until the debugger
+ * has been idle for a brief period".
+ */
+
+/*
+ * Get a notion of the current time, in milliseconds.
+ */
+s8 dvmJdwpGetNowMsec()
+{
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000LL + now.tv_nsec / 1000000LL;
+#else
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return now.tv_sec * 1000LL + now.tv_usec / 1000LL;
+#endif
+}
+
+/*
+ * Return the time, in milliseconds, since the last debugger activity.
+ *
+ * Returns -1 if no debugger is attached, or 0 if we're in the middle of
+ * processing a debugger request.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state)
+{
+    if (!gDvm.debuggerActive) {
+        ALOGD("dvmJdwpLastDebuggerActivity: no active debugger");
+        return -1;
+    }
+
+    s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
+
+    /* initializing or in the middle of something? */
+    if (last == 0) {
+        ALOGV("+++ last=busy");
+        return 0;
+    }
+
+    /* now get the current time */
+    s8 now = dvmJdwpGetNowMsec();
+    assert(now >= last);
+
+    ALOGV("+++ debugger interval=%lld", now - last);
+    return now - last;
+}
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
new file mode 100644
index 0000000..67a953f
--- /dev/null
+++ b/vm/jdwp/JdwpPriv.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * JDWP internal interfaces.
+ */
+#ifndef DALVIK_JDWP_JDWPPRIV_H_
+#define DALVIK_JDWP_JDWPPRIV_H_
+
+#define LOG_TAG "jdwp"
+
+#include "jdwp/Jdwp.h"
+#include "jdwp/JdwpEvent.h"
+#include "Debugger.h"
+
+#include <pthread.h>
+#include <sys/uio.h>
+
+/*
+ * JDWP constants.
+ */
+#define kJDWPHeaderLen  11
+#define kJDWPFlagReply  0x80
+
+/* DDM support */
+#define kJDWPDdmCmdSet  199     /* 0xc7, or 'G'+128 */
+#define kJDWPDdmCmd     1
+
+
+/*
+ * Transport-specific network status.
+ */
+struct JdwpNetState;
+struct JdwpState;
+
+/*
+ * Transport functions.
+ */
+struct JdwpTransport {
+    bool (*startup)(struct JdwpState* state, const JdwpStartupParams* pParams);
+    bool (*accept)(struct JdwpState* state);
+    bool (*establish)(struct JdwpState* state);
+    void (*close)(struct JdwpState* state);
+    void (*shutdown)(struct JdwpState* state);
+    void (*free)(struct JdwpState* state);
+    bool (*isConnected)(struct JdwpState* state);
+    bool (*awaitingHandshake)(struct JdwpState* state);
+    bool (*processIncoming)(struct JdwpState* state);
+    bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
+    bool (*sendBufferedRequest)(struct JdwpState* state,
+        const struct iovec* iov, int iovcnt);
+};
+
+const JdwpTransport* dvmJdwpSocketTransport();
+const JdwpTransport* dvmJdwpAndroidAdbTransport();
+
+
+/*
+ * State for JDWP functions.
+ */
+struct JdwpState {
+    JdwpStartupParams   params;
+
+    /* wait for creation of the JDWP thread */
+    pthread_mutex_t threadStartLock;
+    pthread_cond_t  threadStartCond;
+
+    int             debugThreadStarted;
+    pthread_t       debugThreadHandle;
+    ObjectId        debugThreadId;
+    bool            run;
+
+    const JdwpTransport*    transport;
+    JdwpNetState*   netState;
+
+    /* for wait-for-debugger */
+    pthread_mutex_t attachLock;
+    pthread_cond_t  attachCond;
+
+    /* time of last debugger activity, in milliseconds */
+    s8              lastActivityWhen;
+
+    /* global counters and a mutex to protect them */
+    u4              requestSerial;
+    u4              eventSerial;
+    pthread_mutex_t serialLock;
+
+    /*
+     * Events requested by the debugger (breakpoints, class prep, etc).
+     */
+    int             numEvents;      /* #of elements in eventList */
+    JdwpEvent*      eventList;      /* linked list of events */
+    pthread_mutex_t eventLock;      /* guards numEvents/eventList */
+
+    /*
+     * Synchronize suspension of event thread (to avoid receiving "resume"
+     * events before the thread has finished suspending itself).
+     */
+    pthread_mutex_t eventThreadLock;
+    pthread_cond_t  eventThreadCond;
+    ObjectId        eventThreadId;
+
+    /*
+     * DDM support.
+     */
+    bool            ddmActive;
+};
+
+/*
+ * Base class for JdwpNetState
+ */
+class JdwpNetStateBase {
+public:
+    int             clientSock;     /* active connection to debugger */
+
+    JdwpNetStateBase();
+    ssize_t writePacket(ExpandBuf* pReply);
+    ssize_t writeBufferedPacket(const struct iovec* iov, int iovcnt);
+
+private:
+    pthread_mutex_t socketLock;     /* socket synchronization */
+};
+
+
+/* reset all session-specific data */
+void dvmJdwpResetState(JdwpState* state);
+
+/* atomic ops to get next serial number */
+u4 dvmJdwpNextRequestSerial(JdwpState* state);
+u4 dvmJdwpNextEventSerial(JdwpState* state);
+
+/* get current time, in msec */
+s8 dvmJdwpGetNowMsec(void);
+
+
+/*
+ * Transport functions.
+ */
+INLINE bool dvmJdwpNetStartup(JdwpState* state,
+    const JdwpStartupParams* pParams)
+{
+    return (*state->transport->startup)(state, pParams);
+}
+INLINE bool dvmJdwpAcceptConnection(JdwpState* state) {
+    return (*state->transport->accept)(state);
+}
+INLINE bool dvmJdwpEstablishConnection(JdwpState* state) {
+    return (*state->transport->establish)(state);
+}
+INLINE void dvmJdwpCloseConnection(JdwpState* state) {
+    (*state->transport->close)(state);
+}
+INLINE void dvmJdwpNetShutdown(JdwpState* state) {
+    (*state->transport->shutdown)(state);
+}
+INLINE void dvmJdwpNetFree(JdwpState* state) {
+    (*state->transport->free)(state);
+}
+INLINE bool dvmJdwpIsTransportDefined(JdwpState* state) {
+    return state != NULL && state->transport != NULL;
+}
+INLINE bool dvmJdwpIsConnected(JdwpState* state) {
+    return state != NULL && (*state->transport->isConnected)(state);
+}
+INLINE bool dvmJdwpAwaitingHandshake(JdwpState* state) {
+    return (*state->transport->awaitingHandshake)(state);
+}
+INLINE bool dvmJdwpProcessIncoming(JdwpState* state) {
+    return (*state->transport->processIncoming)(state);
+}
+INLINE bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq) {
+    return (*state->transport->sendRequest)(state, pReq);
+}
+INLINE bool dvmJdwpSendBufferedRequest(JdwpState* state,
+    const struct iovec* iov, int iovcnt)
+{
+    return (*state->transport->sendBufferedRequest)(state, iov, iovcnt);
+}
+
+#endif  // DALVIK_JDWP_JDWPPRIV_H_
diff --git a/vm/jdwp/JdwpSocket.cpp b/vm/jdwp/JdwpSocket.cpp
new file mode 100644
index 0000000..eaea607
--- /dev/null
+++ b/vm/jdwp/JdwpSocket.cpp
@@ -0,0 +1,910 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * JDWP TCP socket network code.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "Bits.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define kBasePort           8000
+#define kMaxPort            8040
+
+#define kInputBufferSize    8192
+
+#define kMagicHandshake     "JDWP-Handshake"
+#define kMagicHandshakeLen  (sizeof(kMagicHandshake)-1)
+
+// fwd
+static void netShutdown(JdwpNetState* state);
+static void netFree(JdwpNetState* state);
+
+
+/*
+ * JDWP network state.
+ *
+ * We only talk to one debugger at a time.
+ */
+struct JdwpNetState : public JdwpNetStateBase {
+    short   listenPort;
+    int     listenSock;         /* listen for connection from debugger */
+    int     wakePipe[2];        /* break out of select */
+
+    struct in_addr remoteAddr;
+    unsigned short remotePort;
+
+    bool    awaitingHandshake;  /* waiting for "JDWP-Handshake" */
+
+    /* pending data from the network; would be more efficient as circular buf */
+    unsigned char  inputBuffer[kInputBufferSize];
+    int     inputCount;
+
+    JdwpNetState()
+    {
+        listenPort  = 0;
+        listenSock  = -1;
+        wakePipe[0] = -1;
+        wakePipe[1] = -1;
+
+        awaitingHandshake = false;
+
+        inputCount = 0;
+    }
+};
+
+static JdwpNetState* netStartup(short port);
+
+/*
+ * Set up some stuff for transport=dt_socket.
+ */
+static bool prepareSocket(JdwpState* state, const JdwpStartupParams* pParams)
+{
+    unsigned short port;
+
+    if (pParams->server) {
+        if (pParams->port != 0) {
+            /* try only the specified port */
+            port = pParams->port;
+            state->netState = netStartup(port);
+        } else {
+            /* scan through a range of ports, binding to the first available */
+            for (port = kBasePort; port <= kMaxPort; port++) {
+                state->netState = netStartup(port);
+                if (state->netState != NULL)
+                    break;
+            }
+        }
+        if (state->netState == NULL) {
+            ALOGE("JDWP net startup failed (req port=%d)", pParams->port);
+            return false;
+        }
+    } else {
+        port = pParams->port;   // used in a debug msg later
+        state->netState = netStartup(-1);
+    }
+
+    if (pParams->suspend)
+        ALOGI("JDWP will wait for debugger on port %d", port);
+    else
+        ALOGD("JDWP will %s on port %d",
+            pParams->server ? "listen" : "connect", port);
+
+    return true;
+}
+
+
+/*
+ * Are we still waiting for the handshake string?
+ */
+static bool awaitingHandshake(JdwpState* state)
+{
+    return state->netState->awaitingHandshake;
+}
+
+/*
+ * Initialize JDWP stuff.
+ *
+ * Allocates a new state structure.  If "port" is non-negative, this also
+ * tries to bind to a listen port.  If "port" is less than zero, we assume
+ * we're preparing for an outbound connection, and return without binding
+ * to anything.
+ *
+ * This may be called several times if we're probing for a port.
+ *
+ * Returns 0 on success.
+ */
+static JdwpNetState* netStartup(short port)
+{
+    int one = 1;
+    JdwpNetState* netState = new JdwpNetState;
+
+    if (port < 0)
+        return netState;
+
+    assert(port != 0);
+
+    netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (netState->listenSock < 0) {
+        ALOGE("Socket create failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    /* allow immediate re-use */
+    if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
+            sizeof(one)) < 0)
+    {
+        ALOGE("setsockopt(SO_REUSEADDR) failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    addr.addrInet.sin_family = AF_INET;
+    addr.addrInet.sin_port = htons(port);
+    inet_aton("127.0.0.1", &addr.addrInet.sin_addr);
+
+    if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) {
+        ALOGV("attempt to bind to port %u failed: %s", port, strerror(errno));
+        goto fail;
+    }
+
+    netState->listenPort = port;
+    LOGVV("+++ bound to port %d", netState->listenPort);
+
+    if (listen(netState->listenSock, 5) != 0) {
+        ALOGE("Listen failed: %s", strerror(errno));
+        goto fail;
+    }
+
+    return netState;
+
+fail:
+    netShutdown(netState);
+    netFree(netState);
+    return NULL;
+}
+
+/*
+ * Shut down JDWP listener.  Don't free state.
+ *
+ * Note that "netState" may be partially initialized if "startup" failed.
+ *
+ * This may be called from a non-JDWP thread as part of shutting the
+ * JDWP thread down.
+ *
+ * (This is currently called several times during startup as we probe
+ * for an open port.)
+ */
+static void netShutdown(JdwpNetState* netState)
+{
+    if (netState == NULL)
+        return;
+
+    int listenSock = netState->listenSock;
+    int clientSock = netState->clientSock;
+
+    /* clear these out so it doesn't wake up and try to reuse them */
+    netState->listenSock = netState->clientSock = -1;
+
+    /* "shutdown" dislodges blocking read() and accept() calls */
+    if (listenSock >= 0) {
+        shutdown(listenSock, SHUT_RDWR);
+        close(listenSock);
+    }
+    if (clientSock >= 0) {
+        shutdown(clientSock, SHUT_RDWR);
+        close(clientSock);
+    }
+
+    /* if we might be sitting in select, kick us loose */
+    if (netState->wakePipe[1] >= 0) {
+        ALOGV("+++ writing to wakePipe");
+        TEMP_FAILURE_RETRY(write(netState->wakePipe[1], "", 1));
+    }
+}
+static void netShutdownExtern(JdwpState* state)
+{
+    netShutdown(state->netState);
+}
+
+/*
+ * Free JDWP state.
+ *
+ * Call this after shutting the network down with netShutdown().
+ */
+static void netFree(JdwpNetState* netState)
+{
+    if (netState == NULL)
+        return;
+    assert(netState->listenSock == -1);
+    assert(netState->clientSock == -1);
+
+    if (netState->wakePipe[0] >= 0) {
+        close(netState->wakePipe[0]);
+        netState->wakePipe[0] = -1;
+    }
+    if (netState->wakePipe[1] >= 0) {
+        close(netState->wakePipe[1]);
+        netState->wakePipe[1] = -1;
+    }
+
+    delete netState;
+}
+static void netFreeExtern(JdwpState* state)
+{
+    netFree(state->netState);
+}
+
+/*
+ * Returns "true" if we're connected to a debugger.
+ */
+static bool isConnected(JdwpState* state)
+{
+    return (state->netState != NULL &&
+            state->netState->clientSock >= 0);
+}
+
+/*
+ * Returns "true" if the fd is ready, "false" if not.
+ */
+#if 0
+static bool isFdReadable(int sock)
+{
+    fd_set readfds;
+    struct timeval tv;
+    int count;
+
+    FD_ZERO(&readfds);
+    FD_SET(sock, &readfds);
+
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    count = select(sock+1, &readfds, NULL, NULL, &tv);
+    if (count <= 0)
+        return false;
+
+    if (FD_ISSET(sock, &readfds))   /* make sure it's our fd */
+        return true;
+
+    ALOGE("WEIRD: odd behavior in select (count=%d)", count);
+    return false;
+}
+#endif
+
+#if 0
+/*
+ * Check to see if we have a pending connection from the debugger.
+ *
+ * Returns true on success (meaning a connection is available).
+ */
+static bool checkConnection(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+
+    assert(netState->listenSock >= 0);
+    /* not expecting to be called when debugger is actively connected */
+    assert(netState->clientSock < 0);
+
+    if (!isFdReadable(netState->listenSock))
+        return false;
+    return true;
+}
+#endif
+
+/*
+ * Disable the TCP Nagle algorithm, which delays transmission of outbound
+ * packets until the previous transmissions have been acked.  JDWP does a
+ * lot of back-and-forth with small packets, so this may help.
+ */
+static int setNoDelay(int fd)
+{
+    int cc, on = 1;
+
+    cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+    assert(cc == 0);
+    return cc;
+}
+
+/*
+ * Accept a connection.  This will block waiting for somebody to show up.
+ * If that's not desirable, use checkConnection() to make sure something
+ * is pending.
+ */
+static bool acceptConnection(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    socklen_t addrlen;
+    int sock;
+
+    if (netState->listenSock < 0)
+        return false;       /* you're not listening! */
+
+    assert(netState->clientSock < 0);      /* must not already be talking */
+
+    addrlen = sizeof(addr);
+    do {
+        sock = accept(netState->listenSock, &addr.addrPlain, &addrlen);
+        if (sock < 0 && errno != EINTR) {
+            // When we call shutdown() on the socket, accept() returns with
+            // EINVAL.  Don't gripe about it.
+            if (errno == EINVAL)
+                LOGVV("accept failed: %s", strerror(errno));
+            else
+                ALOGE("accept failed: %s", strerror(errno));
+            return false;
+        }
+    } while (sock < 0);
+
+    netState->remoteAddr = addr.addrInet.sin_addr;
+    netState->remotePort = ntohs(addr.addrInet.sin_port);
+    ALOGV("+++ accepted connection from %s:%u",
+        inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+    netState->clientSock = sock;
+    netState->awaitingHandshake = true;
+    netState->inputCount = 0;
+
+    ALOGV("Setting TCP_NODELAY on accepted socket");
+    setNoDelay(netState->clientSock);
+
+    if (pipe(netState->wakePipe) < 0) {
+        ALOGE("pipe failed");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Create a connection to a waiting debugger.
+ */
+static bool establishConnection(JdwpState* state)
+{
+    union {
+        struct sockaddr_in  addrInet;
+        struct sockaddr     addrPlain;
+    } addr;
+    struct hostent* pEntry;
+    int h_errno;
+
+    assert(state != NULL && state->netState != NULL);
+    assert(!state->params.server);
+    assert(state->params.host[0] != '\0');
+    assert(state->params.port != 0);
+
+    /*
+     * Start by resolving the host name.
+     */
+//#undef HAVE_GETHOSTBYNAME_R
+//#warning "forcing non-R"
+#ifdef HAVE_GETHOSTBYNAME_R
+    struct hostent he;
+    char auxBuf[128];
+    int cc = gethostbyname_r(state->params.host, &he, auxBuf, sizeof(auxBuf),
+            &pEntry, &h_errno);
+    if (cc != 0) {
+        ALOGW("gethostbyname_r('%s') failed: %s",
+            state->params.host, strerror(errno));
+        return false;
+    }
+
+#else
+    h_errno = 0;
+    pEntry = gethostbyname(state->params.host);
+    if (pEntry == NULL) {
+        ALOGW("gethostbyname('%s') failed: %s",
+            state->params.host, strerror(h_errno));
+        return false;
+    }
+#endif
+
+    /* copy it out ASAP to minimize risk of multithreaded annoyances */
+    memcpy(&addr.addrInet.sin_addr, pEntry->h_addr, pEntry->h_length);
+    addr.addrInet.sin_family = pEntry->h_addrtype;
+
+    addr.addrInet.sin_port = htons(state->params.port);
+
+    ALOGI("Connecting out to '%s' %d",
+        inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port));
+
+    /*
+     * Create a socket.
+     */
+    JdwpNetState* netState;
+    netState = state->netState;
+    netState->clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (netState->clientSock < 0) {
+        ALOGE("Unable to create socket: %s", strerror(errno));
+        return false;
+    }
+
+    /*
+     * Try to connect.
+     */
+    if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) {
+        ALOGE("Unable to connect to %s:%d: %s",
+            inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port),
+            strerror(errno));
+        close(netState->clientSock);
+        netState->clientSock = -1;
+        return false;
+    }
+
+    ALOGI("Connection established to %s (%s:%d)",
+        state->params.host, inet_ntoa(addr.addrInet.sin_addr),
+        ntohs(addr.addrInet.sin_port));
+    netState->awaitingHandshake = true;
+    netState->inputCount = 0;
+
+    setNoDelay(netState->clientSock);
+
+    if (pipe(netState->wakePipe) < 0) {
+        ALOGE("pipe failed");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Close the connection to the debugger.
+ *
+ * Reset the state so we're ready to receive a new connection.
+ */
+static void closeConnection(JdwpState* state)
+{
+    JdwpNetState* netState;
+
+    assert(state != NULL && state->netState != NULL);
+
+    netState = state->netState;
+    if (netState->clientSock < 0)
+        return;
+
+    ALOGV("+++ closed connection to %s:%u",
+        inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+    close(netState->clientSock);
+    netState->clientSock = -1;
+
+    return;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+    long length;
+
+    if (netState->awaitingHandshake)
+        return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+    if (netState->inputCount < 4)
+        return false;
+
+    length = get4BE(netState->inputBuffer);
+    return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer.  However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+    assert(count > 0);
+    assert(count <= netState->inputCount);
+
+    if (count == netState->inputCount) {
+        netState->inputCount = 0;
+        return;
+    }
+
+    memmove(netState->inputBuffer, netState->inputBuffer + count,
+        netState->inputCount - count);
+    netState->inputCount -= count;
+}
+
+/*
+ * Dump the contents of a packet to stdout.
+ */
+#if 0
+static void dumpPacket(const unsigned char* packetBuf)
+{
+    const unsigned char* buf = packetBuf;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0xcc;
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    dataLen = length - (buf - packetBuf);
+
+    ALOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d",
+        reply ? "reply" : "req",
+        dataLen, id, flags, cmdSet, cmd);
+    if (dataLen > 0)
+        dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+}
+#endif
+
+/*
+ * Handle a packet.  Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    const unsigned char* buf = netState->inputBuffer;
+    JdwpReqHeader hdr;
+    u4 length, id;
+    u1 flags, cmdSet, cmd;
+    u2 error;
+    bool reply;
+    int dataLen;
+
+    cmd = cmdSet = 0;       // shut up gcc
+
+    /*dumpPacket(netState->inputBuffer);*/
+
+    length = read4BE(&buf);
+    id = read4BE(&buf);
+    flags = read1(&buf);
+    if ((flags & kJDWPFlagReply) != 0) {
+        reply = true;
+        error = read2BE(&buf);
+    } else {
+        reply = false;
+        cmdSet = read1(&buf);
+        cmd = read1(&buf);
+    }
+
+    assert((int) length <= netState->inputCount);
+    dataLen = length - (buf - netState->inputBuffer);
+
+    if (!reply) {
+        ExpandBuf* pReply = expandBufAlloc();
+
+        hdr.length = length;
+        hdr.id = id;
+        hdr.cmdSet = cmdSet;
+        hdr.cmd = cmd;
+        dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+        if (expandBufGetLength(pReply) > 0) {
+            ssize_t cc = netState->writePacket(pReply);
+
+            if (cc != (ssize_t) expandBufGetLength(pReply)) {
+                ALOGE("Failed sending reply to debugger: %s", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            ALOGW("No reply created for set=%d cmd=%d", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        ALOGV("reply?!");
+        assert(false);
+    }
+
+    ALOGV("----------");
+
+    consumeBytes(netState, length);
+    return true;
+}
+
+/*
+ * Process incoming data.  If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached.  If we don't, the
+ * debugger will just mysteriously hang until it times out.  We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+    JdwpNetState* netState = state->netState;
+    int readCount;
+
+    assert(netState->clientSock >= 0);
+
+    if (!haveFullPacket(netState)) {
+        /* read some more, looping until we have data */
+        errno = 0;
+        while (1) {
+            int selCount;
+            fd_set readfds;
+            int maxfd;
+            int fd;
+
+            maxfd = netState->listenSock;
+            if (netState->clientSock > maxfd)
+                maxfd = netState->clientSock;
+            if (netState->wakePipe[0] > maxfd)
+                maxfd = netState->wakePipe[0];
+
+            if (maxfd < 0) {
+                ALOGV("+++ all fds are closed");
+                return false;
+            }
+
+            FD_ZERO(&readfds);
+
+            /* configure fds; note these may get zapped by another thread */
+            fd = netState->listenSock;
+            if (fd >= 0)
+                FD_SET(fd, &readfds);
+            fd = netState->clientSock;
+            if (fd >= 0)
+                FD_SET(fd, &readfds);
+            fd = netState->wakePipe[0];
+            if (fd >= 0) {
+                FD_SET(fd, &readfds);
+            } else {
+                ALOGI("NOTE: entering select w/o wakepipe");
+            }
+
+            /*
+             * Select blocks until it sees activity on the file descriptors.
+             * Closing the local file descriptor does not count as activity,
+             * so we can't rely on that to wake us up (it works for read()
+             * and accept(), but not select()).
+             *
+             * We can do one of three things: (1) send a signal and catch
+             * EINTR, (2) open an additional fd ("wakePipe") and write to
+             * it when it's time to exit, or (3) time out periodically and
+             * re-issue the select.  We're currently using #2, as it's more
+             * reliable than #1 and generally better than #3.  Wastes two fds.
+             */
+            selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+            if (selCount < 0) {
+                if (errno == EINTR)
+                    continue;
+                ALOGE("select failed: %s", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakePipe[0] >= 0 &&
+                FD_ISSET(netState->wakePipe[0], &readfds))
+            {
+                if (netState->listenSock >= 0)
+                    ALOGE("Exit wake set, but not exiting?");
+                else
+                    ALOGD("Got wake-up signal, bailing out of select");
+                goto fail;
+            }
+            if (netState->listenSock >= 0 &&
+                FD_ISSET(netState->listenSock, &readfds))
+            {
+                ALOGI("Ignoring second debugger -- accepting and dropping");
+                union {
+                    struct sockaddr_in   addrInet;
+                    struct sockaddr      addrPlain;
+                } addr;
+                socklen_t addrlen;
+                int tmpSock;
+                tmpSock = accept(netState->listenSock, &addr.addrPlain,
+                                &addrlen);
+                if (tmpSock < 0)
+                    ALOGI("Weird -- accept failed");
+                else
+                    close(tmpSock);
+            }
+            if (netState->clientSock >= 0 &&
+                FD_ISSET(netState->clientSock, &readfds))
+            {
+                readCount = read(netState->clientSock,
+                                netState->inputBuffer + netState->inputCount,
+                    sizeof(netState->inputBuffer) - netState->inputCount);
+                if (readCount < 0) {
+                    /* read failed */
+                    if (errno != EINTR)
+                        goto fail;
+                    ALOGD("+++ EINTR hit");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    ALOGD("+++ peer disconnected");
+                    goto fail;
+                } else
+                    break;
+            }
+        }
+
+        netState->inputCount += readCount;
+        if (!haveFullPacket(netState))
+            return true;        /* still not there yet */
+    }
+
+    /*
+     * Special-case the initial handshake.  For some bizarre reason we're
+     * expected to emulate bad tty settings by echoing the request back
+     * exactly as it was sent.  Note the handshake is always initiated by
+     * the debugger, no matter who connects to whom.
+     *
+     * Other than this one case, the protocol [claims to be] stateless.
+     */
+    if (netState->awaitingHandshake) {
+        int cc;
+
+        if (memcmp(netState->inputBuffer,
+                kMagicHandshake, kMagicHandshakeLen) != 0)
+        {
+            ALOGE("ERROR: bad handshake '%.14s'", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = TEMP_FAILURE_RETRY(write(netState->clientSock, netState->inputBuffer,
+                                      kMagicHandshakeLen));
+        if (cc != kMagicHandshakeLen) {
+            ALOGE("Failed writing handshake bytes: %s (%d of %d)",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        ALOGV("+++ handshake complete");
+        return true;
+    }
+
+    /*
+     * Handle this packet.
+     */
+    return handlePacket(state);
+
+fail:
+    closeConnection(state);
+    return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+    JdwpNetState* netState = state->netState;
+
+    /*dumpPacket(expandBufGetBuffer(pReq));*/
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    errno = 0;
+    ssize_t cc = netState->writePacket(pReq);
+
+    if (cc != (ssize_t) expandBufGetLength(pReq)) {
+        ALOGE("Failed sending req to debugger: %s (%d of %d)",
+            strerror(errno), (int) cc, (int) expandBufGetLength(pReq));
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+    int iovcnt)
+{
+    JdwpNetState* netState = state->netState;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        ALOGV("NOT sending request -- no debugger is attached");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    ssize_t actual = netState->writeBufferedPacket(iov, iovcnt);
+
+    if ((size_t)actual != expected) {
+        ALOGE("Failed sending b-req to debugger: %s (%d of %zu)",
+            strerror(errno), (int) actual, expected);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Our functions.
+ *
+ * We can't generally share the implementations with other transports,
+ * even if they're also socket-based, because our JdwpNetState will be
+ * different from theirs.
+ */
+static const JdwpTransport socketTransport = {
+    prepareSocket,
+    acceptConnection,
+    establishConnection,
+    closeConnection,
+    netShutdownExtern,
+    netFreeExtern,
+    isConnected,
+    awaitingHandshake,
+    processIncoming,
+    sendRequest,
+    sendBufferedRequest,
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpSocketTransport()
+{
+    return &socketTransport;
+}
diff --git a/vm/jdwp/README.txt b/vm/jdwp/README.txt
new file mode 100644
index 0000000..c97a069
--- /dev/null
+++ b/vm/jdwp/README.txt
@@ -0,0 +1,12 @@
+Java Debug Wire Protocol support
+
+This is a reasonably complete implementation, but only messages that are
+actually generated by debuggers have been implemented.  The reasoning
+behind this is that it's better to leave a call unimplemented than have
+something that appears implemented but has never been tested.
+
+An attempt has been made to keep the implementation distinct from the VM,
+with Debugger.c acting as a sort of portability layer, so that the code
+might be useful in other projects.  Once you get multiple simultaneous
+events and debugger requests with thread suspension bouncing around,
+though, it's difficult to keep things "generic".
diff --git a/vm/mterp/Makefile-mterp b/vm/mterp/Makefile-mterp
new file mode 100644
index 0000000..2f96d59
--- /dev/null
+++ b/vm/mterp/Makefile-mterp
@@ -0,0 +1,51 @@
+# Copyright (C) 2008 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.
+
+#
+# Makefile for the Dalvik modular interpreter.  This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion.  If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+	$(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print) \
+	../Android.mk ../../Android.mk
+
+# Source files generated by the script.  There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+	$(OUTPUT_DIR)/InterpC-$(TARGET_ARCH_EXT).c \
+	$(OUTPUT_DIR)/InterpAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+	@mkdir -p out
+	./gen-mterp.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/mterp/Mterp.cpp b/vm/mterp/Mterp.cpp
new file mode 100644
index 0000000..bfeada2
--- /dev/null
+++ b/vm/mterp/Mterp.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Mterp entry point and support functions.
+ */
+#include "Dalvik.h"
+#include "mterp/Mterp.h"
+
+#include <stddef.h>
+
+
+/*
+ * Verify some constants used by the mterp interpreter.
+ */
+bool dvmCheckAsmConstants()
+{
+    bool failed = false;
+
+#ifndef DVM_NO_ASM_INTERP
+
+#ifndef DVM_JMP_TABLE_MTERP
+    extern void* dvmAsmInstructionStart[];
+    extern void* dvmAsmInstructionEnd[];
+#endif
+
+#define ASM_DEF_VERIFY
+#include "mterp/common/asm-constants.h"
+
+    if (failed) {
+        ALOGE("Please correct the values in mterp/common/asm-constants.h");
+        dvmAbort();
+    }
+
+#ifndef DVM_JMP_TABLE_MTERP
+    /*
+     * If we're using computed goto instruction transitions, make sure
+     * none of the handlers overflows the 64-byte limit.  This won't tell
+     * which one did, but if any one is too big the total size will
+     * overflow.
+     */
+#if defined(__mips__)
+    const int width = 128;
+#else
+    const int width = 64;
+#endif
+    int interpSize = (uintptr_t) dvmAsmInstructionEnd -
+                     (uintptr_t) dvmAsmInstructionStart;
+    if (interpSize != 0 && interpSize != kNumPackedOpcodes*width) {
+        ALOGE("ERROR: unexpected asm interp size %d", interpSize);
+        ALOGE("(did an instruction handler exceed %d bytes?)", width);
+        dvmAbort();
+    }
+#endif
+
+#endif // ndef DVM_NO_ASM_INTERP
+
+    return !failed;
+}
+
+
+/*
+ * "Mterp entry point.
+ */
+void dvmMterpStd(Thread* self)
+{
+    /* configure mterp items */
+    self->interpSave.methodClassDex = self->interpSave.method->clazz->pDvmDex;
+
+    IF_LOGVV() {
+        char* desc = dexProtoCopyMethodDescriptor(
+                         &self->interpSave.method->prototype);
+        LOGVV("mterp threadid=%d : %s.%s %s",
+            dvmThreadSelf()->threadId,
+            self->interpSave.method->clazz->descriptor,
+            self->interpSave.method->name,
+            desc);
+        free(desc);
+    }
+    //ALOGI("self is %p, pc=%p, fp=%p", self, self->interpSave.pc,
+    //      self->interpSave.curFrame);
+    //ALOGI("first instruction is 0x%04x", self->interpSave.pc[0]);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, self->interpSave.method);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+
+    dvmMterpStdRun(self);
+
+#ifdef LOG_INSTR
+    ALOGD("|-- Leaving interpreter loop");
+#endif
+}
diff --git a/vm/mterp/Mterp.h b/vm/mterp/Mterp.h
new file mode 100644
index 0000000..6762f67
--- /dev/null
+++ b/vm/mterp/Mterp.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Some declarations used throughout mterp.
+ */
+#ifndef DALVIK_MTERP_MTERP_H_
+#define DALVIK_MTERP_MTERP_H_
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+/*
+ * Call this during initialization to verify that the values in asm-constants.h
+ * are still correct.
+ */
+extern "C" bool dvmCheckAsmConstants(void);
+
+/*
+ * Local entry and exit points.  The platform-specific implementation must
+ * provide these two.
+ */
+extern "C" void dvmMterpStdRun(Thread* self);
+extern "C" void dvmMterpStdBail(Thread* self);
+
+/*
+ * Helper for common_printMethod(), invoked from the assembly
+ * interpreter.
+ */
+extern "C" void dvmMterpPrintMethod(Method* method);
+
+#endif  // DALVIK_MTERP_MTERP_H_
diff --git a/vm/mterp/NOTES.txt b/vm/mterp/NOTES.txt
new file mode 100644
index 0000000..5dc7caa
--- /dev/null
+++ b/vm/mterp/NOTES.txt
@@ -0,0 +1,71 @@
+Interpreter Notes
+
+
+==== Thread suspension and GC points ====
+
+The interpreter is expected to use a safe-point mechanism to allow thread
+suspension for garbage collection and the debugger.  This typically
+means an explicit check of the thread-suspend flag on backward branches
+(including self-branches on certain instructions), exception throws,
+and method returns.  The interpreter must also be prepared to switch in
+and out of the "debug" interpreter at these points.
+
+There are other ways for a thread to be stopped when a GC happens, notably:
+
+ - object allocation (either while executing an instruction that performs
+   allocation, or indirectly by allocating an exception when something
+   goes wrong)
+ - transitions to native code or indefinite wait (e.g. monitor-enter)
+
+(A debugger can in theory cause the interpreter to advance one instruction
+at a time, but since that only happens in the "debug" interpreter it's not
+necessary for authors of platform-specific code to worry about it.)
+
+For the most part the implementation does not need to worry about these
+things, but they matter when considering the contents of Dalvik's virtual
+registers for "precise" garbage collection.  So, all opcode handlers must
+follow this rule:
+
+ * Do not modify the contents of a virtual register before executing
+   code that can pause the thread.
+
+This should be fairly hard to violate given the nature of essentially all
+instructions, which will compute a result and then finish by storing that
+result into the specified destination register.  Using a virtual register
+to hold a partial or temporary result is not allowed.  Virtual registers
+must not be modified if we abort the instruction with an exception.
+
+
+==== Method results and GC ====
+
+The return value from a method is held in local storage (on the native
+stack for the portable interpreter, and in glue->retval for asm).  It's not
+accessible to a GC.  In practice this isn't a problem, because if the
+following instruction is not a "move-result" then the result is ignored,
+and none of the move-result* instructions are GC points.
+
+(This is potentially an issue when debugging, since we can theoretically
+single-step by individual bytecodes, but in practice we always step by
+source lines and move-result is grouped with the method invoke.)
+
+This suggests a rule:
+
+ * Don't do anything that can cause a GC in the invoke-* handler after
+   a method returns successfully.
+
+Unsuccessful returns, such as a native method call that returns with an
+exception pending, are not interesting because the return value is ignored.
+
+If it's not possible to obey this rule, then we need to track the value
+used in a return-object instruction for a brief period.  The easiest way
+to accomplish this is to store it in the interpreted stack where the GC
+can find it, and use a live-precise GC to ignore the value.
+
+The "trackref" functions can also be used, but they add overhead to method
+calls returning objects, and ensuring that we stop tracking the reference
+when it's no longer needed can be awkward.
+
+Any solution must work correctly when returning into or returning from native
+code.  JNI handles returns from interp->native by adding the value to the
+local references table, but returns from native->interp are simply stored
+in the usual "retval".
diff --git a/vm/mterp/README.txt b/vm/mterp/README.txt
new file mode 100644
index 0000000..6106740
--- /dev/null
+++ b/vm/mterp/README.txt
@@ -0,0 +1,305 @@
+Dalvik "mterp" README
+
+NOTE: Find rebuilding instructions at the bottom of this file.
+
+
+==== Overview ====
+
+This is the source code for the Dalvik interpreter.  The core of the
+original version was implemented as a single C function, but to improve
+performance we rewrote it in assembly.  To make this and future assembly
+ports easier and less error-prone, we used a modular approach that allows
+development of platform-specific code one opcode at a time.
+
+The original all-in-one-function C version still exists as the "portable"
+interpreter, and is generated using the same sources and tools that
+generate the platform-specific versions.
+
+Every configuration has a "config-*" file that controls how the sources
+are generated.  The sources are written into the "out" directory, where
+they are picked up by the Android build system.
+
+The best way to become familiar with the interpreter is to look at the
+generated files in the "out" directory, such as out/InterpC-portstd.c,
+rather than trying to look at the various component pieces in (say)
+armv5te.
+
+
+==== Platform-specific source generation ====
+
+The architecture-specific config files determine what goes into two
+generated output files (InterpC-<arch>.c, InterpAsm-<arch>.S).  The goal is
+to make it easy to swap C and assembly sources during initial development
+and testing, and to provide a way to use architecture-specific versions of
+some operations (e.g. making use of PLD instructions on ARMv6 or avoiding
+CLZ on ARMv4T).
+
+Depending on architecture, instruction-to-instruction transitions may
+be done as either computed goto or jump table.  In the computed goto
+variant, each instruction handler is allocated a fixed-size area (e.g. 64
+byte).  "Overflow" code is tacked on to the end.  In the jump table variant,
+all of the instructions handlers are contiguous and may be of any size.
+The interpreter style is selected via the "handler-size" command (see below).
+
+When a C implementation for an instruction is desired, the assembly
+version packs all local state into the Thread structure and passes
+that to the C function.  Updates to the state are pulled out of
+"Thread" on return.
+
+The "arch" value should indicate an architecture family with common
+programming characteristics, so "armv5te" would work for all ARMv5TE CPUs,
+but might not be backward- or forward-compatible.  (We *might* want to
+specify the ABI model as well, e.g. "armv5te-eabi", but currently that adds
+verbosity without value.)
+
+
+==== Config file format ====
+
+The config files are parsed from top to bottom.  Each line in the file
+may be blank, hold a comment (line starts with '#'), or be a command.
+
+The commands are:
+
+  handler-style <computed-goto|jump-table|all-c>
+
+    Specify which style of interpreter to generate.  In computed-goto,
+    each handler is allocated a fixed region, allowing transitions to
+    be done via table-start-address + (opcode * handler-size). With
+    jump-table style, handlers may be of any length, and the generated
+    table is an array of pointers to the handlers. The "all-c" style is
+    for the portable interpreter (which is implemented completely in C).
+    [Note: all-c is distinct from an "allstubs" configuration.  In both
+    configurations, all handlers are the C versions, but the allstubs
+    configuration uses the assembly outer loop and assembly stubs to
+    transition to the handlers].  This command is required, and must be
+    the first command in the config file.
+
+  handler-size <bytes>
+
+    Specify the size of the fixed region, in bytes.  On most platforms
+    this will need to be a power of 2.  For jump-table and all-c
+    implementations, this command is ignored.
+
+  import <filename>
+
+    The specified file is included immediately, in its entirety.  No
+    substitutions are performed.  ".cpp" and ".h" files are copied to the
+    C output, ".S" files are copied to the asm output.
+
+  asm-stub <filename>
+
+    The named file will be included whenever an assembly "stub" is needed
+    to transfer control to a handler written in C.  Text substitution is
+    performed on the opcode name.  This command is not applicable to
+    to "all-c" configurations.
+
+  asm-alt-stub <filename>
+
+    When present, this command will cause the generation of an alternate
+    set of entry points (for computed-goto interpreters) or an alternate
+    jump table (for jump-table interpreters).
+
+  op-start <directory>
+
+    Indicates the start of the opcode list.  Must precede any "op"
+    commands.  The specified directory is the default location to pull
+    instruction files from.
+
+  op <opcode> <directory>
+
+    Can only appear after "op-start" and before "op-end".  Overrides the
+    default source file location of the specified opcode.  The opcode
+    definition will come from the specified file, e.g. "op OP_NOP armv5te"
+    will load from "armv5te/OP_NOP.S".  A substitution dictionary will be
+    applied (see below).
+
+  alt <opcode> <directory>
+
+    Can only appear after "op-start" and before "op-end".  Similar to the
+    "op" command above, but denotes a source file to override the entry
+    in the alternate handler table.  The opcode definition will come from
+    the specified file, e.g. "alt OP_NOP armv5te" will load from
+    "armv5te/ALT_OP_NOP.S".  A substitution dictionary will be applied
+    (see below).
+
+  op-end
+
+    Indicates the end of the opcode list.  All kNumPackedOpcodes
+    opcodes are emitted when this is seen, followed by any code that
+    didn't fit inside the fixed-size instruction handler space.
+
+The order of "op" and "alt" directives are not significant; the generation
+tool will extract ordering info from the VM sources.
+
+Typically the form in which most opcodes currently exist is used in
+the "op-start" directive.  For a new port you would start with "c",
+and add architecture-specific "op" entries as you write instructions.
+When complete it will default to the target architecture, and you insert
+"c" ops to stub out platform-specific code.
+
+For the <directory> specified in the "op" command, the "c" directory
+is special in two ways: (1) the sources are assumed to be C code, and
+will be inserted into the generated C file; (2) when a C implementation
+is emitted, a "glue stub" is emitted in the assembly source file.
+(The generator script always emits kNumPackedOpcodes assembly
+instructions, unless "asm-stub" was left blank, in which case it only
+emits some labels.)
+
+
+==== Instruction file format ====
+
+The assembly instruction files are simply fragments of assembly sources.
+The starting label will be provided by the generation tool, as will
+declarations for the segment type and alignment.  The expected target
+assembler is GNU "as", but others will work (may require fiddling with
+some of the pseudo-ops emitted by the generation tool).
+
+The C files do a bunch of fancy things with macros in an attempt to share
+code with the portable interpreter.  (This is expected to be reduced in
+the future.)
+
+A substitution dictionary is applied to all opcode fragments as they are
+appended to the output.  Substitutions can look like "$value" or "${value}".
+
+The dictionary always includes:
+
+  $opcode - opcode name, e.g. "OP_NOP"
+  $opnum - opcode number, e.g. 0 for OP_NOP
+  $handler_size_bytes - max size of an instruction handler, in bytes
+  $handler_size_bits - max size of an instruction handler, log 2
+
+Both C and assembly sources will be passed through the C pre-processor,
+so you can take advantage of C-style comments and preprocessor directives
+like "#define".
+
+Some generator operations are available.
+
+  %include "filename" [subst-dict]
+
+    Includes the file, which should look like "armv5te/OP_NOP.S".  You can
+    specify values for the substitution dictionary, using standard Python
+    syntax.  For example, this:
+      %include "armv5te/unop.S" {"result":"r1"}
+    would insert "armv5te/unop.S" at the current file position, replacing
+    occurrences of "$result" with "r1".
+
+  %default <subst-dict>
+
+    Specify default substitution dictionary values, using standard Python
+    syntax.  Useful if you want to have a "base" version and variants.
+
+  %break
+
+    Identifies the split between the main portion of the instruction
+    handler (which must fit in "handler-size" bytes) and the "sister"
+    code, which is appended to the end of the instruction handler block.
+    In jump table implementations, %break is ignored.
+
+  %verify "message"
+
+    Leave a note to yourself about what needs to be tested.  (This may
+    turn into something more interesting someday; for now, it just gets
+    stripped out before the output is generated.)
+
+The generation tool does *not* print a warning if your instructions
+exceed "handler-size", but the VM will abort on startup if it detects an
+oversized handler.  On architectures with fixed-width instructions this
+is easy to work with, on others this you will need to count bytes.
+
+
+==== Using C constants from assembly sources ====
+
+The file "common/asm-constants.h" has some definitions for constant
+values, structure sizes, and struct member offsets.  The format is fairly
+restricted, as simple macros are used to massage it for use with both C
+(where it is verified) and assembly (where the definitions are used).
+
+If a constant in the file becomes out of sync, the VM will log an error
+message and abort during startup.
+
+
+==== Development tips ====
+
+If you need to debug the initial piece of an opcode handler, and your
+debug code expands it beyond the handler size limit, you can insert a
+generic header at the top:
+
+    b       ${opcode}_start
+%break
+${opcode}_start:
+
+If you already have a %break, it's okay to leave it in place -- the second
+%break is ignored.
+
+
+==== Rebuilding ====
+
+If you change any of the source file fragments, you need to rebuild the
+combined source files in the "out" directory.  Make sure the files in
+"out" are editable, then:
+
+    $ cd mterp
+    $ ./rebuild.sh
+
+As of this writing, this requires Python 2.5. You may see inscrutible
+error messages or just general failure if you have a different version
+of Python installed.
+
+The ultimate goal is to have the build system generate the necessary
+output files without requiring this separate step, but we're not yet
+ready to require Python in the build.
+
+==== Interpreter Control ====
+
+The central mechanism for interpreter control is the InterpBreak struture
+that is found in each thread's Thread struct (see vm/Thread.h).  There
+is one mandatory field, and two optional fields:
+
+    subMode - required, describes debug/profile/special operation
+    breakFlags & curHandlerTable - optional, used lower subMode polling costs
+
+The subMode field is a bitmask which records all currently active
+special modes of operation.  For example, when Traceview profiling
+is active, kSubModeMethodTrace is set.  This bit informs the interpreter
+that it must notify the profiling subsystem on each method entry and
+return.  There are similar bits for an active debugging session,
+instruction count profiling, pending thread suspension request, etc.
+
+To support special subMode operation the simplest mechanism for the
+interpreter is to poll the subMode field before interpreting each Dalvik
+bytecode and take any required action.  In fact, this is precisely
+what the portable interpreter does.  The "FINISH" macro expands to
+include a test of subMode and subsequent call to the "dvmCheckBefore()".
+
+Per-instruction polling, however, is expensive and subMode operation is
+relative rare.  For normal operation we'd like to avoid having to perform
+any checks unless a special subMode is actually in effect.  This is
+where curHandlerTable and breakFlags come in to play.
+
+The mterp fast interpreter achieves much of its performance advantage
+over the portable interpreter through its efficient mechanism of
+transitioning from one Dalvik bytecode to the next.  Mterp for ARM targets
+uses a computed-goto mechanism, in which the handler entrypoints are
+located at the base of the handler table + (opcode * 64).  Mterp for x86
+targets instead uses a jump table of handler entry points indexed
+by the Dalvik opcode.  To support efficient handling of special subModes,
+mterp supports two sets of handler entries (for ARM) or two jump
+tables (for x86).  One handler set is optimized for speed and performs no
+inter-instruction checks (mainHandlerTable in the Thread structure), while
+the other includes a test of the subMode field (altHandlerTable).
+
+In normal operation (i.e. subMode == 0), the dedicated register rIBASE
+(r8 for ARM, edx for x86) holds a mainHandlerTable.  If we need to switch
+to a subMode that requires inter-instruction checking, rIBASE is changed
+to altHandlerTable.  Note that this change is not immediate.  What is actually
+changed is the value of curHandlerTable - which is part of the interpBreak
+structure.  Rather than explicitly check for changes, each thread will
+blindly refresh rIBASE at backward branches, exception throws and returns.
+
+The breakFlags field tells the interpreter control mechanism whether
+curHandlerTable should hold the real or alternate handler base.  If
+non-zero, we use the altHandlerBase.  The bits within breakFlags
+tells dvmCheckBefore which set of subModes need to be checked.
+
+See dvmCheckBefore() for subMode handling, and dvmEnableSubMode(),
+dvmDisableSubMode() for switching on and off.
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..5a5ad1d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9823765
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"faddd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..22023ec
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e787589
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fadds   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..b75216e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..eade97d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..6e85fe7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bdeb0be
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .L${opcode}_finish          @ argh
+
+%break
+.L${opcode}_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..11770ad
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a52f434
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fdivd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..2e82ada
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2147583
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fdivs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..33d5b61
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"fcvtsd  s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..2ef4838
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"ftosizd  s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..0acb3d8
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fcvtds  d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..d0a9a2e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..6eb430e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fsitod  d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..698bdc7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"fsitos  s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_LONG_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..3ed3b19
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,28 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialised 64-bit floating point operation.
+     *
+     * Note: The result will be returned in d2.
+     *
+     * For: long-to-double
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    vldr    d0, [r3]                    @ d0<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    vcvt.f64.s32    d1, s1              @ d1<- (double)(vAAh)
+    vcvt.f64.u32    d2, s0              @ d2<- (double)(vAAl)
+    vldr            d3, constval$opcode
+    vmla.f64        d2, d1, d3          @ d2<- vAAh*2^32 + vAAl
+
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    vstr.64 d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* literal pool helper */
+constval${opcode}:
+    .8byte          0x41f0000000000000
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..7563191
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..eadf101
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fmuld   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..bb3ab42
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..3918537
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fmuls   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..d40e083
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..705124f
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fsubd   d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..0bf2bc0
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e214068
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fsubs   s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/README.txt b/vm/mterp/arm-vfp/README.txt
new file mode 100644
index 0000000..c94e1e8
--- /dev/null
+++ b/vm/mterp/arm-vfp/README.txt
@@ -0,0 +1,7 @@
+Instruction handlers that take advantage of ARM VFP.  These work with VFP
+v2 and v3 (VFPLite).
+
+The ARM code driving the floating-point calculations will run on ARMv5TE
+and later.  It assumes that word alignment is sufficient for double-word
+accesses (which is true for some ARMv5 and all ARMv6/v7), to avoid having
+to transfer double-precision values in two steps.
diff --git a/vm/mterp/arm-vfp/fbinop.S b/vm/mterp/arm-vfp/fbinop.S
new file mode 100644
index 0000000..ff9683e
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop.S
@@ -0,0 +1,23 @@
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinop2addr.S b/vm/mterp/arm-vfp/fbinop2addr.S
new file mode 100644
index 0000000..85b9fab
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop2addr.S
@@ -0,0 +1,21 @@
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide.S b/vm/mterp/arm-vfp/fbinopWide.S
new file mode 100644
index 0000000..2b9ad69
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide.S
@@ -0,0 +1,23 @@
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $instr                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide2addr.S b/vm/mterp/arm-vfp/fbinopWide2addr.S
new file mode 100644
index 0000000..15d9424
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide2addr.S
@@ -0,0 +1,22 @@
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    $instr                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funop.S b/vm/mterp/arm-vfp/funop.S
new file mode 100644
index 0000000..a5846ce
--- /dev/null
+++ b/vm/mterp/arm-vfp/funop.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopNarrower.S b/vm/mterp/arm-vfp/funopNarrower.S
new file mode 100644
index 0000000..7ae1676
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopNarrower.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopWider.S b/vm/mterp/arm-vfp/funopWider.S
new file mode 100644
index 0000000..055b851
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopWider.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    $instr                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE.S b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..de36691
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..744b2ab
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT.S b/vm/mterp/armv5te/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..badf1f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..70f1fe8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT.S b/vm/mterp/armv5te/OP_ADD_INT.S
new file mode 100644
index 0000000..97cb5fe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..c424f0c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT16.S b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f5a1603
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT8.S b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..93e57d3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG.S b/vm/mterp/armv5te/OP_ADD_LONG.S
new file mode 100644
index 0000000..b30cd05
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..ff34ed5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AGET.S b/vm/mterp/armv5te/OP_AGET.S
new file mode 100644
index 0000000..8d8ed58
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET.S
@@ -0,0 +1,27 @@
+%default { "load":"ldr", "shift":"2" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #$shift     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $load   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AGET_BOOLEAN.S b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..ccce848
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_BYTE.S b/vm/mterp/armv5te/OP_AGET_BYTE.S
new file mode 100644
index 0000000..c290922
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_CHAR.S b/vm/mterp/armv5te/OP_AGET_CHAR.S
new file mode 100644
index 0000000..78c40a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_OBJECT.S b/vm/mterp/armv5te/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..9d64585
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S"
diff --git a/vm/mterp/armv5te/OP_AGET_SHORT.S b/vm/mterp/armv5te/OP_AGET_SHORT.S
new file mode 100644
index 0000000..77951e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_WIDE.S b/vm/mterp/armv5te/OP_AGET_WIDE.S
new file mode 100644
index 0000000..6f641dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .L${opcode}_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AND_INT.S b/vm/mterp/armv5te/OP_AND_INT.S
new file mode 100644
index 0000000..2a0022e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_2ADDR.S b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..24203aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT16.S b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..a06084d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT8.S b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..bd309bb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG.S b/vm/mterp/armv5te/OP_AND_LONG.S
new file mode 100644
index 0000000..c76bae8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..6b7c830
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_APUT.S b/vm/mterp/armv5te/OP_APUT.S
new file mode 100644
index 0000000..741aadd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT.S
@@ -0,0 +1,27 @@
+%default { "store":"str", "shift":"2" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #$shift     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $store  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_APUT_BOOLEAN.S b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_BYTE.S b/vm/mterp/armv5te/OP_APUT_BYTE.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_CHAR.S b/vm/mterp/armv5te/OP_APUT_CHAR.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_OBJECT.S b/vm/mterp/armv5te/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..95aec3a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_OBJECT.S
@@ -0,0 +1,55 @@
+%verify "executed"
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .L${opcode}_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+%break
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .L${opcode}_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .L${opcode}_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.L${opcode}_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.L${opcode}_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
diff --git a/vm/mterp/armv5te/OP_APUT_SHORT.S b/vm/mterp/armv5te/OP_APUT_SHORT.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_WIDE.S b/vm/mterp/armv5te/OP_APUT_WIDE.S
new file mode 100644
index 0000000..cc9f332
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .L${opcode}_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_ARRAY_LENGTH.S b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..3a6faf3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_BREAKPOINT.S b/vm/mterp/armv5te/OP_BREAKPOINT.S
new file mode 100644
index 0000000..b4ea333
--- /dev/null
+++ b/vm/mterp/armv5te/OP_BREAKPOINT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
diff --git a/vm/mterp/armv5te/OP_CHECK_CAST.S b/vm/mterp/armv5te/OP_CHECK_CAST.S
new file mode 100644
index 0000000..57df60e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CHECK_CAST.S
@@ -0,0 +1,68 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .L${opcode}_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .L${opcode}_fullcheck       @ no, do full check
+.L${opcode}_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.L${opcode}_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .L${opcode}_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .L${opcode}_resolved        @ pick up where we left off
diff --git a/vm/mterp/armv5te/OP_CMPG_DOUBLE.S b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..a18c186
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_DOUBLE.S" { "naninst":"mov     r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPG_FLOAT.S b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..3f87470
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_FLOAT.S" { "naninst":"mov     r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPL_DOUBLE.S b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..01a63f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
@@ -0,0 +1,48 @@
+%default { "naninst":"mvn     r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .L${opcode}_finish
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       .L${opcode}_finish
diff --git a/vm/mterp/armv5te/OP_CMPL_FLOAT.S b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..657f0dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
@@ -0,0 +1,115 @@
+%default { "naninst":"mvn     r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .L${opcode}_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.L${opcode}_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .L${opcode}_finish
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       .L${opcode}_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     ${opcode}_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       ${opcode}_continue
+@%break
+
+${opcode}_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     ${opcode}_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     ${opcode}_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+${opcode}_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+${opcode}_nan:
+    $naninst                            @ r1<- 1 or -1 for NaN
+    b       ${opcode}_finish
+
+#endif
diff --git a/vm/mterp/armv5te/OP_CMP_LONG.S b/vm/mterp/armv5te/OP_CMP_LONG.S
new file mode 100644
index 0000000..084a3f2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMP_LONG.S
@@ -0,0 +1,60 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .L${opcode}_less            @ signed compare on high part
+    bgt     .L${opcode}_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .L${opcode}_greater         @ unsigned compare on low part
+    bne     .L${opcode}_less
+    b       .L${opcode}_finish          @ equal; r1 already holds 0
+%break
+
+.L${opcode}_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.L${opcode}_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST.S b/vm/mterp/armv5te/OP_CONST.S
new file mode 100644
index 0000000..a813c52
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_16.S b/vm/mterp/armv5te/OP_CONST_16.S
new file mode 100644
index 0000000..b5654a3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_4.S b/vm/mterp/armv5te/OP_CONST_4.S
new file mode 100644
index 0000000..6dd53af
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_CLASS.S b/vm/mterp/armv5te/OP_CONST_CLASS.S
new file mode 100644
index 0000000..018e1e0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_CLASS.S
@@ -0,0 +1,35 @@
+%verify "executed"
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_HIGH16.S b/vm/mterp/armv5te/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..4536d3a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING.S b/vm/mterp/armv5te/OP_CONST_STRING.S
new file mode 100644
index 0000000..0d889dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING.S
@@ -0,0 +1,34 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..6ca6bd1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,36 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .L${opcode}_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE.S b/vm/mterp/armv5te/OP_CONST_WIDE.S
new file mode 100644
index 0000000..b724264
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_16.S b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..e3e4149
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_32.S b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..a86e042
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..11bf518
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE.S b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..2fb085b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2dcef28
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT.S b/vm/mterp/armv5te/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..8fc2fa9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e2d57dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT.S b/vm/mterp/armv5te/OP_DIV_INT.S
new file mode 100644
index 0000000..1e326d9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..22c8380
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT16.S b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..574594f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT8.S b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..8e56a4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG.S b/vm/mterp/armv5te/OP_DIV_LONG.S
new file mode 100644
index 0000000..ec5db4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..4651383
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..f0b1a33
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_d2f"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..3e0a26b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ff0fd2e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+%include "armv5te/unopWide.S" {"instr":"bl      d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..ca71de1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
@@ -0,0 +1,99 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .L${opcode}_debugmode       @ yes - take slow path
+.L${opcode}_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.L${opcode}_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .L${opcode}_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.L${opcode}_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .L${opcode}_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.L${opcode}_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..d9e35b8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,94 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .L${opcode}_debugmode       @ yes - take slow path
+.L${opcode}_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.L${opcode}_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .L${opcode}_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.L${opcode}_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .L${opcode}_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .L${opcode}_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.L${opcode}_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..de39958
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,109 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .L${opcode}_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .L${opcode}_continue
+%break
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.L${opcode}_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     $isrange
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .L${opcode}_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     $isrange
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_${opcode}
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_${opcode}:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..a2273c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..a0d8399
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..b235e61
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2d"}
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_INT.S b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..c9cb957
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unop.S" {"instr":"bl      __aeabi_f2iz"}
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..e42e1a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+%include "armv5te/unopWider.S" {"instr":"bl      f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
diff --git a/vm/mterp/armv5te/OP_GOTO.S b/vm/mterp/armv5te/OP_GOTO.S
new file mode 100644
index 0000000..7feca7b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO.S
@@ -0,0 +1,22 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_GOTO_16.S b/vm/mterp/armv5te/OP_GOTO_16.S
new file mode 100644
index 0000000..8b1f1bd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_16.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_GOTO_32.S b/vm/mterp/armv5te/OP_GOTO_32.S
new file mode 100644
index 0000000..6202d7e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_32.S
@@ -0,0 +1,29 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IF_EQ.S b/vm/mterp/armv5te/OP_IF_EQ.S
new file mode 100644
index 0000000..ecd2c55
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_EQZ.S b/vm/mterp/armv5te/OP_IF_EQZ.S
new file mode 100644
index 0000000..2011a03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_GE.S b/vm/mterp/armv5te/OP_IF_GE.S
new file mode 100644
index 0000000..7424939
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GEZ.S b/vm/mterp/armv5te/OP_IF_GEZ.S
new file mode 100644
index 0000000..2cae521
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GT.S b/vm/mterp/armv5te/OP_IF_GT.S
new file mode 100644
index 0000000..553a74a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_GTZ.S b/vm/mterp/armv5te/OP_IF_GTZ.S
new file mode 100644
index 0000000..c82911f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_LE.S b/vm/mterp/armv5te/OP_IF_LE.S
new file mode 100644
index 0000000..dd99709
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LEZ.S b/vm/mterp/armv5te/OP_IF_LEZ.S
new file mode 100644
index 0000000..382c34d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LT.S b/vm/mterp/armv5te/OP_IF_LT.S
new file mode 100644
index 0000000..34dc445
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_LTZ.S b/vm/mterp/armv5te/OP_IF_LTZ.S
new file mode 100644
index 0000000..6a0432f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_NE.S b/vm/mterp/armv5te/OP_IF_NE.S
new file mode 100644
index 0000000..950c916
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IF_NEZ.S b/vm/mterp/armv5te/OP_IF_NEZ.S
new file mode 100644
index 0000000..226549c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IGET.S b/vm/mterp/armv5te/OP_IGET.S
new file mode 100644
index 0000000..a81467c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET.S
@@ -0,0 +1,47 @@
+%default { "load":"ldr", "barrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    $load   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    $barrier                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_BOOLEAN.S b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0fbd0aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IGET_BYTE.S b/vm/mterp/armv5te/OP_IGET_BYTE.S
new file mode 100644
index 0000000..ef8fdfc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BYTE.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IGET_CHAR.S b/vm/mterp/armv5te/OP_IGET_CHAR.S
new file mode 100644
index 0000000..b88b017
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_CHAR.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT.S b/vm/mterp/armv5te/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..e5ca05f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..727b2aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_QUICK.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_QUICK.S b/vm/mterp/armv5te/OP_IGET_QUICK.S
new file mode 100644
index 0000000..c19f870
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_SHORT.S b/vm/mterp/armv5te/OP_IGET_SHORT.S
new file mode 100644
index 0000000..a1b60b1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_SHORT.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IGET_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE.S b/vm/mterp/armv5te/OP_IGET_WIDE.S
new file mode 100644
index 0000000..c73edfd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE.S
@@ -0,0 +1,49 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     $volatile
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..b32e429
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..face363
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_INSTANCE_OF.S b/vm/mterp/armv5te/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..73911b1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INSTANCE_OF.S
@@ -0,0 +1,85 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .L${opcode}_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .L${opcode}_trivial         @ yes, trivial finish
+    b       .L${opcode}_fullcheck       @ no, do full check
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.L${opcode}_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to ${opcode}_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.L${opcode}_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.L${opcode}_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b ${opcode}_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .L${opcode}_resolved        @ pick up where we left off
diff --git a/vm/mterp/armv5te/OP_INT_TO_BYTE.S b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..cf1c981
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #24", "instr":"mov     r0, r0, asr #24"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_CHAR.S b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..568cf08
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #16", "instr":"mov     r0, r0, lsr #16"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..8b00b64
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl      __aeabi_i2d"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_FLOAT.S b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..53d49df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"bl      __aeabi_i2f"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_LONG.S b/vm/mterp/armv5te/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..b744439
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"mov     r1, r0, asr #31"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_SHORT.S b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..b6deb85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov     r0, r0, asl #16", "instr":"mov     r0, r0, asr #16"}
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..7167a2b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
@@ -0,0 +1,46 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethod${routine}   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+%break
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..0b799e5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..5ed35a8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,27 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!$isrange)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..f1eb27d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..f97c4e3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,44 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, ${cccc})                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .L${opcode}_setFinal        @ yes, go
+.L${opcode}_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .L${opcode}_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(${cccc}+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+%break
+
+.L${opcode}_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .L${opcode}_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC.S b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..a89db03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
@@ -0,0 +1,54 @@
+%default { "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethod${routine} @ yes, continue on
+    b       .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethod${routine}     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethod${routine}     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethod${routine}     @ whew, finally!
+#else
+    bne     common_invokeMethod${routine}     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..92b9ca5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER.S b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..b1d1411
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
@@ -0,0 +1,60 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .L${opcode}_continue        @ resolved, continue on
+    b       .L${opcode}_resolve         @ do resolve now
+%break
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.L${opcode}_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .L${opcode}_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethod${routine} @ continue on
+
+.L${opcode}_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.L${opcode}_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..4848b7e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,25 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..77d43cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..0a0f737
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..58b0a42
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,45 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!$isrange)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .L${opcode}_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+%break
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.L${opcode}_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..4b425da
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!$isrange)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethod${routine} @ (r0=method, r9="this")
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..d257f2b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..4f9ca50
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_IPUT.S b/vm/mterp/armv5te/OP_IPUT.S
new file mode 100644
index 0000000..e0aa269
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT.S
@@ -0,0 +1,48 @@
+%default { "store":"str", "postbarrier":"@ no-op ", "prebarrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $prebarrier                        @ releasing store
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    $postbarrier
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..9ac68ca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IPUT_BYTE.S b/vm/mterp/armv5te/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3871999
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IPUT_CHAR.S b/vm/mterp/armv5te/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..60e136c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT.S b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..8795971
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
@@ -0,0 +1,51 @@
+%default { "postbarrier":"@ no-op ", "prebarrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $prebarrier                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    $postbarrier
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..7bf9b21
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..317c5b2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad76eca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_SHORT.S b/vm/mterp/armv5te/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4844575
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IPUT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..1a7a098
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE.S b/vm/mterp/armv5te/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..ec787f0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     $volatile
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..b7dd703
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..944811b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..842a8ff
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"bl      __aeabi_l2d"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..ba250cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl      __aeabi_l2f"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_INT.S b/vm/mterp/armv5te/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..4509546
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MONITOR_ENTER.S b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..ba5a144
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MONITOR_EXIT.S b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..9f36f0e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
@@ -0,0 +1,26 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
diff --git a/vm/mterp/armv5te/OP_MOVE.S b/vm/mterp/armv5te/OP_MOVE.S
new file mode 100644
index 0000000..efeddf4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_16.S b/vm/mterp/armv5te/OP_MOVE_16.S
new file mode 100644
index 0000000..3c08759
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..e2fc66f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..fdcc64e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..0cd09df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..9f0f1a9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..c331a82
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT.S b/vm/mterp/armv5te/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..72377f8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a04cdb4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..ea80de8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE.S b/vm/mterp/armv5te/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..0811c95
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..2b3e99f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..81ae7dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE.S b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..69a10f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2201414
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT.S b/vm/mterp/armv5te/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..13e64e0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..d97e80f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT.S b/vm/mterp/armv5te/OP_MUL_INT.S
new file mode 100644
index 0000000..252114b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..a5a3754
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop2addr.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT16.S b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..1a28d8b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit16.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT8.S b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..e714ed9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit8.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_LONG.S b/vm/mterp/armv5te/OP_MUL_LONG.S
new file mode 100644
index 0000000..3a7aac1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG.S
@@ -0,0 +1,41 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..a561dc9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,26 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEG_DOUBLE.S b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..d371016
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"add     r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_FLOAT.S b/vm/mterp/armv5te/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..81b439c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"add     r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_INT.S b/vm/mterp/armv5te/OP_NEG_INT.S
new file mode 100644
index 0000000..2f57540
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"rsb     r0, r0, #0"}
diff --git a/vm/mterp/armv5te/OP_NEG_LONG.S b/vm/mterp/armv5te/OP_NEG_LONG.S
new file mode 100644
index 0000000..215f056
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"rsbs    r0, r0, #0", "instr":"rsc     r1, r1, #0"}
diff --git a/vm/mterp/armv5te/OP_NEW_ARRAY.S b/vm/mterp/armv5te/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..eca1ac6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_ARRAY.S
@@ -0,0 +1,61 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .L${opcode}_finish          @ resolved, continue
+    b       .L${opcode}_resolve         @ do resolve now
+%break
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to ${opcode}_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.L${opcode}_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEW_INSTANCE.S b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..a03d111
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
@@ -0,0 +1,101 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .L${opcode}_resolve         @ no, resolve it now
+.L${opcode}_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .L${opcode}_needinit        @ no, init class now
+.L${opcode}_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .L${opcode}_finish          @ continue
+%break
+
+    .balign 32                          @ minimize cache lines
+.L${opcode}_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .L${opcode}_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.L${opcode}_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.L${opcode}_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .L${opcode}_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.L${opcode}_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .L${opcode}_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .L${opcode}_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_NOP.S b/vm/mterp/armv5te/OP_NOP.S
new file mode 100644
index 0000000..ff4cf5a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOP.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
diff --git a/vm/mterp/armv5te/OP_NOT_INT.S b/vm/mterp/armv5te/OP_NOT_INT.S
new file mode 100644
index 0000000..7281cbe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"mvn     r0, r0"}
diff --git a/vm/mterp/armv5te/OP_NOT_LONG.S b/vm/mterp/armv5te/OP_NOT_LONG.S
new file mode 100644
index 0000000..fe741dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"mvn     r0, r0", "instr":"mvn     r1, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT.S b/vm/mterp/armv5te/OP_OR_INT.S
new file mode 100644
index 0000000..f0e5b2d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_2ADDR.S b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..3b2cf7f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT16.S b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..9098c76
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT8.S b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..b69e315
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG.S b/vm/mterp/armv5te/OP_OR_LONG.S
new file mode 100644
index 0000000..43110a2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..97d5f5b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_PACKED_SWITCH.S b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..6fa03c1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
@@ -0,0 +1,35 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      $func                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE.S b/vm/mterp/armv5te/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..54e0214
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9808c56
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide2addr.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT.S b/vm/mterp/armv5te/OP_REM_FLOAT.S
new file mode 100644
index 0000000..09cba5c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..83af133
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop2addr.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_INT.S b/vm/mterp/armv5te/OP_REM_INT.S
new file mode 100644
index 0000000..fbe9ad3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_2ADDR.S b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..42337c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT16.S b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..396e890
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit16.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT8.S b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..906c5c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit8.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG.S b/vm/mterp/armv5te/OP_REM_LONG.S
new file mode 100644
index 0000000..d703047
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..b62f093
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_RETURN.S b/vm/mterp/armv5te/OP_RETURN.S
new file mode 100644
index 0000000..5f7350a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_OBJECT.S b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..7956b45
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_RETURN.S"
diff --git a/vm/mterp/armv5te/OP_RETURN_VOID.S b/vm/mterp/armv5te/OP_RETURN_VOID.S
new file mode 100644
index 0000000..e09ebb0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S b/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S
new file mode 100644
index 0000000..1f51b62
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_VOID_BARRIER.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    SMP_DMB_ST
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_WIDE.S b/vm/mterp/armv5te/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..c185077
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RSUB_INT.S b/vm/mterp/armv5te/OP_RSUB_INT.S
new file mode 100644
index 0000000..8113456
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv5te/binopLit16.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..5d5e0fb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SGET.S b/vm/mterp/armv5te/OP_SGET.S
new file mode 100644
index 0000000..2e2453a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET.S
@@ -0,0 +1,50 @@
+%default { "barrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    $barrier                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish
diff --git a/vm/mterp/armv5te/OP_SGET_BOOLEAN.S b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_BYTE.S b/vm/mterp/armv5te/OP_SGET_BYTE.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_CHAR.S b/vm/mterp/armv5te/OP_SGET_CHAR.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT.S b/vm/mterp/armv5te/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_SHORT.S b/vm/mterp/armv5te/OP_SGET_SHORT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE.S b/vm/mterp/armv5te/OP_SGET_WIDE.S
new file mode 100644
index 0000000..f79ec30
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE.S
@@ -0,0 +1,55 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if $volatile
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..b852348
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT.S b/vm/mterp/armv5te/OP_SHL_INT.S
new file mode 100644
index 0000000..7470344
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..15b6579
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_LIT8.S b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..4da9a0f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_LONG.S b/vm/mterp/armv5te/OP_SHL_LONG.S
new file mode 100644
index 0000000..b48ca5e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..42a0904
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_INT.S b/vm/mterp/armv5te/OP_SHR_INT.S
new file mode 100644
index 0000000..586f294
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..8b97195
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_LIT8.S b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..465b61f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_LONG.S b/vm/mterp/armv5te/OP_SHR_LONG.S
new file mode 100644
index 0000000..e6489a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..9414ffb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SPARSE_SWITCH.S b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..2447286
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/armv5te/OP_SPUT.S b/vm/mterp/armv5te/OP_SPUT.S
new file mode 100644
index 0000000..986f06a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT.S
@@ -0,0 +1,51 @@
+%default { "prebarrier":"@ no-op", "postbarrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $prebarrier                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    $postbarrier
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_BYTE.S b/vm/mterp/armv5te/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_CHAR.S b/vm/mterp/armv5te/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT.S b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..77938d0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
@@ -0,0 +1,59 @@
+%default { "postbarrier":"@ no-op ", "prebarrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $prebarrier                        @ releasing store
+    b       .L${opcode}_end
+%break
+
+
+.L${opcode}_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    $postbarrier
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
+
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..a9d69bb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_OBJECT.S"  {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_SHORT.S b/vm/mterp/armv5te/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..1b8dd25
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE.S b/vm/mterp/armv5te/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..d0f65e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE.S
@@ -0,0 +1,57 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .L${opcode}_resolve         @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if $volatile
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .L${opcode}_finish          @ resume
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..a88de85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE.S b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..fed41d2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d8d51a7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT.S b/vm/mterp/armv5te/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..651669c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e7d27c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT.S b/vm/mterp/armv5te/OP_SUB_INT.S
new file mode 100644
index 0000000..f25e643
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..56ff9bc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG.S b/vm/mterp/armv5te/OP_SUB_LONG.S
new file mode 100644
index 0000000..cfdcc88
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..4fd6610
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_THROW.S b/vm/mterp/armv5te/OP_THROW.S
new file mode 100644
index 0000000..6e157b4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW.S
@@ -0,0 +1,14 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
diff --git a/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..afe9fd8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,13 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
diff --git a/vm/mterp/armv5te/OP_UNUSED_3E.S b/vm/mterp/armv5te/OP_UNUSED_3E.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_3F.S b/vm/mterp/armv5te/OP_UNUSED_3F.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_40.S b/vm/mterp/armv5te/OP_UNUSED_40.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_41.S b/vm/mterp/armv5te/OP_UNUSED_41.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_42.S b/vm/mterp/armv5te/OP_UNUSED_42.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_43.S b/vm/mterp/armv5te/OP_UNUSED_43.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_73.S b/vm/mterp/armv5te/OP_UNUSED_73.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_79.S b/vm/mterp/armv5te/OP_UNUSED_79.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_7A.S b/vm/mterp/armv5te/OP_UNUSED_7A.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_FF.S b/vm/mterp/armv5te/OP_UNUSED_FF.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_USHR_INT.S b/vm/mterp/armv5te/OP_USHR_INT.S
new file mode 100644
index 0000000..a72c2cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..8ace5f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_LIT8.S b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..9d038c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_LONG.S b/vm/mterp/armv5te/OP_USHR_LONG.S
new file mode 100644
index 0000000..d9ae338
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..27592a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_XOR_INT.S b/vm/mterp/armv5te/OP_XOR_INT.S
new file mode 100644
index 0000000..81bfa00
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..ead63c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT16.S b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..fcb4abb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT8.S b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..5ef0711
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG.S b/vm/mterp/armv5te/OP_XOR_LONG.S
new file mode 100644
index 0000000..29a0075
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..7dc71c9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv5te/alt_stub.S b/vm/mterp/armv5te/alt_stub.S
new file mode 100644
index 0000000..4ab5f9f
--- /dev/null
+++ b/vm/mterp/armv5te/alt_stub.S
@@ -0,0 +1,18 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (${opnum} * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
diff --git a/vm/mterp/armv5te/bincmp.S b/vm/mterp/armv5te/bincmp.S
new file mode 100644
index 0000000..12a2c8c
--- /dev/null
+++ b/vm/mterp/armv5te/bincmp.S
@@ -0,0 +1,30 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    mov${revcmp} r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv5te/binop.S b/vm/mterp/armv5te/binop.S
new file mode 100644
index 0000000..d169ed6
--- /dev/null
+++ b/vm/mterp/armv5te/binop.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
diff --git a/vm/mterp/armv5te/binop2addr.S b/vm/mterp/armv5te/binop2addr.S
new file mode 100644
index 0000000..061242a
--- /dev/null
+++ b/vm/mterp/armv5te/binop2addr.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit16.S b/vm/mterp/armv5te/binopLit16.S
new file mode 100644
index 0000000..df49929
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit16.S
@@ -0,0 +1,30 @@
+%default {"result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit8.S b/vm/mterp/armv5te/binopLit8.S
new file mode 100644
index 0000000..2addd26
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit8.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if $chkzero
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
diff --git a/vm/mterp/armv5te/binopWide.S b/vm/mterp/armv5te/binopWide.S
new file mode 100644
index 0000000..71fd3fe
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
diff --git a/vm/mterp/armv5te/binopWide2addr.S b/vm/mterp/armv5te/binopWide2addr.S
new file mode 100644
index 0000000..3fd5747
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide2addr.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
diff --git a/vm/mterp/armv5te/debug.cpp b/vm/mterp/armv5te/debug.cpp
new file mode 100644
index 0000000..570e432
--- /dev/null
+++ b/vm/mterp/armv5te/debug.cpp
@@ -0,0 +1,82 @@
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
diff --git a/vm/mterp/armv5te/entry.S b/vm/mterp/armv5te/entry.S
new file mode 100644
index 0000000..46b26a6
--- /dev/null
+++ b/vm/mterp/armv5te/entry.S
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
new file mode 100644
index 0000000..2980fa7
--- /dev/null
+++ b/vm/mterp/armv5te/footer.S
@@ -0,0 +1,1232 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
diff --git a/vm/mterp/armv5te/header.S b/vm/mterp/armv5te/header.S
new file mode 100644
index 0000000..b3c8f0a
--- /dev/null
+++ b/vm/mterp/armv5te/header.S
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #${handler_size_bits}
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/armv5te/platform.S b/vm/mterp/armv5te/platform.S
new file mode 100644
index 0000000..ff3150b
--- /dev/null
+++ b/vm/mterp/armv5te/platform.S
@@ -0,0 +1,17 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
diff --git a/vm/mterp/armv5te/stub.S b/vm/mterp/armv5te/stub.S
new file mode 100644
index 0000000..767427b
--- /dev/null
+++ b/vm/mterp/armv5te/stub.S
@@ -0,0 +1,8 @@
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF()            @ only need to export these two
+    mov     r0, rSELF               @ self is first arg to function
+    bl      dvmMterp_${opcode}      @ call
+    LOAD_PC_FP_FROM_SELF()          @ retrieve updated values
+    FETCH_INST()                    @ load next instruction from rPC
+    GET_INST_OPCODE(ip)             @ ...trim down to just the opcode
+    GOTO_OPCODE(ip)                 @ ...and jump to the handler
diff --git a/vm/mterp/armv5te/unop.S b/vm/mterp/armv5te/unop.S
new file mode 100644
index 0000000..12d8206
--- /dev/null
+++ b/vm/mterp/armv5te/unop.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv5te/unopNarrower.S b/vm/mterp/armv5te/unopNarrower.S
new file mode 100644
index 0000000..f1ad902
--- /dev/null
+++ b/vm/mterp/armv5te/unopNarrower.S
@@ -0,0 +1,24 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unopWide.S b/vm/mterp/armv5te/unopWide.S
new file mode 100644
index 0000000..0805fdf
--- /dev/null
+++ b/vm/mterp/armv5te/unopWide.S
@@ -0,0 +1,22 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
diff --git a/vm/mterp/armv5te/unopWider.S b/vm/mterp/armv5te/unopWider.S
new file mode 100644
index 0000000..df1baea
--- /dev/null
+++ b/vm/mterp/armv5te/unopWider.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unused.S b/vm/mterp/armv5te/unused.S
new file mode 100644
index 0000000..0194f58
--- /dev/null
+++ b/vm/mterp/armv5te/unused.S
@@ -0,0 +1 @@
+    bl      common_abort
diff --git a/vm/mterp/armv5te/zcmp.S b/vm/mterp/armv5te/zcmp.S
new file mode 100644
index 0000000..bd63fe4
--- /dev/null
+++ b/vm/mterp/armv5te/zcmp.S
@@ -0,0 +1,27 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    mov${revcmp} r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6/OP_INT_TO_BYTE.S b/vm/mterp/armv6/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..40d8a5c
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxtb    r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_CHAR.S b/vm/mterp/armv6/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..3f0fdad
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"uxth    r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_SHORT.S b/vm/mterp/armv6/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..82274c4
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d81ece9
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..ec6cdf1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..af271cb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f66b1d4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"add     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..0e3a901
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"adds    r0, r0, r2", "instr":"adc     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..e7b716d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_LIT16.S b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..77bb06b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"and     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..b77fbd2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"and     r0, r0, r2", "instr":"and     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..05991be
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_CONST_4.S b/vm/mterp/armv6t2/OP_CONST_4.S
new file mode 100644
index 0000000..8ec67d7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_CONST_4.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a3b7ffb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ddiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..125230c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fdiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..8e58043
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..b4df053
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"bl     __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..cbd9c2d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..bdbb2fb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_d2f"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..f66dc5f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ac751de
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+%include "armv6t2/unopWide.S" {"instr":"bl      d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..64ca64c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2d"}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..4ba28e7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unop.S" {"instr":"bl      __aeabi_f2iz"}
+
+#if 0
+@include "armv6t2/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..168e338
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+%include "armv6t2/unopWider.S" {"instr":"bl      f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
diff --git a/vm/mterp/armv6t2/OP_IF_EQ.S b/vm/mterp/armv6t2/OP_IF_EQ.S
new file mode 100644
index 0000000..d14b10b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv6t2/OP_IF_GE.S b/vm/mterp/armv6t2/OP_IF_GE.S
new file mode 100644
index 0000000..e6c518d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv6t2/OP_IF_GT.S b/vm/mterp/armv6t2/OP_IF_GT.S
new file mode 100644
index 0000000..6e89b3c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv6t2/OP_IF_LE.S b/vm/mterp/armv6t2/OP_IF_LE.S
new file mode 100644
index 0000000..0be9f60
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv6t2/OP_IF_LT.S b/vm/mterp/armv6t2/OP_IF_LT.S
new file mode 100644
index 0000000..cea79b1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv6t2/OP_IF_NE.S b/vm/mterp/armv6t2/OP_IF_NE.S
new file mode 100644
index 0000000..ad1f936
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv6t2/OP_IGET.S b/vm/mterp/armv6t2/OP_IGET.S
new file mode 100644
index 0000000..1cf41fc
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET.S
@@ -0,0 +1,45 @@
+%default { "load":"ldr", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    $load   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_QUICK.S b/vm/mterp/armv6t2/OP_IGET_QUICK.S
new file mode 100644
index 0000000..1e913c3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE.S b/vm/mterp/armv6t2/OP_IGET_WIDE.S
new file mode 100644
index 0000000..3e826dd
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .L${opcode}_finish
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..067d40d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_INT_TO_BYTE.S b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..27f92e6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxtb    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_CHAR.S b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..db1eaa3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"uxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..38a2ef2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_i2d"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..7407a73
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"bl      __aeabi_i2f"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_LONG.S b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..e4d4221
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"mov     r1, r0, asr #31"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_SHORT.S b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..6426a9f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxth    r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_IPUT.S b/vm/mterp/armv6t2/OP_IPUT.S
new file mode 100644
index 0000000..0b219c0
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT.S
@@ -0,0 +1,45 @@
+%default { "store":"str", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    @bl      common_squeak${sqnum}
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad87b55
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE.S b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..f4ddb43
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
@@ -0,0 +1,39 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish up
+    b       common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.L${opcode}_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..09f7a8e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..f04f917
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_l2d"}
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..eaf983b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl      __aeabi_l2f"}
diff --git a/vm/mterp/armv6t2/OP_MOVE.S b/vm/mterp/armv6t2/OP_MOVE.S
new file mode 100644
index 0000000..3047158
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_MOVE_WIDE.S b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..adc2c95
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..b2b1297
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..a48a3a0
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..e822fce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binop2addr.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..a07e540
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binopLit16.S" {"instr":"mul     r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..1526a1e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,25 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_NEG_DOUBLE.S b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..52ef346
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"add     r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_FLOAT.S b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..34672d3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"add     r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_INT.S b/vm/mterp/armv6t2/OP_NEG_INT.S
new file mode 100644
index 0000000..98fb1b3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"rsb     r0, r0, #0"}
diff --git a/vm/mterp/armv6t2/OP_NEG_LONG.S b/vm/mterp/armv6t2/OP_NEG_LONG.S
new file mode 100644
index 0000000..22f45fd
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"rsbs    r0, r0, #0", "instr":"rsc     r1, r1, #0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_INT.S b/vm/mterp/armv6t2/OP_NOT_INT.S
new file mode 100644
index 0000000..5ce758e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"mvn     r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_LONG.S b/vm/mterp/armv6t2/OP_NOT_LONG.S
new file mode 100644
index 0000000..ac7e875
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"mvn     r0, r0", "instr":"mvn     r1, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..b13b90c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_LIT16.S b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..87db288
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"orr     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1891e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"orr     r0, r0, r2", "instr":"orr     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..48e4cc3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      fmod"}
diff --git a/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..8273df1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv6t2/binop2addr.S" {"instr":"bl      fmodf"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..be4951d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_LIT16.S b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..ba66b48
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binopLit16.S" {"instr":"bl      __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..c663f78
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_RSUB_INT.S b/vm/mterp/armv6t2/OP_RSUB_INT.S
new file mode 100644
index 0000000..761259c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv6t2/binopLit16.S" {"instr":"rsb     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c6959b2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asl r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..502481e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..ce0a2ce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, asr r1"}
diff --git a/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..501b3f2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..631187b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl      __aeabi_dsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..13ee1b4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl      __aeabi_fsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..a3bd5e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"sub     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..46dda45
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"subs    r0, r0, r2", "instr":"sbc     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..5d5808e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and     r1, r1, #31", "instr":"mov     r0, r0, lsr r1"}
diff --git a/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1fcef4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..49c82d6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..6fe178d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"eor     r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..8d5ba2b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"eor     r0, r0, r2", "instr":"eor     r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/bincmp.S b/vm/mterp/armv6t2/bincmp.S
new file mode 100644
index 0000000..8d8c48d
--- /dev/null
+++ b/vm/mterp/armv6t2/bincmp.S
@@ -0,0 +1,29 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    mov${revcmp} r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv6t2/binop2addr.S b/vm/mterp/armv6t2/binop2addr.S
new file mode 100644
index 0000000..b402156
--- /dev/null
+++ b/vm/mterp/armv6t2/binop2addr.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopLit16.S b/vm/mterp/armv6t2/binopLit16.S
new file mode 100644
index 0000000..1f67524
--- /dev/null
+++ b/vm/mterp/armv6t2/binopLit16.S
@@ -0,0 +1,29 @@
+%default {"result":"r0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if $chkzero
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    $instr                              @ $result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG($result, r9)               @ vAA<- $result
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopWide2addr.S b/vm/mterp/armv6t2/binopWide2addr.S
new file mode 100644
index 0000000..0b4691f
--- /dev/null
+++ b/vm/mterp/armv6t2/binopWide2addr.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if $chkzero
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {$result0,$result1}     @ vAA/vAA+1<- $result0/$result1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
diff --git a/vm/mterp/armv6t2/unop.S b/vm/mterp/armv6t2/unop.S
new file mode 100644
index 0000000..58465fe
--- /dev/null
+++ b/vm/mterp/armv6t2/unop.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
diff --git a/vm/mterp/armv6t2/unopNarrower.S b/vm/mterp/armv6t2/unopNarrower.S
new file mode 100644
index 0000000..224e8e7
--- /dev/null
+++ b/vm/mterp/armv6t2/unopNarrower.S
@@ -0,0 +1,23 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv6t2/unopWide.S b/vm/mterp/armv6t2/unopWide.S
new file mode 100644
index 0000000..e0a303e
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWide.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $preinstr                           @ optional op; may set condition codes
+    $instr                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/armv6t2/unopWider.S b/vm/mterp/armv6t2/unopWider.S
new file mode 100644
index 0000000..7ec221b
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWider.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    $preinstr                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    $instr                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/armv7-a/OP_DIV_INT.S b/vm/mterp/armv7-a/OP_DIV_INT.S
new file mode 100644
index 0000000..9c88b26
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_DIV_INT.S
@@ -0,0 +1,31 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl    __aeabi_idiv                  @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
diff --git a/vm/mterp/armv7-a/OP_DIV_INT_2ADDR.S b/vm/mterp/armv7-a/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..be6de6d
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,30 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/armv7-a/OP_DIV_INT_LIT16.S b/vm/mterp/armv7-a/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..f140c3c
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_DIV_INT_LIT16.S
@@ -0,0 +1,29 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv7-a/OP_DIV_INT_LIT8.S b/vm/mterp/armv7-a/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..08d67fa
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_DIV_INT_LIT8.S
@@ -0,0 +1,30 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl   __aeabi_idiv                   @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
diff --git a/vm/mterp/armv7-a/OP_REM_INT.S b/vm/mterp/armv7-a/OP_REM_INT.S
new file mode 100644
index 0000000..cd03338
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_REM_INT.S
@@ -0,0 +1,34 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls  r1, r1, r2, r0                 @ r1<- op, r0-r2 changed
+#else
+    bl   __aeabi_idivmod                @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
diff --git a/vm/mterp/armv7-a/OP_REM_INT_2ADDR.S b/vm/mterp/armv7-a/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..5099642
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_REM_INT_2ADDR.S
@@ -0,0 +1,33 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl      __aeabi_idivmod             @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/armv7-a/OP_REM_INT_LIT16.S b/vm/mterp/armv7-a/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..3fc86a4
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_REM_INT_LIT16.S
@@ -0,0 +1,32 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl     __aeabi_idivmod              @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
diff --git a/vm/mterp/armv7-a/OP_REM_INT_LIT8.S b/vm/mterp/armv7-a/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..61bffff
--- /dev/null
+++ b/vm/mterp/armv7-a/OP_REM_INT_LIT8.S
@@ -0,0 +1,33 @@
+%default {}
+%verify "executed"
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl       __aeabi_idivmod            @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
diff --git a/vm/mterp/armv7-a/platform.S b/vm/mterp/armv7-a/platform.S
new file mode 100644
index 0000000..b11350a
--- /dev/null
+++ b/vm/mterp/armv7-a/platform.S
@@ -0,0 +1,31 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb ish
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb ishst
+#else
+    /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/c/OP_ADD_DOUBLE.cpp b/vm/mterp/c/OP_ADD_DOUBLE.cpp
new file mode 100644
index 0000000..571aeb8
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..af952cb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT.cpp b/vm/mterp/c/OP_ADD_FLOAT.cpp
new file mode 100644
index 0000000..dab7d33
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..a068fd0
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT.cpp b/vm/mterp/c/OP_ADD_INT.cpp
new file mode 100644
index 0000000..bfaa590
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_2ADDR.cpp b/vm/mterp/c/OP_ADD_INT_2ADDR.cpp
new file mode 100644
index 0000000..dfd3289
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT16.cpp b/vm/mterp/c/OP_ADD_INT_LIT16.cpp
new file mode 100644
index 0000000..442ab40
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT8.cpp b/vm/mterp/c/OP_ADD_INT_LIT8.cpp
new file mode 100644
index 0000000..1455599
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG.cpp b/vm/mterp/c/OP_ADD_LONG.cpp
new file mode 100644
index 0000000..25d1f47
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp b/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp
new file mode 100644
index 0000000..4ae71bb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AGET.cpp b/vm/mterp/c/OP_AGET.cpp
new file mode 100644
index 0000000..766beaf
--- /dev/null
+++ b/vm/mterp/c/OP_AGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BOOLEAN.cpp b/vm/mterp/c/OP_AGET_BOOLEAN.cpp
new file mode 100644
index 0000000..d63bc10
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BYTE.cpp b/vm/mterp/c/OP_AGET_BYTE.cpp
new file mode 100644
index 0000000..61ecc05
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_CHAR.cpp b/vm/mterp/c/OP_AGET_CHAR.cpp
new file mode 100644
index 0000000..55e16ef
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_OBJECT.cpp b/vm/mterp/c/OP_AGET_OBJECT.cpp
new file mode 100644
index 0000000..903637c
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_SHORT.cpp b/vm/mterp/c/OP_AGET_SHORT.cpp
new file mode 100644
index 0000000..176b4a6
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_WIDE.cpp b/vm/mterp/c/OP_AGET_WIDE.cpp
new file mode 100644
index 0000000..e7974cb
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT.cpp b/vm/mterp/c/OP_AND_INT.cpp
new file mode 100644
index 0000000..3cf31cb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_2ADDR.cpp b/vm/mterp/c/OP_AND_INT_2ADDR.cpp
new file mode 100644
index 0000000..9f69292
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT16.cpp b/vm/mterp/c/OP_AND_INT_LIT16.cpp
new file mode 100644
index 0000000..19f825b
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT8.cpp b/vm/mterp/c/OP_AND_INT_LIT8.cpp
new file mode 100644
index 0000000..c0e1315
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG.cpp b/vm/mterp/c/OP_AND_LONG.cpp
new file mode 100644
index 0000000..1c638fb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG_2ADDR.cpp b/vm/mterp/c/OP_AND_LONG_2ADDR.cpp
new file mode 100644
index 0000000..23c464d
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_APUT.cpp b/vm/mterp/c/OP_APUT.cpp
new file mode 100644
index 0000000..07d3e04
--- /dev/null
+++ b/vm/mterp/c/OP_APUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BOOLEAN.cpp b/vm/mterp/c/OP_APUT_BOOLEAN.cpp
new file mode 100644
index 0000000..fc69147
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BYTE.cpp b/vm/mterp/c/OP_APUT_BYTE.cpp
new file mode 100644
index 0000000..45aeb0b
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_CHAR.cpp b/vm/mterp/c/OP_APUT_CHAR.cpp
new file mode 100644
index 0000000..1553c27
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_OBJECT.cpp b/vm/mterp/c/OP_APUT_OBJECT.cpp
new file mode 100644
index 0000000..0730b72
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_OBJECT.cpp
@@ -0,0 +1,38 @@
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_APUT_SHORT.cpp b/vm/mterp/c/OP_APUT_SHORT.cpp
new file mode 100644
index 0000000..a72b5ea
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_WIDE.cpp b/vm/mterp/c/OP_APUT_WIDE.cpp
new file mode 100644
index 0000000..39c8cfa
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_ARRAY_LENGTH.cpp b/vm/mterp/c/OP_ARRAY_LENGTH.cpp
new file mode 100644
index 0000000..0d5a933
--- /dev/null
+++ b/vm/mterp/c/OP_ARRAY_LENGTH.cpp
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_BREAKPOINT.cpp b/vm/mterp/c/OP_BREAKPOINT.cpp
new file mode 100644
index 0000000..3041190
--- /dev/null
+++ b/vm/mterp/c/OP_BREAKPOINT.cpp
@@ -0,0 +1,24 @@
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_CHECK_CAST.cpp b/vm/mterp/c/OP_CHECK_CAST.cpp
new file mode 100644
index 0000000..2c4a304
--- /dev/null
+++ b/vm/mterp/c/OP_CHECK_CAST.cpp
@@ -0,0 +1,31 @@
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_DOUBLE.cpp b/vm/mterp/c/OP_CMPG_DOUBLE.cpp
new file mode 100644
index 0000000..3f4082c
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_FLOAT.cpp b/vm/mterp/c/OP_CMPG_FLOAT.cpp
new file mode 100644
index 0000000..0bba49e
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_DOUBLE.cpp b/vm/mterp/c/OP_CMPL_DOUBLE.cpp
new file mode 100644
index 0000000..4da18b4
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_FLOAT.cpp b/vm/mterp/c/OP_CMPL_FLOAT.cpp
new file mode 100644
index 0000000..7916193
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMP_LONG.cpp b/vm/mterp/c/OP_CMP_LONG.cpp
new file mode 100644
index 0000000..a0e412c
--- /dev/null
+++ b/vm/mterp/c/OP_CMP_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
diff --git a/vm/mterp/c/OP_CONST.cpp b/vm/mterp/c/OP_CONST.cpp
new file mode 100644
index 0000000..e281a51
--- /dev/null
+++ b/vm/mterp/c/OP_CONST.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_16.cpp b/vm/mterp/c/OP_CONST_16.cpp
new file mode 100644
index 0000000..f58f50c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_4.cpp b/vm/mterp/c/OP_CONST_4.cpp
new file mode 100644
index 0000000..800ef9a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_4.cpp
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_CLASS.cpp b/vm/mterp/c/OP_CONST_CLASS.cpp
new file mode 100644
index 0000000..9c60a27
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_CLASS.cpp
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_HIGH16.cpp b/vm/mterp/c/OP_CONST_HIGH16.cpp
new file mode 100644
index 0000000..26b22f4
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_HIGH16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING.cpp b/vm/mterp/c/OP_CONST_STRING.cpp
new file mode 100644
index 0000000..748119a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING.cpp
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp b/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp
new file mode 100644
index 0000000..435b34c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING_JUMBO.cpp
@@ -0,0 +1,20 @@
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE.cpp b/vm/mterp/c/OP_CONST_WIDE.cpp
new file mode 100644
index 0000000..ccb3955
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE.cpp
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_16.cpp b/vm/mterp/c/OP_CONST_WIDE_16.cpp
new file mode 100644
index 0000000..da69f37
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_32.cpp b/vm/mterp/c/OP_CONST_WIDE_32.cpp
new file mode 100644
index 0000000..ad4acbb
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_32.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp b/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp
new file mode 100644
index 0000000..bcc0664
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_HIGH16.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE.cpp b/vm/mterp/c/OP_DIV_DOUBLE.cpp
new file mode 100644
index 0000000..d6e4b55
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..85a1523
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT.cpp b/vm/mterp/c/OP_DIV_FLOAT.cpp
new file mode 100644
index 0000000..2c5049b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..cd7b4d9
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT.cpp b/vm/mterp/c/OP_DIV_INT.cpp
new file mode 100644
index 0000000..af6e8c6
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_2ADDR.cpp b/vm/mterp/c/OP_DIV_INT_2ADDR.cpp
new file mode 100644
index 0000000..80c0da7
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT16.cpp b/vm/mterp/c/OP_DIV_INT_LIT16.cpp
new file mode 100644
index 0000000..4e8d901
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT8.cpp b/vm/mterp/c/OP_DIV_INT_LIT8.cpp
new file mode 100644
index 0000000..eec5389
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG.cpp b/vm/mterp/c/OP_DIV_LONG.cpp
new file mode 100644
index 0000000..557b56b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp b/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp
new file mode 100644
index 0000000..00e0e6c
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp
new file mode 100644
index 0000000..152e5fd
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_INT.cpp b/vm/mterp/c/OP_DOUBLE_TO_INT.cpp
new file mode 100644
index 0000000..e210b92
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_INT.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp b/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp
new file mode 100644
index 0000000..44d548c
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_LONG.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE.cpp b/vm/mterp/c/OP_EXECUTE_INLINE.cpp
new file mode 100644
index 0000000..288ccc9
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE.cpp
@@ -0,0 +1,59 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp
new file mode 100644
index 0000000..467f0e9
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.cpp
@@ -0,0 +1,43 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp b/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp
new file mode 100644
index 0000000..fad7dbb
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp
new file mode 100644
index 0000000..06c3a79
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
diff --git a/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp b/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp
new file mode 100644
index 0000000..678bb32
--- /dev/null
+++ b/vm/mterp/c/OP_FILL_ARRAY_DATA.cpp
@@ -0,0 +1,27 @@
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp
new file mode 100644
index 0000000..ea5e7a6
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_INT.cpp b/vm/mterp/c/OP_FLOAT_TO_INT.cpp
new file mode 100644
index 0000000..15522f8
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_INT.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_LONG.cpp b/vm/mterp/c/OP_FLOAT_TO_LONG.cpp
new file mode 100644
index 0000000..03bd30d
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_LONG.cpp
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_GOTO.cpp b/vm/mterp/c/OP_GOTO.cpp
new file mode 100644
index 0000000..0e10384
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO.cpp
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_16.cpp b/vm/mterp/c/OP_GOTO_16.cpp
new file mode 100644
index 0000000..f0541fd
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_16.cpp
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_32.cpp b/vm/mterp/c/OP_GOTO_32.cpp
new file mode 100644
index 0000000..1b1815c
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_32.cpp
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQ.cpp b/vm/mterp/c/OP_IF_EQ.cpp
new file mode 100644
index 0000000..2c3b9b5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQZ.cpp b/vm/mterp/c/OP_IF_EQZ.cpp
new file mode 100644
index 0000000..d2dd1aa
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GE.cpp b/vm/mterp/c/OP_IF_GE.cpp
new file mode 100644
index 0000000..8aa85c4
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GEZ.cpp b/vm/mterp/c/OP_IF_GEZ.cpp
new file mode 100644
index 0000000..8c4b78a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GT.cpp b/vm/mterp/c/OP_IF_GT.cpp
new file mode 100644
index 0000000..d35eb29
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GTZ.cpp b/vm/mterp/c/OP_IF_GTZ.cpp
new file mode 100644
index 0000000..63a0073
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GTZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LE.cpp b/vm/mterp/c/OP_IF_LE.cpp
new file mode 100644
index 0000000..f4b213a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LEZ.cpp b/vm/mterp/c/OP_IF_LEZ.cpp
new file mode 100644
index 0000000..1d57a50
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LT.cpp b/vm/mterp/c/OP_IF_LT.cpp
new file mode 100644
index 0000000..0233892
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LTZ.cpp b/vm/mterp/c/OP_IF_LTZ.cpp
new file mode 100644
index 0000000..b4b9be2
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LTZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NE.cpp b/vm/mterp/c/OP_IF_NE.cpp
new file mode 100644
index 0000000..8da70a5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NEZ.cpp b/vm/mterp/c/OP_IF_NEZ.cpp
new file mode 100644
index 0000000..209e836
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NEZ.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IGET.cpp b/vm/mterp/c/OP_IGET.cpp
new file mode 100644
index 0000000..c6333e5
--- /dev/null
+++ b/vm/mterp/c/OP_IGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BOOLEAN.cpp b/vm/mterp/c/OP_IGET_BOOLEAN.cpp
new file mode 100644
index 0000000..a5a47be
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BYTE.cpp b/vm/mterp/c/OP_IGET_BYTE.cpp
new file mode 100644
index 0000000..647f311
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_CHAR.cpp b/vm/mterp/c/OP_IGET_CHAR.cpp
new file mode 100644
index 0000000..9a8adb0
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT.cpp b/vm/mterp/c/OP_IGET_OBJECT.cpp
new file mode 100644
index 0000000..03c9e50
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp b/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp
new file mode 100644
index 0000000..2ac3a54
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..3577552
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_QUICK.cpp b/vm/mterp/c/OP_IGET_QUICK.cpp
new file mode 100644
index 0000000..b5724cc
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_SHORT.cpp b/vm/mterp/c/OP_IGET_SHORT.cpp
new file mode 100644
index 0000000..3e77789
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_VOLATILE.cpp b/vm/mterp/c/OP_IGET_VOLATILE.cpp
new file mode 100644
index 0000000..7a3be56
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE.cpp b/vm/mterp/c/OP_IGET_WIDE.cpp
new file mode 100644
index 0000000..cb1fcca
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp b/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp
new file mode 100644
index 0000000..adb4fc1
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..a080823
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INSTANCE_OF.cpp b/vm/mterp/c/OP_INSTANCE_OF.cpp
new file mode 100644
index 0000000..8b8f9d3
--- /dev/null
+++ b/vm/mterp/c/OP_INSTANCE_OF.cpp
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_BYTE.cpp b/vm/mterp/c/OP_INT_TO_BYTE.cpp
new file mode 100644
index 0000000..ea75747
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_CHAR.cpp b/vm/mterp/c/OP_INT_TO_CHAR.cpp
new file mode 100644
index 0000000..45ae0df
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_DOUBLE.cpp b/vm/mterp/c/OP_INT_TO_DOUBLE.cpp
new file mode 100644
index 0000000..624c702
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_FLOAT.cpp b/vm/mterp/c/OP_INT_TO_FLOAT.cpp
new file mode 100644
index 0000000..fd15199
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_LONG.cpp b/vm/mterp/c/OP_INT_TO_LONG.cpp
new file mode 100644
index 0000000..8bc4223
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_SHORT.cpp b/vm/mterp/c/OP_INT_TO_SHORT.cpp
new file mode 100644
index 0000000..0f06739
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT.cpp b/vm/mterp/c/OP_INVOKE_DIRECT.cpp
new file mode 100644
index 0000000..58cfe5b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp
new file mode 100644
index 0000000..9877bbe
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE.cpp b/vm/mterp/c/OP_INVOKE_INTERFACE.cpp
new file mode 100644
index 0000000..9c639d5
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp
new file mode 100644
index 0000000..6244c9e
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp b/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp
new file mode 100644
index 0000000..a22446f
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_OBJECT_INIT_RANGE.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC.cpp b/vm/mterp/c/OP_INVOKE_STATIC.cpp
new file mode 100644
index 0000000..81f3d62
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp
new file mode 100644
index 0000000..3fc4c35
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER.cpp b/vm/mterp/c/OP_INVOKE_SUPER.cpp
new file mode 100644
index 0000000..e7baea4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp
new file mode 100644
index 0000000..b66e033
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp
new file mode 100644
index 0000000..879497b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp
new file mode 100644
index 0000000..724e3a0
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp
new file mode 100644
index 0000000..29a4560
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp
new file mode 100644
index 0000000..244fed4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp
new file mode 100644
index 0000000..9adb4ad
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp
new file mode 100644
index 0000000..94671ae
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
diff --git a/vm/mterp/c/OP_IPUT.cpp b/vm/mterp/c/OP_IPUT.cpp
new file mode 100644
index 0000000..9d503ef
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BOOLEAN.cpp b/vm/mterp/c/OP_IPUT_BOOLEAN.cpp
new file mode 100644
index 0000000..7fe4929
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BYTE.cpp b/vm/mterp/c/OP_IPUT_BYTE.cpp
new file mode 100644
index 0000000..8a49fb7
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_CHAR.cpp b/vm/mterp/c/OP_IPUT_CHAR.cpp
new file mode 100644
index 0000000..b2812c2
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT.cpp b/vm/mterp/c/OP_IPUT_OBJECT.cpp
new file mode 100644
index 0000000..dbfb5ab
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT.cpp
@@ -0,0 +1,13 @@
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp
new file mode 100644
index 0000000..8670188
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..cce63c1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_QUICK.cpp b/vm/mterp/c/OP_IPUT_QUICK.cpp
new file mode 100644
index 0000000..483b9b1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_SHORT.cpp b/vm/mterp/c/OP_IPUT_SHORT.cpp
new file mode 100644
index 0000000..0a63ebc
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_VOLATILE.cpp
new file mode 100644
index 0000000..814379e
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE.cpp b/vm/mterp/c/OP_IPUT_WIDE.cpp
new file mode 100644
index 0000000..bb4926c
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp b/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp
new file mode 100644
index 0000000..691630b
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_QUICK.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..d888b4a
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp b/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp
new file mode 100644
index 0000000..91b5eb2
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_FLOAT.cpp b/vm/mterp/c/OP_LONG_TO_FLOAT.cpp
new file mode 100644
index 0000000..ff1f5fb
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_INT.cpp b/vm/mterp/c/OP_LONG_TO_INT.cpp
new file mode 100644
index 0000000..87c9a2e
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_ENTER.cpp b/vm/mterp/c/OP_MONITOR_ENTER.cpp
new file mode 100644
index 0000000..976aab3
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_ENTER.cpp
@@ -0,0 +1,16 @@
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_EXIT.cpp b/vm/mterp/c/OP_MONITOR_EXIT.cpp
new file mode 100644
index 0000000..c26585d
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_EXIT.cpp
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE.cpp b/vm/mterp/c/OP_MOVE.cpp
new file mode 100644
index 0000000..6666199
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_16.cpp b/vm/mterp/c/OP_MOVE_16.cpp
new file mode 100644
index 0000000..53af5d5
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_16.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_EXCEPTION.cpp b/vm/mterp/c/OP_MOVE_EXCEPTION.cpp
new file mode 100644
index 0000000..86587ca
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_EXCEPTION.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_FROM16.cpp b/vm/mterp/c/OP_MOVE_FROM16.cpp
new file mode 100644
index 0000000..59fc285
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_FROM16.cpp
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_OBJECT.cpp b/vm/mterp/c/OP_MOVE_OBJECT.cpp
new file mode 100644
index 0000000..579095f
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE.cpp"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_16.cpp b/vm/mterp/c/OP_MOVE_OBJECT_16.cpp
new file mode 100644
index 0000000..89cfb77
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_16.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_16.cpp"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp
new file mode 100644
index 0000000..9451b9e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_FROM16.cpp"
diff --git a/vm/mterp/c/OP_MOVE_RESULT.cpp b/vm/mterp/c/OP_MOVE_RESULT.cpp
new file mode 100644
index 0000000..ddf535b
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp
new file mode 100644
index 0000000..08c5c02
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_MOVE_RESULT.cpp"
diff --git a/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp b/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp
new file mode 100644
index 0000000..f6ec8d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_WIDE.cpp
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE.cpp b/vm/mterp/c/OP_MOVE_WIDE.cpp
new file mode 100644
index 0000000..9ee323d
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE.cpp
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_16.cpp b/vm/mterp/c/OP_MOVE_WIDE_16.cpp
new file mode 100644
index 0000000..e3d0e16
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_16.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp b/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp
new file mode 100644
index 0000000..cdbaa2e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_FROM16.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE.cpp b/vm/mterp/c/OP_MUL_DOUBLE.cpp
new file mode 100644
index 0000000..3e65efa
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..905b6a7
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT.cpp b/vm/mterp/c/OP_MUL_FLOAT.cpp
new file mode 100644
index 0000000..310495c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..03623ca
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT.cpp b/vm/mterp/c/OP_MUL_INT.cpp
new file mode 100644
index 0000000..b723a29
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_2ADDR.cpp b/vm/mterp/c/OP_MUL_INT_2ADDR.cpp
new file mode 100644
index 0000000..f7a179c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT16.cpp b/vm/mterp/c/OP_MUL_INT_LIT16.cpp
new file mode 100644
index 0000000..3a34dbc
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT8.cpp b/vm/mterp/c/OP_MUL_INT_LIT8.cpp
new file mode 100644
index 0000000..2ca0036
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG.cpp b/vm/mterp/c/OP_MUL_LONG.cpp
new file mode 100644
index 0000000..768a2ad
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp b/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp
new file mode 100644
index 0000000..1469a22
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_DOUBLE.cpp b/vm/mterp/c/OP_NEG_DOUBLE.cpp
new file mode 100644
index 0000000..805082c
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_FLOAT.cpp b/vm/mterp/c/OP_NEG_FLOAT.cpp
new file mode 100644
index 0000000..00e14f5
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_INT.cpp b/vm/mterp/c/OP_NEG_INT.cpp
new file mode 100644
index 0000000..9b97bef
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
diff --git a/vm/mterp/c/OP_NEG_LONG.cpp b/vm/mterp/c/OP_NEG_LONG.cpp
new file mode 100644
index 0000000..52d553a
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_NEW_ARRAY.cpp b/vm/mterp/c/OP_NEW_ARRAY.cpp
new file mode 100644
index 0000000..6d6771a
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_ARRAY.cpp
@@ -0,0 +1,35 @@
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NEW_INSTANCE.cpp b/vm/mterp/c/OP_NEW_INSTANCE.cpp
new file mode 100644
index 0000000..b0b9c18
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_INSTANCE.cpp
@@ -0,0 +1,48 @@
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NOP.cpp b/vm/mterp/c/OP_NOP.cpp
new file mode 100644
index 0000000..d9fd744
--- /dev/null
+++ b/vm/mterp/c/OP_NOP.cpp
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_NOT_INT.cpp b/vm/mterp/c/OP_NOT_INT.cpp
new file mode 100644
index 0000000..e585f62
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
diff --git a/vm/mterp/c/OP_NOT_LONG.cpp b/vm/mterp/c/OP_NOT_LONG.cpp
new file mode 100644
index 0000000..4fb393a
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT.cpp b/vm/mterp/c/OP_OR_INT.cpp
new file mode 100644
index 0000000..19e397b
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_2ADDR.cpp b/vm/mterp/c/OP_OR_INT_2ADDR.cpp
new file mode 100644
index 0000000..5d5fde0
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT16.cpp b/vm/mterp/c/OP_OR_INT_LIT16.cpp
new file mode 100644
index 0000000..5a682fa
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT8.cpp b/vm/mterp/c/OP_OR_INT_LIT8.cpp
new file mode 100644
index 0000000..40b9837
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG.cpp b/vm/mterp/c/OP_OR_LONG.cpp
new file mode 100644
index 0000000..62f4dd3
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG_2ADDR.cpp b/vm/mterp/c/OP_OR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..03a7c65
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_PACKED_SWITCH.cpp b/vm/mterp/c/OP_PACKED_SWITCH.cpp
new file mode 100644
index 0000000..c820e80
--- /dev/null
+++ b/vm/mterp/c/OP_PACKED_SWITCH.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE.cpp b/vm/mterp/c/OP_REM_DOUBLE.cpp
new file mode 100644
index 0000000..343e25e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE.cpp
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..392eacf
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT.cpp b/vm/mterp/c/OP_REM_FLOAT.cpp
new file mode 100644
index 0000000..9604b30
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT.cpp
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..87bb31e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT_2ADDR.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT.cpp b/vm/mterp/c/OP_REM_INT.cpp
new file mode 100644
index 0000000..0e3efe6
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_2ADDR.cpp b/vm/mterp/c/OP_REM_INT_2ADDR.cpp
new file mode 100644
index 0000000..5801f35
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT16.cpp b/vm/mterp/c/OP_REM_INT_LIT16.cpp
new file mode 100644
index 0000000..a4dabe8
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT8.cpp b/vm/mterp/c/OP_REM_INT_LIT8.cpp
new file mode 100644
index 0000000..2bc88be
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG.cpp b/vm/mterp/c/OP_REM_LONG.cpp
new file mode 100644
index 0000000..fb2ba71
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG_2ADDR.cpp b/vm/mterp/c/OP_REM_LONG_2ADDR.cpp
new file mode 100644
index 0000000..3049770
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_RETURN.cpp b/vm/mterp/c/OP_RETURN.cpp
new file mode 100644
index 0000000..89d3b3b
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_OBJECT.cpp b/vm/mterp/c/OP_RETURN_OBJECT.cpp
new file mode 100644
index 0000000..d8bae43
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_OBJECT.cpp
@@ -0,0 +1 @@
+%include "c/OP_RETURN.cpp"
diff --git a/vm/mterp/c/OP_RETURN_VOID.cpp b/vm/mterp/c/OP_RETURN_VOID.cpp
new file mode 100644
index 0000000..7431f60
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp b/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp
new file mode 100644
index 0000000..312402e
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID_BARRIER.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_WIDE.cpp b/vm/mterp/c/OP_RETURN_WIDE.cpp
new file mode 100644
index 0000000..a27bfd4
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_WIDE.cpp
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT.cpp b/vm/mterp/c/OP_RSUB_INT.cpp
new file mode 100644
index 0000000..336ca55
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT.cpp
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT_LIT8.cpp b/vm/mterp/c/OP_RSUB_INT_LIT8.cpp
new file mode 100644
index 0000000..742854b
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT_LIT8.cpp
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_SGET.cpp b/vm/mterp/c/OP_SGET.cpp
new file mode 100644
index 0000000..5297cd7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BOOLEAN.cpp b/vm/mterp/c/OP_SGET_BOOLEAN.cpp
new file mode 100644
index 0000000..7c5d45e
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BYTE.cpp b/vm/mterp/c/OP_SGET_BYTE.cpp
new file mode 100644
index 0000000..b37cab4
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_CHAR.cpp b/vm/mterp/c/OP_SGET_CHAR.cpp
new file mode 100644
index 0000000..7ede5ec
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT.cpp b/vm/mterp/c/OP_SGET_OBJECT.cpp
new file mode 100644
index 0000000..9f3b63d
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..0a9049f
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_SHORT.cpp b/vm/mterp/c/OP_SGET_SHORT.cpp
new file mode 100644
index 0000000..cd1fe4c
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_VOLATILE.cpp b/vm/mterp/c/OP_SGET_VOLATILE.cpp
new file mode 100644
index 0000000..6713a54
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE.cpp b/vm/mterp/c/OP_SGET_WIDE.cpp
new file mode 100644
index 0000000..817c6e7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..26a67bf
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT.cpp b/vm/mterp/c/OP_SHL_INT.cpp
new file mode 100644
index 0000000..e32af49
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_2ADDR.cpp b/vm/mterp/c/OP_SHL_INT_2ADDR.cpp
new file mode 100644
index 0000000..c5f5399
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_LIT8.cpp b/vm/mterp/c/OP_SHL_INT_LIT8.cpp
new file mode 100644
index 0000000..009d14e
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG.cpp b/vm/mterp/c/OP_SHL_LONG.cpp
new file mode 100644
index 0000000..f6b502a
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp b/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp
new file mode 100644
index 0000000..b8a9954
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT.cpp b/vm/mterp/c/OP_SHR_INT.cpp
new file mode 100644
index 0000000..3834824
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_2ADDR.cpp b/vm/mterp/c/OP_SHR_INT_2ADDR.cpp
new file mode 100644
index 0000000..c76c178
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_LIT8.cpp b/vm/mterp/c/OP_SHR_INT_LIT8.cpp
new file mode 100644
index 0000000..e2657d7
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG.cpp b/vm/mterp/c/OP_SHR_LONG.cpp
new file mode 100644
index 0000000..357a666
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp b/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..43e27ea
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SPARSE_SWITCH.cpp b/vm/mterp/c/OP_SPARSE_SWITCH.cpp
new file mode 100644
index 0000000..9ca16ad
--- /dev/null
+++ b/vm/mterp/c/OP_SPARSE_SWITCH.cpp
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_SPUT.cpp b/vm/mterp/c/OP_SPUT.cpp
new file mode 100644
index 0000000..286e64c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BOOLEAN.cpp b/vm/mterp/c/OP_SPUT_BOOLEAN.cpp
new file mode 100644
index 0000000..55ceb11
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BOOLEAN.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BYTE.cpp b/vm/mterp/c/OP_SPUT_BYTE.cpp
new file mode 100644
index 0000000..d242fe1
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BYTE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_CHAR.cpp b/vm/mterp/c/OP_SPUT_CHAR.cpp
new file mode 100644
index 0000000..18a2f06
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_CHAR.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT.cpp b/vm/mterp/c/OP_SPUT_OBJECT.cpp
new file mode 100644
index 0000000..fb223d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp
new file mode 100644
index 0000000..38d6c0d
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_SHORT.cpp b/vm/mterp/c/OP_SPUT_SHORT.cpp
new file mode 100644
index 0000000..c6cd8d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_SHORT.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_VOLATILE.cpp
new file mode 100644
index 0000000..7899d05
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE.cpp b/vm/mterp/c/OP_SPUT_WIDE.cpp
new file mode 100644
index 0000000..0c74651
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp
new file mode 100644
index 0000000..bdf552c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.cpp
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE.cpp b/vm/mterp/c/OP_SUB_DOUBLE.cpp
new file mode 100644
index 0000000..64a112d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp
new file mode 100644
index 0000000..5870400
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT.cpp b/vm/mterp/c/OP_SUB_FLOAT.cpp
new file mode 100644
index 0000000..96c5fbd
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp
new file mode 100644
index 0000000..802935c
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT.cpp b/vm/mterp/c/OP_SUB_INT.cpp
new file mode 100644
index 0000000..2c1006d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT_2ADDR.cpp b/vm/mterp/c/OP_SUB_INT_2ADDR.cpp
new file mode 100644
index 0000000..328ed33
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG.cpp b/vm/mterp/c/OP_SUB_LONG.cpp
new file mode 100644
index 0000000..bace11a
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp b/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp
new file mode 100644
index 0000000..f234dd4
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_THROW.cpp b/vm/mterp/c/OP_THROW.cpp
new file mode 100644
index 0000000..6fde0cc
--- /dev/null
+++ b/vm/mterp/c/OP_THROW.cpp
@@ -0,0 +1,24 @@
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
diff --git a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp
new file mode 100644
index 0000000..85cc8fb
--- /dev/null
+++ b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.cpp
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3E.cpp b/vm/mterp/c/OP_UNUSED_3E.cpp
new file mode 100644
index 0000000..9ecf8e3
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3E.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3F.cpp b/vm/mterp/c/OP_UNUSED_3F.cpp
new file mode 100644
index 0000000..9d1d68d
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3F.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_40.cpp b/vm/mterp/c/OP_UNUSED_40.cpp
new file mode 100644
index 0000000..f73a59c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_40.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_41.cpp b/vm/mterp/c/OP_UNUSED_41.cpp
new file mode 100644
index 0000000..38747e6
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_41.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_42.cpp b/vm/mterp/c/OP_UNUSED_42.cpp
new file mode 100644
index 0000000..154d293
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_42.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_43.cpp b/vm/mterp/c/OP_UNUSED_43.cpp
new file mode 100644
index 0000000..c7e702c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_43.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_73.cpp b/vm/mterp/c/OP_UNUSED_73.cpp
new file mode 100644
index 0000000..85aa95f
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_73.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_79.cpp b/vm/mterp/c/OP_UNUSED_79.cpp
new file mode 100644
index 0000000..1fa86e9
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_79.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_7A.cpp b/vm/mterp/c/OP_UNUSED_7A.cpp
new file mode 100644
index 0000000..beab006
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_7A.cpp
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_FF.cpp b/vm/mterp/c/OP_UNUSED_FF.cpp
new file mode 100644
index 0000000..1348aec
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_FF.cpp
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT.cpp b/vm/mterp/c/OP_USHR_INT.cpp
new file mode 100644
index 0000000..7596c94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_2ADDR.cpp b/vm/mterp/c/OP_USHR_INT_2ADDR.cpp
new file mode 100644
index 0000000..5fa2b94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_LIT8.cpp b/vm/mterp/c/OP_USHR_INT_LIT8.cpp
new file mode 100644
index 0000000..0d325d7
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG.cpp b/vm/mterp/c/OP_USHR_LONG.cpp
new file mode 100644
index 0000000..9b7e757
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp b/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..4ac0598
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT.cpp b/vm/mterp/c/OP_XOR_INT.cpp
new file mode 100644
index 0000000..d591909
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_2ADDR.cpp b/vm/mterp/c/OP_XOR_INT_2ADDR.cpp
new file mode 100644
index 0000000..7d32879
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT16.cpp b/vm/mterp/c/OP_XOR_INT_LIT16.cpp
new file mode 100644
index 0000000..c204b79
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT16.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT8.cpp b/vm/mterp/c/OP_XOR_INT_LIT8.cpp
new file mode 100644
index 0000000..f01773a
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT8.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG.cpp b/vm/mterp/c/OP_XOR_LONG.cpp
new file mode 100644
index 0000000..d3dbc4c
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp b/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp
new file mode 100644
index 0000000..e7a50f4
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG_2ADDR.cpp
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/gotoTargets.cpp b/vm/mterp/c/gotoTargets.cpp
new file mode 100644
index 0000000..452bee8
--- /dev/null
+++ b/vm/mterp/c/gotoTargets.cpp
@@ -0,0 +1,971 @@
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
diff --git a/vm/mterp/c/header.cpp b/vm/mterp/c/header.cpp
new file mode 100644
index 0000000..709e4c5
--- /dev/null
+++ b/vm/mterp/c/header.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
diff --git a/vm/mterp/c/opcommon.cpp b/vm/mterp/c/opcommon.cpp
new file mode 100644
index 0000000..a49ed86
--- /dev/null
+++ b/vm/mterp/c/opcommon.cpp
@@ -0,0 +1,647 @@
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
diff --git a/vm/mterp/common/FindInterface.h b/vm/mterp/common/FindInterface.h
new file mode 100644
index 0000000..23e6f0e
--- /dev/null
+++ b/vm/mterp/common/FindInterface.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+
+extern "C" {
+
+/*
+ * Look up an interface on a class using the cache.
+ *
+ * This function used to be defined in mterp/c/header.c, but it is now used by
+ * the JIT compiler as well so it is separated into its own header file to
+ * avoid potential out-of-sync changes in the future.
+ */
+INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+    u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+{
+#define ATOMIC_CACHE_CALC \
+    dvmInterpFindInterfaceMethod(thisClass, methodIdx, method, methodClassDex)
+#define ATOMIC_CACHE_NULL_ALLOWED false
+
+    return (Method*) ATOMIC_CACHE_LOOKUP(methodClassDex->pInterfaceCache,
+                DEX_INTERFACE_CACHE_SIZE, thisClass, methodIdx);
+
+#undef ATOMIC_CACHE_CALC
+}
+
+}
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
new file mode 100644
index 0000000..80b36fc
--- /dev/null
+++ b/vm/mterp/common/asm-constants.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2008 The Android Open Source Project
+ *
+ * Constants used by the assembler and verified by the C compiler.
+ */
+
+#if defined(ASM_DEF_VERIFY)
+  /*
+   * Generate C fragments that verify values; assumes "bool failed" exists.
+   * These are all constant expressions, so on success these will compile
+   * down to nothing.
+   */
+# define MTERP_OFFSET(_name, _type, _field, _offset)                        \
+    if (OFFSETOF_MEMBER(_type, _field) != _offset) {                        \
+        ALOGE("Bad asm offset %s (%d), should be %d",                        \
+            #_name, _offset, OFFSETOF_MEMBER(_type, _field));               \
+        failed = true;                                                      \
+    }
+# define MTERP_SIZEOF(_name, _type, _size)                                  \
+    if (sizeof(_type) != (_size)) {                                         \
+        ALOGE("Bad asm sizeof %s (%d), should be %d",                        \
+            #_name, (_size), sizeof(_type));                                \
+        failed = true;                                                      \
+    }
+# define MTERP_CONSTANT(_name, _value)                                      \
+    if ((_name) != (_value)) {                                              \
+        ALOGE("Bad asm constant %s (%d), should be %d",                      \
+            #_name, (_value), (_name));                                     \
+        failed = true;                                                      \
+    }
+#else
+  /* generate constant labels for the assembly output */
+# define MTERP_OFFSET(name, type, field, offset)    name = offset
+# define MTERP_SIZEOF(name, type, size)             name = size
+# define MTERP_CONSTANT(name, value)                name = value
+#endif
+
+/*
+ * Platform dependencies.  Some platforms require 64-bit alignment of 64-bit
+ * data structures.  Some versions of gcc will hold small enumerated types
+ * in a char instead of an int.
+ */
+#if defined(__ARM_EABI__) || defined(__mips__)
+# define MTERP_NO_UNALIGN_64
+#endif
+#if defined(HAVE_SHORT_ENUMS)
+# define MTERP_SMALL_ENUM   1
+#else
+# define MTERP_SMALL_ENUM   4
+#endif
+
+/*
+ * This file must only contain the following kinds of statements:
+ *
+ *  MTERP_OFFSET(name, StructType, fieldname, offset)
+ *
+ *   Declares that the expected offset of StructType.fieldname is "offset".
+ *   This will break whenever the contents of StructType are rearranged.
+ *
+ *  MTERP_SIZEOF(name, Type, size)
+ *
+ *   Declares that the expected size of Type is "size".
+ *
+ *  MTERP_CONSTANT(name, value)
+ *
+ *   Declares that the expected value of "name" is "value".  Useful for
+ *   enumerations and defined constants that are inaccessible to the
+ *   assembly source.  (Note this assumes you will use the same name in
+ *   both C and assembly, which is good practice.)
+ *
+ * In all cases the "name" field is the label you will use in the assembler.
+ *
+ * The "value" field must always be an actual number, not a symbol, unless
+ * you are sure that the symbol's value will be visible to both C and
+ * assembly sources.  There may be restrictions on the possible range of
+ * values (which are usually provided as immediate operands), so it's best
+ * to restrict numbers assuming a signed 8-bit field.
+ *
+ * On the assembly side, these just become "name=value" constants.  On the
+ * C side, these turn into assertions that cause the VM to abort if the
+ * values are incorrect.
+ */
+
+/* DvmDex fields */
+MTERP_OFFSET(offDvmDex_pResStrings,     DvmDex, pResStrings, 8)
+MTERP_OFFSET(offDvmDex_pResClasses,     DvmDex, pResClasses, 12)
+MTERP_OFFSET(offDvmDex_pResMethods,     DvmDex, pResMethods, 16)
+MTERP_OFFSET(offDvmDex_pResFields,      DvmDex, pResFields, 20)
+MTERP_OFFSET(offDvmDex_pInterfaceCache, DvmDex, pInterfaceCache, 24)
+
+/* StackSaveArea fields */
+#ifdef EASY_GDB
+MTERP_OFFSET(offStackSaveArea_prevSave, StackSaveArea, prevSave, 0)
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 4)
+MTERP_OFFSET(offStackSaveArea_savedPc,  StackSaveArea, savedPc, 8)
+MTERP_OFFSET(offStackSaveArea_method,   StackSaveArea, method, 12)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 16)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+                                        StackSaveArea, xtra.localRefCookie, 16)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 20)
+MTERP_SIZEOF(sizeofStackSaveArea,       StackSaveArea, 24)
+#else
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 0)
+MTERP_OFFSET(offStackSaveArea_savedPc,  StackSaveArea, savedPc, 4)
+MTERP_OFFSET(offStackSaveArea_method,   StackSaveArea, method, 8)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 12)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+                                        StackSaveArea, xtra.localRefCookie, 12)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 16)
+MTERP_SIZEOF(sizeofStackSaveArea,       StackSaveArea, 20)
+#endif
+
+  /* ShadowSpace fields */
+#if defined(WITH_JIT) && defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offShadowSpace_startPC,     ShadowSpace, startPC, 0)
+MTERP_OFFSET(offShadowSpace_fp,          ShadowSpace, fp, 4)
+MTERP_OFFSET(offShadowSpace_method,      ShadowSpace, method, 8)
+MTERP_OFFSET(offShadowSpace_methodClassDex, ShadowSpace, methodClassDex, 12)
+MTERP_OFFSET(offShadowSpace_retval,      ShadowSpace, retval, 16)
+MTERP_OFFSET(offShadowSpace_interpStackEnd, ShadowSpace, interpStackEnd, 24)
+MTERP_OFFSET(offShadowSpace_jitExitState,ShadowSpace, jitExitState, 28)
+MTERP_OFFSET(offShadowSpace_svState,     ShadowSpace, selfVerificationState, 32)
+MTERP_OFFSET(offShadowSpace_shadowFP,    ShadowSpace, shadowFP, 40)
+#endif
+
+/* InstField fields */
+MTERP_OFFSET(offInstField_byteOffset,   InstField, byteOffset, 16)
+
+/* Field fields */
+MTERP_OFFSET(offField_clazz,            Field, clazz, 0)
+
+/* StaticField fields */
+MTERP_OFFSET(offStaticField_value,      StaticField, value, 16)
+
+/* Method fields */
+MTERP_OFFSET(offMethod_clazz,           Method, clazz, 0)
+MTERP_OFFSET(offMethod_accessFlags,     Method, accessFlags, 4)
+MTERP_OFFSET(offMethod_methodIndex,     Method, methodIndex, 8)
+MTERP_OFFSET(offMethod_registersSize,   Method, registersSize, 10)
+MTERP_OFFSET(offMethod_outsSize,        Method, outsSize, 12)
+MTERP_OFFSET(offMethod_name,            Method, name, 16)
+MTERP_OFFSET(offMethod_insns,           Method, insns, 32)
+MTERP_OFFSET(offMethod_nativeFunc,      Method, nativeFunc, 40)
+
+/* InlineOperation fields -- code assumes "func" offset is zero, do not alter */
+MTERP_OFFSET(offInlineOperation_func,   InlineOperation, func, 0)
+
+/* Thread fields */
+MTERP_OFFSET(offThread_pc,                Thread, interpSave.pc, 0)
+MTERP_OFFSET(offThread_curFrame,          Thread, interpSave.curFrame, 4)
+MTERP_OFFSET(offThread_method,            Thread, interpSave.method, 8)
+MTERP_OFFSET(offThread_methodClassDex,    Thread, interpSave.methodClassDex, 12)
+/* make sure all JValue union members are stored at the same offset */
+MTERP_OFFSET(offThread_retval,            Thread, interpSave.retval, 16)
+#ifdef HAVE_BIG_ENDIAN
+MTERP_OFFSET(offThread_retval_z,          Thread, interpSave.retval.z, 19)
+#else
+MTERP_OFFSET(offThread_retval_z,          Thread, interpSave.retval.z, 16)
+#endif
+MTERP_OFFSET(offThread_retval_i,          Thread, interpSave.retval.i, 16)
+MTERP_OFFSET(offThread_retval_j,          Thread, interpSave.retval.j, 16)
+MTERP_OFFSET(offThread_retval_l,          Thread, interpSave.retval.l, 16)
+MTERP_OFFSET(offThread_bailPtr,           Thread, interpSave.bailPtr, 24)
+MTERP_OFFSET(offThread_threadId,          Thread, threadId, 36)
+
+//40
+MTERP_OFFSET(offThread_subMode, \
+                               Thread, interpBreak.ctl.subMode, 40)
+MTERP_OFFSET(offThread_breakFlags, \
+                               Thread, interpBreak.ctl.breakFlags, 42)
+MTERP_OFFSET(offThread_curHandlerTable, \
+                               Thread, interpBreak.ctl.curHandlerTable, 44)
+MTERP_OFFSET(offThread_suspendCount,      Thread, suspendCount, 48);
+MTERP_OFFSET(offThread_dbgSuspendCount,   Thread, dbgSuspendCount, 52);
+MTERP_OFFSET(offThread_cardTable,         Thread, cardTable, 56)
+MTERP_OFFSET(offThread_interpStackEnd,    Thread, interpStackEnd, 60)
+MTERP_OFFSET(offThread_exception,         Thread, exception, 68)
+MTERP_OFFSET(offThread_debugIsMethodEntry, Thread, debugIsMethodEntry, 72)
+MTERP_OFFSET(offThread_interpStackSize,   Thread, interpStackSize, 76)
+MTERP_OFFSET(offThread_stackOverflowed,   Thread, stackOverflowed, 80)
+MTERP_OFFSET(offThread_mainHandlerTable,  Thread, mainHandlerTable, 88)
+MTERP_OFFSET(offThread_singleStepCount,   Thread, singleStepCount, 96)
+
+#ifdef WITH_JIT
+MTERP_OFFSET(offThread_jitToInterpEntries,Thread, jitToInterpEntries, 100)
+MTERP_OFFSET(offThread_inJitCodeCache,    Thread, inJitCodeCache, 124)
+MTERP_OFFSET(offThread_pJitProfTable,     Thread, pJitProfTable, 128)
+MTERP_OFFSET(offThread_jitThreshold,      Thread, jitThreshold, 132)
+MTERP_OFFSET(offThread_jitResumeNPC,      Thread, jitResumeNPC, 136)
+MTERP_OFFSET(offThread_jitResumeNSP,      Thread, jitResumeNSP, 140)
+MTERP_OFFSET(offThread_jitResumeDPC,      Thread, jitResumeDPC, 144)
+MTERP_OFFSET(offThread_jitState,          Thread, jitState, 148)
+MTERP_OFFSET(offThread_icRechainCount,    Thread, icRechainCount, 152)
+MTERP_OFFSET(offThread_pProfileCountdown, Thread, pProfileCountdown, 156)
+MTERP_OFFSET(offThread_callsiteClass,     Thread, callsiteClass, 160)
+MTERP_OFFSET(offThread_methodToCall,      Thread, methodToCall, 164)
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 168)
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offThread_shadowSpace,       Thread, shadowSpace, 188)
+#endif
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 100)
+#endif
+
+/* Object fields */
+MTERP_OFFSET(offObject_clazz,           Object, clazz, 0)
+MTERP_OFFSET(offObject_lock,            Object, lock, 4)
+
+/* Lock shape */
+MTERP_CONSTANT(LW_LOCK_OWNER_SHIFT, 3)
+MTERP_CONSTANT(LW_HASH_STATE_SHIFT, 1)
+
+/* ArrayObject fields */
+MTERP_OFFSET(offArrayObject_length,     ArrayObject, length, 8)
+#ifdef MTERP_NO_UNALIGN_64
+MTERP_OFFSET(offArrayObject_contents,   ArrayObject, contents, 16)
+#else
+MTERP_OFFSET(offArrayObject_contents,   ArrayObject, contents, 12)
+#endif
+
+/* String fields */
+MTERP_CONSTANT(STRING_FIELDOFF_VALUE,     8)
+MTERP_CONSTANT(STRING_FIELDOFF_HASHCODE, 12)
+MTERP_CONSTANT(STRING_FIELDOFF_OFFSET,   16)
+MTERP_CONSTANT(STRING_FIELDOFF_COUNT,    20)
+
+#if defined(WITH_JIT)
+/*
+ * Reasons for the non-chaining interpreter entry points
+ * Enums defined in vm/Globals.h
+ */
+MTERP_CONSTANT(kInlineCacheMiss,        0)
+MTERP_CONSTANT(kCallsiteInterpreted,    1)
+MTERP_CONSTANT(kSwitchOverflow,         2)
+MTERP_CONSTANT(kHeavyweightMonitor,     3)
+
+/* Size of callee save area */
+MTERP_CONSTANT(JIT_CALLEE_SAVE_DOUBLE_COUNT,   8)
+#endif
+
+/* ClassObject fields */
+MTERP_OFFSET(offClassObject_descriptor, ClassObject, descriptor, 24)
+MTERP_OFFSET(offClassObject_accessFlags, ClassObject, accessFlags, 32)
+MTERP_OFFSET(offClassObject_pDvmDex,    ClassObject, pDvmDex, 40)
+MTERP_OFFSET(offClassObject_status,     ClassObject, status, 44)
+MTERP_OFFSET(offClassObject_super,      ClassObject, super, 72)
+MTERP_OFFSET(offClassObject_vtableCount, ClassObject, vtableCount, 112)
+MTERP_OFFSET(offClassObject_vtable,     ClassObject, vtable, 116)
+
+#if defined(WITH_JIT)
+MTERP_CONSTANT(kJitNot,                 0)
+MTERP_CONSTANT(kJitTSelectRequest,      1)
+MTERP_CONSTANT(kJitTSelectRequestHot,   2)
+MTERP_CONSTANT(kJitSelfVerification,    3)
+MTERP_CONSTANT(kJitTSelect,             4)
+MTERP_CONSTANT(kJitTSelectEnd,          5)
+MTERP_CONSTANT(kJitDone,                6)
+
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_CONSTANT(kSVSIdle, 0)
+MTERP_CONSTANT(kSVSStart, 1)
+MTERP_CONSTANT(kSVSPunt, 2)
+MTERP_CONSTANT(kSVSSingleStep, 3)
+MTERP_CONSTANT(kSVSNoProfile, 4)
+MTERP_CONSTANT(kSVSTraceSelect, 5)
+MTERP_CONSTANT(kSVSNormal, 6)
+MTERP_CONSTANT(kSVSNoChain, 7)
+MTERP_CONSTANT(kSVSBackwardBranch, 8)
+MTERP_CONSTANT(kSVSDebugInterp, 9)
+#endif
+#endif
+
+/* ClassStatus enumeration */
+MTERP_SIZEOF(sizeofClassStatus,         ClassStatus, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(CLASS_INITIALIZED,   7)
+
+/* MethodType enumeration */
+MTERP_SIZEOF(sizeofMethodType,          MethodType, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(METHOD_DIRECT,       1)
+MTERP_CONSTANT(METHOD_STATIC,       2)
+MTERP_CONSTANT(METHOD_VIRTUAL,      3)
+MTERP_CONSTANT(METHOD_INTERFACE,    4)
+
+/* ClassObject constants */
+MTERP_CONSTANT(ACC_PRIVATE,         0x0002)
+MTERP_CONSTANT(ACC_STATIC,          0x0008)
+MTERP_CONSTANT(ACC_NATIVE,          0x0100)
+MTERP_CONSTANT(ACC_INTERFACE,       0x0200)
+MTERP_CONSTANT(ACC_ABSTRACT,        0x0400)
+MTERP_CONSTANT(CLASS_ISFINALIZABLE, 1<<31)
+
+/* flags for dvmMalloc */
+MTERP_CONSTANT(ALLOC_DONT_TRACK,    0x01)
+
+/* for GC */
+MTERP_CONSTANT(GC_CARD_SHIFT, 7)
+
+/* opcode number */
+MTERP_CONSTANT(OP_MOVE_EXCEPTION,   0x0d)
+MTERP_CONSTANT(OP_INVOKE_DIRECT_RANGE, 0x76)
+
+/* flags for interpBreak */
+MTERP_CONSTANT(kSubModeNormal,          0x0000)
+MTERP_CONSTANT(kSubModeMethodTrace,     0x0001)
+MTERP_CONSTANT(kSubModeEmulatorTrace,   0x0002)
+MTERP_CONSTANT(kSubModeInstCounting,    0x0004)
+MTERP_CONSTANT(kSubModeDebuggerActive,  0x0008)
+MTERP_CONSTANT(kSubModeSuspendPending,  0x0010)
+MTERP_CONSTANT(kSubModeCallbackPending, 0x0020)
+MTERP_CONSTANT(kSubModeCountedStep,     0x0040)
+MTERP_CONSTANT(kSubModeJitTraceBuild,   0x4000)
+MTERP_CONSTANT(kSubModeJitSV,           0x8000)
+MTERP_CONSTANT(kSubModeDebugProfile,    0x000f)
+
+MTERP_CONSTANT(kInterpNoBreak,            0x00)
+MTERP_CONSTANT(kInterpSingleStep,         0x01)
+MTERP_CONSTANT(kInterpSafePoint,          0x02)
+
+MTERP_CONSTANT(DBG_METHOD_ENTRY,          0x04)
+MTERP_CONSTANT(DBG_METHOD_EXIT,           0x08)
+
+#if defined(__thumb__)
+# define PCREL_REF(sym,label) sym-(label+4)
+#else
+# define PCREL_REF(sym,label) sym-(label+8)
+#endif
diff --git a/vm/mterp/common/jit-config.h b/vm/mterp/common/jit-config.h
new file mode 100644
index 0000000..8cc32e3
--- /dev/null
+++ b/vm/mterp/common/jit-config.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#if __ARM_ARCH_5TE__
+#define JIT_PROF_SIZE_LOG_2     9
+#else
+#define JIT_PROF_SIZE_LOG_2     11
+#endif
+
+#define JIT_PROF_SIZE           (1 << JIT_PROF_SIZE_LOG_2)
diff --git a/vm/mterp/common/mips-defines.h b/vm/mterp/common/mips-defines.h
new file mode 100644
index 0000000..90852ad
--- /dev/null
+++ b/vm/mterp/common/mips-defines.h
@@ -0,0 +1,21 @@
+#include <machine/regdef.h>
+
+#define fp $30
+
+#define fcc0    $fcc0
+#define fcc1    $fcc1
+
+#define fv0 $f0
+#define fv0f $f1
+#define fv1 $f2
+#define fv1f $f3
+
+#define fa0 $f12
+#define fa0f $f13
+#define fa1 $f14
+#define fa1f $f15
+
+#define ft0 $f4
+#define ft0f $f5
+#define ft1 $f6
+#define ft1f $f7
diff --git a/vm/mterp/config-allstubs b/vm/mterp/config-allstubs
new file mode 100644
index 0000000..9df7b12
--- /dev/null
+++ b/vm/mterp/config-allstubs
@@ -0,0 +1,47 @@
+# Copyright (C) 2008 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.
+
+#
+# Configuration for "allstubs" target.  This is structured like the
+# assembly interpreters, but consists entirely of C stubs, making it
+# a handy if inefficient way to exercise all of the C handlers.  The
+# handler-style command should match the target assembly interpreter.
+#
+
+#handler-style jump-table
+handler-style computed-goto
+handler-size 64
+
+# C file header and basic definitions
+import c/header.cpp
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # use nothing but C stubs
+op-end
+
+# arch-specific entry point to interpreter
+import cstubs/entry.cpp
+
+# "helper" code
+import c/gotoTargets.cpp
+
+# finish
+import cstubs/enddefs.cpp
diff --git a/vm/mterp/config-armv5te b/vm/mterp/config-armv5te
new file mode 100644
index 0000000..5861f8b
--- /dev/null
+++ b/vm/mterp/config-armv5te
@@ -0,0 +1,58 @@
+# Copyright (C) 2008 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.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    #op OP_FILL_ARRAY_DATA c
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv5te-vfp b/vm/mterp/config-armv5te-vfp
new file mode 100644
index 0000000..160913c
--- /dev/null
+++ b/vm/mterp/config-armv5te-vfp
@@ -0,0 +1,108 @@
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv5TE targets with VFP support.
+#
+# This is just ARMv5TE with replacements for the handlers that can benefit
+# from floating-point instructions.  Essentially all float/double
+# operations except for "remainder" and conversions to/from 64-bit ints.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    op OP_ADD_DOUBLE arm-vfp
+    op OP_ADD_DOUBLE_2ADDR arm-vfp
+    op OP_ADD_FLOAT arm-vfp
+    op OP_ADD_FLOAT_2ADDR arm-vfp
+    op OP_CMPG_DOUBLE arm-vfp
+    op OP_CMPG_FLOAT arm-vfp
+    op OP_CMPL_DOUBLE arm-vfp
+    op OP_CMPL_FLOAT arm-vfp
+    op OP_DIV_DOUBLE arm-vfp
+    op OP_DIV_DOUBLE_2ADDR arm-vfp
+    op OP_DIV_FLOAT arm-vfp
+    op OP_DIV_FLOAT_2ADDR arm-vfp
+    op OP_DOUBLE_TO_FLOAT arm-vfp
+    op OP_DOUBLE_TO_INT arm-vfp
+    op OP_FLOAT_TO_DOUBLE arm-vfp
+    op OP_FLOAT_TO_INT arm-vfp
+    op OP_INT_TO_DOUBLE arm-vfp
+    op OP_INT_TO_FLOAT arm-vfp
+    op OP_MUL_DOUBLE arm-vfp
+    op OP_MUL_DOUBLE_2ADDR arm-vfp
+    op OP_MUL_FLOAT arm-vfp
+    op OP_MUL_FLOAT_2ADDR arm-vfp
+    op OP_SUB_DOUBLE arm-vfp
+    op OP_SUB_DOUBLE_2ADDR arm-vfp
+    op OP_SUB_FLOAT arm-vfp
+    op OP_SUB_FLOAT_2ADDR arm-vfp
+
+    # use trivial integer operation
+    #op OP_NEG_DOUBLE armv5te
+    #op OP_NEG_FLOAT armv5te
+
+    # use __aeabi_* functions
+    #op OP_DOUBLE_TO_LONG armv5te
+    #op OP_FLOAT_TO_LONG armv5te
+    #op OP_LONG_TO_DOUBLE armv5te
+    #op OP_LONG_TO_FLOAT armv5te
+
+    # no "remainder" op in vfp or libgcc.a; use libc function
+    #op OP_REM_DOUBLE armv5te
+    #op OP_REM_DOUBLE_2ADDR armv5te
+    #op OP_REM_FLOAT armv5te
+    #op OP_REM_FLOAT_2ADDR armv5te
+
+    # experiment, unrelated to vfp
+    #op OP_INT_TO_BYTE armv6
+    #op OP_INT_TO_CHAR armv6
+    #op OP_INT_TO_SHORT armv6
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv7-a b/vm/mterp/config-armv7-a
new file mode 100644
index 0000000..3f609ce
--- /dev/null
+++ b/vm/mterp/config-armv7-a
@@ -0,0 +1,176 @@
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    # handlers that take advantage of >= ARMv6T2 instructions
+    op OP_ADD_DOUBLE_2ADDR armv6t2
+    op OP_ADD_FLOAT_2ADDR armv6t2
+    op OP_ADD_INT_2ADDR armv6t2
+    op OP_ADD_INT_LIT16 armv6t2
+    op OP_ADD_LONG_2ADDR armv6t2
+    op OP_AND_INT_2ADDR armv6t2
+    op OP_AND_INT_LIT16 armv6t2
+    op OP_AND_LONG_2ADDR armv6t2
+    op OP_ARRAY_LENGTH armv6t2
+    op OP_CONST_4 armv6t2
+    op OP_DIV_DOUBLE_2ADDR armv6t2
+    op OP_DIV_FLOAT_2ADDR armv6t2
+    op OP_DIV_INT armv7-a
+    op OP_DIV_INT_2ADDR armv7-a
+    op OP_DIV_INT_LIT16 armv7-a
+    op OP_DIV_INT_LIT8 armv7-a
+    op OP_DIV_LONG_2ADDR armv6t2
+    op OP_DOUBLE_TO_FLOAT armv6t2
+    op OP_DOUBLE_TO_INT armv6t2
+    op OP_DOUBLE_TO_LONG armv6t2
+    op OP_FLOAT_TO_DOUBLE armv6t2
+    op OP_FLOAT_TO_INT armv6t2
+    op OP_FLOAT_TO_LONG armv6t2
+    op OP_IF_EQ armv6t2
+    op OP_IF_GE armv6t2
+    op OP_IF_GT armv6t2
+    op OP_IF_LE armv6t2
+    op OP_IF_LT armv6t2
+    op OP_IF_NE armv6t2
+    op OP_IGET armv6t2
+    op OP_IGET_QUICK armv6t2
+    op OP_IGET_WIDE armv6t2
+    op OP_IGET_WIDE_QUICK armv6t2
+    op OP_INT_TO_BYTE armv6t2
+    op OP_INT_TO_CHAR armv6t2
+    op OP_INT_TO_DOUBLE armv6t2
+    op OP_INT_TO_FLOAT armv6t2
+    op OP_INT_TO_LONG armv6t2
+    op OP_INT_TO_SHORT armv6t2
+    op OP_IPUT armv6t2
+    op OP_IPUT_QUICK armv6t2
+    op OP_IPUT_WIDE armv6t2
+    op OP_IPUT_WIDE_QUICK armv6t2
+    op OP_LONG_TO_DOUBLE armv6t2
+    op OP_LONG_TO_FLOAT armv6t2
+    op OP_MOVE armv6t2
+    op OP_MOVE_WIDE armv6t2
+    op OP_MUL_DOUBLE_2ADDR armv6t2
+    op OP_MUL_FLOAT_2ADDR armv6t2
+    op OP_MUL_INT_2ADDR armv6t2
+    op OP_MUL_INT_LIT16 armv6t2
+    op OP_MUL_LONG_2ADDR armv6t2
+    op OP_NEG_DOUBLE armv6t2
+    op OP_NEG_FLOAT armv6t2
+    op OP_NEG_INT armv6t2
+    op OP_NEG_LONG armv6t2
+    op OP_NOT_INT armv6t2
+    op OP_NOT_LONG armv6t2
+    op OP_OR_INT_2ADDR armv6t2
+    op OP_OR_INT_LIT16 armv6t2
+    op OP_OR_LONG_2ADDR armv6t2
+    op OP_REM_DOUBLE_2ADDR armv6t2
+    op OP_REM_FLOAT_2ADDR armv6t2
+    op OP_REM_INT armv7-a
+    op OP_REM_INT_2ADDR armv7-a
+    op OP_REM_INT_LIT16 armv7-a
+    op OP_REM_INT_LIT8 armv7-a
+    op OP_REM_LONG_2ADDR armv6t2
+    op OP_RSUB_INT armv6t2
+    op OP_SHL_INT_2ADDR armv6t2
+    op OP_SHL_LONG_2ADDR armv6t2
+    op OP_SHR_INT_2ADDR armv6t2
+    op OP_SHR_LONG_2ADDR armv6t2
+    op OP_SUB_DOUBLE_2ADDR armv6t2
+    op OP_SUB_FLOAT_2ADDR armv6t2
+    op OP_SUB_INT_2ADDR armv6t2
+    op OP_SUB_LONG_2ADDR armv6t2
+    op OP_USHR_INT_2ADDR armv6t2
+    op OP_USHR_LONG_2ADDR armv6t2
+    op OP_XOR_INT_2ADDR armv6t2
+    op OP_XOR_INT_LIT16 armv6t2
+    op OP_XOR_LONG_2ADDR armv6t2
+
+    # floating point handlers that use VFP
+    # these override the handlers specified earlier
+    op OP_ADD_DOUBLE arm-vfp
+    op OP_ADD_DOUBLE_2ADDR arm-vfp
+    op OP_ADD_FLOAT arm-vfp
+    op OP_ADD_FLOAT_2ADDR arm-vfp
+    op OP_CMPG_DOUBLE arm-vfp
+    op OP_CMPG_FLOAT arm-vfp
+    op OP_CMPL_DOUBLE arm-vfp
+    op OP_CMPL_FLOAT arm-vfp
+    op OP_DIV_DOUBLE arm-vfp
+    op OP_DIV_DOUBLE_2ADDR arm-vfp
+    op OP_DIV_FLOAT arm-vfp
+    op OP_DIV_FLOAT_2ADDR arm-vfp
+    op OP_DOUBLE_TO_FLOAT arm-vfp
+    op OP_DOUBLE_TO_INT arm-vfp
+    op OP_FLOAT_TO_DOUBLE arm-vfp
+    op OP_FLOAT_TO_INT arm-vfp
+    op OP_INT_TO_DOUBLE arm-vfp
+    op OP_INT_TO_FLOAT arm-vfp
+    op OP_MUL_DOUBLE arm-vfp
+    op OP_MUL_DOUBLE_2ADDR arm-vfp
+    op OP_MUL_FLOAT arm-vfp
+    op OP_MUL_FLOAT_2ADDR arm-vfp
+    op OP_SUB_DOUBLE arm-vfp
+    op OP_SUB_DOUBLE_2ADDR arm-vfp
+    op OP_SUB_FLOAT arm-vfp
+    op OP_SUB_FLOAT_2ADDR arm-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+#
+# Add this if you see linker failures for stuff like "dvmMterp_exceptionThrown".
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-armv7-a-neon b/vm/mterp/config-armv7-a-neon
new file mode 100644
index 0000000..e91661a
--- /dev/null
+++ b/vm/mterp/config-armv7-a-neon
@@ -0,0 +1,174 @@
+# Copyright (C) 2009 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.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-style computed-goto
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# source for alternate entry stub
+asm-alt-stub armv5te/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    # handlers that take advantage of >= ARMv6T2 instructions
+    op OP_ADD_DOUBLE_2ADDR armv6t2
+    op OP_ADD_FLOAT_2ADDR armv6t2
+    op OP_ADD_INT_2ADDR armv6t2
+    op OP_ADD_INT_LIT16 armv6t2
+    op OP_ADD_LONG_2ADDR armv6t2
+    op OP_AND_INT_2ADDR armv6t2
+    op OP_AND_INT_LIT16 armv6t2
+    op OP_AND_LONG_2ADDR armv6t2
+    op OP_ARRAY_LENGTH armv6t2
+    op OP_CONST_4 armv6t2
+    op OP_DIV_DOUBLE_2ADDR armv6t2
+    op OP_DIV_FLOAT_2ADDR armv6t2
+    op OP_DIV_INT armv7-a
+    op OP_DIV_INT_2ADDR armv7-a
+    op OP_DIV_INT_LIT16 armv7-a
+    op OP_DIV_INT_LIT8 armv7-a
+    op OP_DIV_LONG_2ADDR armv6t2
+    op OP_DOUBLE_TO_FLOAT armv6t2
+    op OP_DOUBLE_TO_INT armv6t2
+    op OP_DOUBLE_TO_LONG armv6t2
+    op OP_FLOAT_TO_DOUBLE armv6t2
+    op OP_FLOAT_TO_INT armv6t2
+    op OP_FLOAT_TO_LONG armv6t2
+    op OP_IF_EQ armv6t2
+    op OP_IF_GE armv6t2
+    op OP_IF_GT armv6t2
+    op OP_IF_LE armv6t2
+    op OP_IF_LT armv6t2
+    op OP_IF_NE armv6t2
+    op OP_IGET armv6t2
+    op OP_IGET_QUICK armv6t2
+    op OP_IGET_WIDE armv6t2
+    op OP_IGET_WIDE_QUICK armv6t2
+    op OP_INT_TO_BYTE armv6t2
+    op OP_INT_TO_CHAR armv6t2
+    op OP_INT_TO_DOUBLE armv6t2
+    op OP_INT_TO_FLOAT armv6t2
+    op OP_INT_TO_LONG armv6t2
+    op OP_INT_TO_SHORT armv6t2
+    op OP_IPUT armv6t2
+    op OP_IPUT_QUICK armv6t2
+    op OP_IPUT_WIDE armv6t2
+    op OP_IPUT_WIDE_QUICK armv6t2
+    op OP_LONG_TO_DOUBLE arm-vfp
+    op OP_LONG_TO_FLOAT armv6t2
+    op OP_MOVE armv6t2
+    op OP_MOVE_WIDE armv6t2
+    op OP_MUL_DOUBLE_2ADDR armv6t2
+    op OP_MUL_FLOAT_2ADDR armv6t2
+    op OP_MUL_INT_2ADDR armv6t2
+    op OP_MUL_INT_LIT16 armv6t2
+    op OP_MUL_LONG_2ADDR armv6t2
+    op OP_NEG_DOUBLE armv6t2
+    op OP_NEG_FLOAT armv6t2
+    op OP_NEG_INT armv6t2
+    op OP_NEG_LONG armv6t2
+    op OP_NOT_INT armv6t2
+    op OP_NOT_LONG armv6t2
+    op OP_OR_INT_2ADDR armv6t2
+    op OP_OR_INT_LIT16 armv6t2
+    op OP_OR_LONG_2ADDR armv6t2
+    op OP_REM_DOUBLE_2ADDR armv6t2
+    op OP_REM_FLOAT_2ADDR armv6t2
+    op OP_REM_INT armv7-a
+    op OP_REM_INT_2ADDR armv7-a
+    op OP_REM_INT_LIT16 armv7-a
+    op OP_REM_INT_LIT8 armv7-a
+    op OP_REM_LONG_2ADDR armv6t2
+    op OP_RSUB_INT armv6t2
+    op OP_SHL_INT_2ADDR armv6t2
+    op OP_SHL_LONG_2ADDR armv6t2
+    op OP_SHR_INT_2ADDR armv6t2
+    op OP_SHR_LONG_2ADDR armv6t2
+    op OP_SUB_DOUBLE_2ADDR armv6t2
+    op OP_SUB_FLOAT_2ADDR armv6t2
+    op OP_SUB_INT_2ADDR armv6t2
+    op OP_SUB_LONG_2ADDR armv6t2
+    op OP_USHR_INT_2ADDR armv6t2
+    op OP_USHR_LONG_2ADDR armv6t2
+    op OP_XOR_INT_2ADDR armv6t2
+    op OP_XOR_INT_LIT16 armv6t2
+    op OP_XOR_LONG_2ADDR armv6t2
+
+    # floating point handlers that use VFP
+    # these override the handlers specified earlier
+    op OP_ADD_DOUBLE arm-vfp
+    op OP_ADD_DOUBLE_2ADDR arm-vfp
+    op OP_ADD_FLOAT arm-vfp
+    op OP_ADD_FLOAT_2ADDR arm-vfp
+    op OP_CMPG_DOUBLE arm-vfp
+    op OP_CMPG_FLOAT arm-vfp
+    op OP_CMPL_DOUBLE arm-vfp
+    op OP_CMPL_FLOAT arm-vfp
+    op OP_DIV_DOUBLE arm-vfp
+    op OP_DIV_DOUBLE_2ADDR arm-vfp
+    op OP_DIV_FLOAT arm-vfp
+    op OP_DIV_FLOAT_2ADDR arm-vfp
+    op OP_DOUBLE_TO_FLOAT arm-vfp
+    op OP_DOUBLE_TO_INT arm-vfp
+    op OP_FLOAT_TO_DOUBLE arm-vfp
+    op OP_FLOAT_TO_INT arm-vfp
+    op OP_INT_TO_DOUBLE arm-vfp
+    op OP_INT_TO_FLOAT arm-vfp
+    op OP_MUL_DOUBLE arm-vfp
+    op OP_MUL_DOUBLE_2ADDR arm-vfp
+    op OP_MUL_FLOAT arm-vfp
+    op OP_MUL_FLOAT_2ADDR arm-vfp
+    op OP_SUB_DOUBLE arm-vfp
+    op OP_SUB_DOUBLE_2ADDR arm-vfp
+    op OP_SUB_FLOAT arm-vfp
+    op OP_SUB_FLOAT_2ADDR arm-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.cpp
diff --git a/vm/mterp/config-mips b/vm/mterp/config-mips
new file mode 100644
index 0000000..0dc80e8
--- /dev/null
+++ b/vm/mterp/config-mips
@@ -0,0 +1,65 @@
+# Copyright (C) 2008 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.
+
+#
+# Configuration for MIPS architecture targets.
+#
+
+handler-style computed-goto
+handler-size 128
+# Need to specify split-ops to generate alt-ops at the end after
+# importing other files.
+split-ops
+
+# source for the instruction table stub
+asm-stub mips/stub.S
+
+# source for alternate entry stub
+asm-alt-stub mips/alt_stub.S
+
+# file header and basic definitions
+import c/header.cpp
+import mips/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# highly-platform-specific defs
+import mips/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.cpp
+
+# arch-specific entry point to interpreter
+import mips/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start mips
+
+# OP_BREAKPOINT needs explicit testing
+    op OP_BREAKPOINT c
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import mips/footer.S
+import mips/debug.cpp
+alt-ops
diff --git a/vm/mterp/config-portable b/vm/mterp/config-portable
new file mode 100644
index 0000000..8f696b7
--- /dev/null
+++ b/vm/mterp/config-portable
@@ -0,0 +1,42 @@
+# Copyright (C) 2008 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.
+
+#
+# Configuration for the portable interpreter.
+#
+
+handler-style all-c
+
+# C file header and basic definitions
+import c/header.cpp
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# entry point
+import portable/entry.cpp
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.cpp
+
+# finish
+import portable/enddefs.cpp
diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86
new file mode 100644
index 0000000..9e5fa43
--- /dev/null
+++ b/vm/mterp/config-x86
@@ -0,0 +1,61 @@
+# Copyright (C) 2008 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.
+
+#
+# Configuration for "desktop" targets.
+#
+
+handler-style jump-table
+
+# source for the instruction table stub
+asm-stub x86/stub.S
+
+# source for alternate entry stub
+asm-alt-stub x86/alt_stub.S
+
+# C file header and basic definitions
+import c/header.cpp
+import x86/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.cpp
+
+# common defs for the C opcodes
+import c/opcommon.cpp
+
+# opcode list; argument to op-start is default directory
+op-start x86
+    # stub -- need native impl
+    op OP_EXECUTE_INLINE_RANGE c
+    op OP_IGET_WIDE_VOLATILE c
+    op OP_IPUT_WIDE_VOLATILE c
+    op OP_SGET_WIDE_VOLATILE c
+    op OP_SPUT_WIDE_VOLATILE c
+    op OP_RETURN_VOID_BARRIER c
+    op OP_INVOKE_OBJECT_INIT_RANGE c
+op-end
+
+# arch-specific entry point to interpreter
+import x86/entry.S
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+# (asm code is currently calling into dvmMterp_exceptionThrown)
+import c/gotoTargets.cpp
+
+# end of defs; include this when cstubs/stubdefs.cpp is included
+import cstubs/enddefs.cpp
+
+# common subroutines for asm
+import x86/footer.S
diff --git a/vm/mterp/cstubs/enddefs.cpp b/vm/mterp/cstubs/enddefs.cpp
new file mode 100644
index 0000000..cac74bf
--- /dev/null
+++ b/vm/mterp/cstubs/enddefs.cpp
@@ -0,0 +1,9 @@
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
diff --git a/vm/mterp/cstubs/entry.cpp b/vm/mterp/cstubs/entry.cpp
new file mode 100644
index 0000000..90b6cea
--- /dev/null
+++ b/vm/mterp/cstubs/entry.cpp
@@ -0,0 +1,61 @@
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) (const void*) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point.  This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+void dvmMterpStdRun(Thread* self)
+{
+    jmp_buf jmpBuf;
+
+    self->interpSave.bailPtr = &jmpBuf;
+
+    /* We exit via a longjmp */
+    if (setjmp(jmpBuf)) {
+        LOGVV("mterp threadid=%d returning", dvmThreadSelf()->threadId);
+        return;
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(Thread* self);
+
+        u2 inst = /*self->interpSave.*/pc[0];
+        /*
+         * In mterp, dvmCheckBefore is handled via the altHandlerTable,
+         * while in the portable interpreter it is part of the handler
+         * FINISH code.  For allstubs, we must do an explicit check
+         * in the interpretation loop.
+         */
+        if (self->interpBreak.ctl.subMode) {
+            dvmCheckBefore(pc, fp, self);
+        }
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        (void) gDvmMterpHandlerNames;   /* avoid gcc "defined but not used" */
+        LOGVV("handler %p %s",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(self);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(Thread* self)
+{
+    jmp_buf* pJmpBuf = (jmp_buf*) self->interpSave.bailPtr;
+    longjmp(*pJmpBuf, 1);
+}
diff --git a/vm/mterp/cstubs/stubdefs.cpp b/vm/mterp/cstubs/stubdefs.cpp
new file mode 100644
index 0000000..58b4559
--- /dev/null
+++ b/vm/mterp/cstubs/stubdefs.cpp
@@ -0,0 +1,136 @@
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
diff --git a/vm/mterp/gen-mterp.py b/vm/mterp/gen-mterp.py
new file mode 100755
index 0000000..f60d8c6
--- /dev/null
+++ b/vm/mterp/gen-mterp.py
@@ -0,0 +1,634 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 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.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik interpreter.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "../../libdex/DexOpcodes.h" # need opcode list
+kNumPackedOpcodes = 256 # TODO: Derive this from DexOpcodes.h.
+
+splitops = False
+verbose = False
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0             # 0=not started, 1=started, 2=ended
+in_alt_op_start = 0         # 0=not started, 1=started, 2=ended
+default_op_dir = None
+default_alt_stub = None
+opcode_locations = {}
+alt_opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L"         # use ".L" to hide labels from gdb
+alt_label_prefix = ".L_ALT" # use ".L" to hide labels from gdb
+style = None                # interpreter style
+generate_alt_table = False
+
+# Exception class.
+class DataParseError(SyntaxError):
+    "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+    return { "handler_size_bits":handler_size_bits,
+             "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set interpreter style.
+#
+def setHandlerStyle(tokens):
+    global style
+    if len(tokens) != 2:
+        raise DataParseError("handler-style requires one argument")
+    style = tokens[1]
+    if style != "computed-goto" and style != "jump-table" and style != "all-c":
+        raise DataParseError("handler-style (%s) invalid" % style)
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes).  Throws an exception if "bytes" is not 0 or
+# a power of two.
+#
+def setHandlerSize(tokens):
+    global handler_size_bits, handler_size_bytes
+    if style != "computed-goto":
+        print "Warning: handler-size valid only for computed-goto interpreters"
+    if len(tokens) != 2:
+        raise DataParseError("handler-size requires one argument")
+    if handler_size_bits != -1000:
+        raise DataParseError("handler-size may only be set once")
+
+    # compute log2(n), and make sure n is 0 or a power of 2
+    handler_size_bytes = bytes = int(tokens[1])
+    bits = -1
+    while bytes > 0:
+        bytes //= 2     # halve with truncating division
+        bits += 1
+
+    if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+        raise DataParseError("handler-size (%d) must be power of 2" \
+                % orig_bytes)
+    handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    source = tokens[1]
+    if source.endswith(".cpp"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), c_fp, None)
+    elif source.endswith(".S"):
+        appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+    else:
+        raise DataParseError("don't know how to import %s (expecting .cpp/.S)"
+                % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+    global asm_stub_text
+    if style == "all-c":
+        print "Warning: asm-stub ignored for all-c interpreter"
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    try:
+        stub_fp = open(tokens[1])
+        asm_stub_text = stub_fp.readlines()
+    except IOError, err:
+        stub_fp.close()
+        raise DataParseError("unable to load asm-stub: %s" % str(err))
+    stub_fp.close()
+
+#
+# Parse arch config file --
+# Record location of default alt stub
+#
+def setAsmAltStub(tokens):
+    global default_alt_stub, generate_alt_table
+    if style == "all-c":
+        print "Warning: asm-alt-stub ingored for all-c interpreter"
+    if len(tokens) != 2:
+        raise DataParseError("import requires one argument")
+    default_alt_stub = tokens[1]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+    global in_op_start
+    global default_op_dir
+    if len(tokens) != 2:
+        raise DataParseError("opStart takes a directory name argument")
+    if in_op_start != 0:
+        raise DataParseError("opStart can only be specified once")
+    default_op_dir = tokens[1]
+    in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single alt opcode's source file.
+#
+def altEntry(tokens):
+    global generate_alt_table
+    if len(tokens) != 3:
+        raise DataParseError("alt requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("alt statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    if alt_opcode_locations.has_key(tokens[1]):
+        print "Note: alt overrides earlier %s (%s -> %s)" \
+                % (tokens[1], alt_opcode_locations[tokens[1]], tokens[2])
+    alt_opcode_locations[tokens[1]] = tokens[2]
+    generate_alt_table = True
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+    #global opcode_locations
+    if len(tokens) != 3:
+        raise DataParseError("op requires exactly two arguments")
+    if in_op_start != 1:
+        raise DataParseError("op statements must be between opStart/opEnd")
+    try:
+        index = opcodes.index(tokens[1])
+    except ValueError:
+        raise DataParseError("unknown opcode %s" % tokens[1])
+    if opcode_locations.has_key(tokens[1]):
+        print "Note: op overrides earlier %s (%s -> %s)" \
+                % (tokens[1], opcode_locations[tokens[1]], tokens[2])
+    opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Emit jump table
+#
+def emitJmpTable(start_label, prefix):
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .text\n")
+    asm_fp.write("%s:\n" % start_label)
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        dict = getGlobalSubDict()
+        dict.update({ "opcode":op, "opnum":i })
+        asm_fp.write("    .long " + prefix + \
+                     "_%(opcode)s /* 0x%(opnum)02x */\n" % dict)
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+    global in_op_start
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if in_op_start != 1:
+        raise DataParseError("opEnd must follow opStart, and only appear once")
+    in_op_start = 2
+
+    loadAndEmitOpcodes()
+    if splitops == False:
+        if generate_alt_table:
+            loadAndEmitAltOpcodes()
+            if style == "jump-table":
+                emitJmpTable("dvmAsmInstructionStart", label_prefix);
+                emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
+
+def genaltop(tokens):
+    if in_op_start != 2:
+       raise DataParseError("alt-op can be specified only after op-end")
+    if len(tokens) != 1:
+        raise DataParseError("opEnd takes no arguments")
+    if generate_alt_table:
+        loadAndEmitAltOpcodes()
+        if style == "jump-table":
+            emitJmpTable("dvmAsmInstructionStart", label_prefix);
+            emitJmpTable("dvmAsmAltInstructionStart", alt_label_prefix);
+
+
+#
+# Extract an ordered list of instructions from the VM sources.  We use the
+# "goto table" definition macro, which has exactly kNumPackedOpcodes
+# entries.
+#
+def getOpcodeList():
+    opcodes = []
+    opcode_fp = open(interp_defs_file)
+    opcode_re = re.compile(r"^\s*H\(OP_(\w+)\),.*", re.DOTALL)
+    for line in opcode_fp:
+        match = opcode_re.match(line)
+        if not match:
+            continue
+        opcodes.append("OP_" + match.group(1))
+    opcode_fp.close()
+
+    if len(opcodes) != kNumPackedOpcodes:
+        print "ERROR: found %d opcodes in Interp.h (expected %d)" \
+                % (len(opcodes), kNumPackedOpcodes)
+        raise SyntaxError, "bad opcode count"
+    return opcodes
+
+def emitAlign():
+    if style == "computed-goto":
+        asm_fp.write("    .balign %d\n" % handler_size_bytes)
+
+#
+# Load and emit opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitOpcodes():
+    sister_list = []
+    assert len(opcodes) == kNumPackedOpcodes
+    need_dummy_start = False
+    if style == "jump-table":
+        start_label = "dvmAsmInstructionStartCode"
+        end_label = "dvmAsmInstructionEndCode"
+    else:
+        start_label = "dvmAsmInstructionStart"
+        end_label = "dvmAsmInstructionEnd"
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("%s = " % start_label + label_prefix + "_OP_NOP\n")
+    asm_fp.write("    .text\n\n")
+
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+
+        if opcode_locations.has_key(op):
+            location = opcode_locations[op]
+        else:
+            location = default_op_dir
+
+        if location == "c":
+            loadAndEmitC(location, i)
+            if len(asm_stub_text) == 0:
+                need_dummy_start = True
+        else:
+            loadAndEmitAsm(location, i, sister_list)
+
+    # For a 100% C implementation, there are no asm handlers or stubs.  We
+    # need to have the dvmAsmInstructionStart label point at OP_NOP, and it's
+    # too annoying to try to slide it in after the alignment psuedo-op, so
+    # we take the low road and just emit a dummy OP_NOP here.
+    if need_dummy_start:
+        emitAlign()
+        asm_fp.write(label_prefix + "_OP_NOP:   /* dummy */\n");
+
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
+
+    if style == "computed-goto":
+        emitSectionComment("Sister implementations", asm_fp)
+        asm_fp.write("    .global dvmAsmSisterStart\n")
+        asm_fp.write("    .type   dvmAsmSisterStart, %function\n")
+        asm_fp.write("    .text\n")
+        asm_fp.write("    .balign 4\n")
+        asm_fp.write("dvmAsmSisterStart:\n")
+        asm_fp.writelines(sister_list)
+        asm_fp.write("\n    .size   dvmAsmSisterStart, .-dvmAsmSisterStart\n")
+        asm_fp.write("    .global dvmAsmSisterEnd\n")
+        asm_fp.write("dvmAsmSisterEnd:\n\n")
+
+#
+# Load an alternate entry stub
+#
+def loadAndEmitAltStub(source, opindex):
+    op = opcodes[opindex]
+    if verbose:
+        print " alt emit %s --> stub" % source
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+
+    emitAsmHeader(asm_fp, dict, alt_label_prefix)
+    appendSourceFile(source, dict, asm_fp, None)
+
+#
+# Load and emit alternate opcodes for all kNumPackedOpcodes instructions.
+#
+def loadAndEmitAltOpcodes():
+    assert len(opcodes) == kNumPackedOpcodes
+    if style == "jump-table":
+        start_label = "dvmAsmAltInstructionStartCode"
+        end_label = "dvmAsmAltInstructionEndCode"
+    else:
+        start_label = "dvmAsmAltInstructionStart"
+        end_label = "dvmAsmAltInstructionEnd"
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global %s\n" % start_label)
+    asm_fp.write("    .type   %s, %%function\n" % start_label)
+    asm_fp.write("    .text\n\n")
+    asm_fp.write("%s = " % start_label + label_prefix + "_ALT_OP_NOP\n")
+
+    for i in xrange(kNumPackedOpcodes):
+        op = opcodes[i]
+        if alt_opcode_locations.has_key(op):
+            source = "%s/ALT_%s.S" % (alt_opcode_locations[op], op)
+        else:
+            source = default_alt_stub
+        loadAndEmitAltStub(source, i)
+
+    emitAlign()
+    asm_fp.write("    .size   %s, .-%s\n" % (start_label, start_label))
+    asm_fp.write("    .global %s\n" % end_label)
+    asm_fp.write("%s:\n" % end_label)
+
+#
+# Load a C fragment and emit it, then output an assembly stub.
+#
+def loadAndEmitC(location, opindex):
+    op = opcodes[opindex]
+    source = "%s/%s.cpp" % (location, op)
+    if verbose:
+        print " emit %s --> C++" % source
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+
+    appendSourceFile(source, dict, c_fp, None)
+
+    if len(asm_stub_text) != 0:
+        emitAsmStub(asm_fp, dict)
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+    op = opcodes[opindex]
+    source = "%s/%s.S" % (location, op)
+    dict = getGlobalSubDict()
+    dict.update({ "opcode":op, "opnum":opindex })
+    if verbose:
+        print " emit %s --> asm" % source
+
+    emitAsmHeader(asm_fp, dict, label_prefix)
+    appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict, prefix):
+    outfp.write("/* ------------------------------ */\n")
+    # The alignment directive ensures that the handler occupies
+    # at least the correct amount of space.  We don't try to deal
+    # with overflow here.
+    emitAlign()
+    # Emit a label so that gdb will say the right thing.  We prepend an
+    # underscore so the symbol name doesn't clash with the Opcode enum.
+    outfp.write(prefix + "_%(opcode)s: /* 0x%(opnum)02x */\n" % dict)
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+    emitAsmHeader(outfp, dict, label_prefix)
+    for line in asm_stub_text:
+        templ = Template(line)
+        outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp".  Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings.  (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+    outfp.write("/* File: %s */\n" % source)
+    infp = open(source, "r")
+    in_sister = False
+    for line in infp:
+        if line.startswith("%include"):
+            # Parse the "include" line
+            tokens = line.strip().split(' ', 2)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%include in %s" % source)
+
+            alt_source = tokens[1].strip("\"")
+            if alt_source == source:
+                raise DataParseError("self-referential %%include in %s"
+                        % source)
+
+            new_dict = dict.copy()
+            if len(tokens) == 3:
+                new_dict.update(eval(tokens[2]))
+            #print " including src=%s dict=%s" % (alt_source, new_dict)
+            appendSourceFile(alt_source, new_dict, outfp, sister_list)
+            continue
+
+        elif line.startswith("%default"):
+            # copy keywords into dictionary
+            tokens = line.strip().split(' ', 1)
+            if len(tokens) < 2:
+                raise DataParseError("malformed %%default in %s" % source)
+            defaultValues = eval(tokens[1])
+            for entry in defaultValues:
+                dict.setdefault(entry, defaultValues[entry])
+            continue
+
+        elif line.startswith("%verify"):
+            # more to come, someday
+            continue
+
+        elif line.startswith("%break") and sister_list != None:
+            # allow more than one %break, ignoring all following the first
+            if style == "computed-goto" and not in_sister:
+                in_sister = True
+                sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+            continue
+
+        # perform keyword substitution if a dictionary was provided
+        if dict != None:
+            templ = Template(line)
+            try:
+                subline = templ.substitute(dict)
+            except KeyError, err:
+                raise DataParseError("keyword substitution failed in %s: %s"
+                        % (source, str(err)))
+            except:
+                print "ERROR: substitution failed: " + line
+                raise
+        else:
+            subline = line
+
+        # write output to appropriate file
+        if in_sister:
+            sister_list.append(subline)
+        else:
+            outfp.write(subline)
+    outfp.write("\n")
+    infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+    equals = "========================================" \
+             "==================================="
+
+    fp.write("\n/*\n * %s\n *  %s\n * %s\n */\n" %
+        (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+    print "Usage: %s target-arch output-dir" % sys.argv[0]
+    sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+#    print "  %s" % op
+
+#
+# Open config file.
+#
+try:
+    config_fp = open("config-%s" % target_arch)
+except:
+    print "Unable to open config file 'config-%s'" % target_arch
+    sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+    c_fp = open("%s/InterpC-%s.cpp" % (output_dir, target_arch), "w")
+    asm_fp = open("%s/InterpAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+    print "Unable to open output files"
+    print "Make sure directory '%s' exists and existing files are writable" \
+            % output_dir
+    # Ideally we'd remove the files to avoid confusing "make", but if they
+    # failed to open we probably won't be able to remove them either.
+    sys.exit(1)
+
+print "Generating %s, %s" % (c_fp.name, asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-mterp.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+c_fp.write(file_header)
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+    for line in config_fp:
+        line = line.strip()         # remove CRLF, leading spaces
+        tokens = line.split(' ')    # tokenize
+        #print "%d: %s" % (len(tokens), tokens)
+        if len(tokens[0]) == 0:
+            #print "  blank"
+            pass
+        elif tokens[0][0] == '#':
+            #print "  comment"
+            pass
+        else:
+            if tokens[0] == "handler-size":
+                setHandlerSize(tokens)
+            elif tokens[0] == "import":
+                importFile(tokens)
+            elif tokens[0] == "asm-stub":
+                setAsmStub(tokens)
+            elif tokens[0] == "asm-alt-stub":
+                setAsmAltStub(tokens)
+            elif tokens[0] == "op-start":
+                opStart(tokens)
+            elif tokens[0] == "op-end":
+                opEnd(tokens)
+            elif tokens[0] == "alt":
+                altEntry(tokens)
+            elif tokens[0] == "op":
+                opEntry(tokens)
+            elif tokens[0] == "handler-style":
+                setHandlerStyle(tokens)
+            elif tokens[0] == "alt-ops":
+                genaltop(tokens)
+            elif tokens[0] == "split-ops":
+                splitops = True
+            else:
+                raise DataParseError, "unrecognized command '%s'" % tokens[0]
+            if style == None:
+                print "tokens[0] = %s" % tokens[0]
+                raise DataParseError, "handler-style must be first command"
+except DataParseError, err:
+    print "Failed: " + str(err)
+    # TODO: remove output files so "make" doesn't get confused
+    failed = True
+    c_fp.close()
+    asm_fp.close()
+    c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if c_fp:
+    c_fp.close()
+if asm_fp:
+    asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/mterp/mips/OP_ADD_DOUBLE.S b/vm/mterp/mips/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..1d5cebc
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__adddf3)", "instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..499961f
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__adddf3)", "instr_f":"add.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_FLOAT.S b/vm/mterp/mips/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..18c94f4
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..0a39770
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__addsf3)", "instr_f":"add.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_ADD_INT.S b/vm/mterp/mips/OP_ADD_INT.S
new file mode 100644
index 0000000..dcbbb7e
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_2ADDR.S b/vm/mterp/mips/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..8bb3b0c
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_LIT16.S b/vm/mterp/mips/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..de45f81
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_INT_LIT8.S b/vm/mterp/mips/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..feaaaa2
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"addu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_ADD_LONG.S b/vm/mterp/mips/OP_ADD_LONG.S
new file mode 100644
index 0000000..d57e1cf
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_LONG.S
@@ -0,0 +1,10 @@
+%verify "executed"
+/*
+ *  The compiler generates the following sequence for
+ *  [v1 v0] =  [a1 a0] + [a3 a2];
+ *    addu v0,a2,a0
+ *    addu a1,a3,a1
+ *    sltu v1,v0,a2
+ *    addu v1,v1,a1
+ */
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" }
diff --git a/vm/mterp/mips/OP_ADD_LONG_2ADDR.S b/vm/mterp/mips/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..6a87119
--- /dev/null
+++ b/vm/mterp/mips/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,5 @@
+%verify "executed"
+/*
+ *See OP_ADD_LONG.S for details
+ */
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" }
diff --git a/vm/mterp/mips/OP_AGET.S b/vm/mterp/mips/OP_AGET.S
new file mode 100644
index 0000000..e1b182a
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET.S
@@ -0,0 +1,31 @@
+%default { "load":"lw", "shift":"2" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if $shift
+    EASN(a0, a0, a1, $shift)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $load a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
diff --git a/vm/mterp/mips/OP_AGET_BOOLEAN.S b/vm/mterp/mips/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..d38c466
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lbu", "shift":"0" }
diff --git a/vm/mterp/mips/OP_AGET_BYTE.S b/vm/mterp/mips/OP_AGET_BYTE.S
new file mode 100644
index 0000000..2c0b0be
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_AGET_CHAR.S b/vm/mterp/mips/OP_AGET_CHAR.S
new file mode 100644
index 0000000..9146b97
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lhu", "shift":"1" }
diff --git a/vm/mterp/mips/OP_AGET_OBJECT.S b/vm/mterp/mips/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..16d500d
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S"
diff --git a/vm/mterp/mips/OP_AGET_SHORT.S b/vm/mterp/mips/OP_AGET_SHORT.S
new file mode 100644
index 0000000..ba4c939
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_AGET.S" { "load":"lh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_AGET_WIDE.S b/vm/mterp/mips/OP_AGET_WIDE.S
new file mode 100644
index 0000000..896ea4f
--- /dev/null
+++ b/vm/mterp/mips/OP_AGET_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64_off(a2, a3, a0, offArrayObject_contents)
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a2, a3, rOBJ)                  #  vAA/vAA+1 <- a2/a3
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_AND_INT.S b/vm/mterp/mips/OP_AND_INT.S
new file mode 100644
index 0000000..721129b
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_2ADDR.S b/vm/mterp/mips/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..4563705
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_LIT16.S b/vm/mterp/mips/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..81c0a04
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_INT_LIT8.S b/vm/mterp/mips/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..61c1c9d
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"and a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_AND_LONG.S b/vm/mterp/mips/OP_AND_LONG.S
new file mode 100644
index 0000000..8249617
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_AND_LONG_2ADDR.S b/vm/mterp/mips/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..f9bf88f
--- /dev/null
+++ b/vm/mterp/mips/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_APUT.S b/vm/mterp/mips/OP_APUT.S
new file mode 100644
index 0000000..7839b69
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT.S
@@ -0,0 +1,27 @@
+%default { "store":"sw", "shift":"2" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if $shift
+    EASN(a0, a0, a1, $shift)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $store a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_APUT_BOOLEAN.S b/vm/mterp/mips/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..eeb9747
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_APUT_BYTE.S b/vm/mterp/mips/OP_APUT_BYTE.S
new file mode 100644
index 0000000..eeb9747
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sb", "shift":"0" }
diff --git a/vm/mterp/mips/OP_APUT_CHAR.S b/vm/mterp/mips/OP_APUT_CHAR.S
new file mode 100644
index 0000000..4c57fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_APUT_OBJECT.S b/vm/mterp/mips/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..03b6d38
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_OBJECT.S
@@ -0,0 +1,50 @@
+%verify "executed"
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t1)                            #  t1 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(rINST, a2)                    #  rINST <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    GET_VREG(rBIX, t1)                     #  rBIX <- vAA
+    # null array object?
+    beqz      rINST, common_errNullObject  #  yes, bail
+
+    LOAD_base_offArrayObject_length(a3, rINST) #  a3 <- arrayObj->length
+    EAS2(rOBJ, rINST, a1)                  #  rOBJ <- arrayObj + index*width
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  rBIX = vAA (obj)
+     *  rOBJ = offset into array (vBB + vCC * width)
+     */
+    bnez      rBIX, .L${opcode}_checks     #  yes, skip type checks
+.L${opcode}_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    sw        rBIX, offArrayObject_contents(rOBJ) #  vBB[vCC] <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+%break
+.L${opcode}_checks:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    JAL(dvmCanPutArrayElement)             #  test object type vs. array type
+    beqz      v0, .L${opcode}_throw        #  okay ?
+    lw        a2, offThread_cardTable(rSELF)
+    srl       t1, rINST, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)
+    b         .L${opcode}_finish           #  yes, skip type checks
+.L${opcode}_throw:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    EXPORT_PC()
+    JAL(dvmThrowArrayStoreExceptionIncompatibleElement)
+    b         common_exceptionThrown
diff --git a/vm/mterp/mips/OP_APUT_SHORT.S b/vm/mterp/mips/OP_APUT_SHORT.S
new file mode 100644
index 0000000..4c57fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_APUT.S" { "store":"sh", "shift":"1" }
diff --git a/vm/mterp/mips/OP_APUT_WIDE.S b/vm/mterp/mips/OP_APUT_WIDE.S
new file mode 100644
index 0000000..0046cd5
--- /dev/null
+++ b/vm/mterp/mips/OP_APUT_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t0 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a2, a3, rOBJ)                   #  a2/a3 <- vAA/vAA+1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64_off(a2, a3, a0, offArrayObject_contents) #  a2/a3 <- vBB[vCC]
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_ARRAY_LENGTH.S b/vm/mterp/mips/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..9416011
--- /dev/null
+++ b/vm/mterp/mips/OP_ARRAY_LENGTH.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+    GET_OPB(a1)                            #  a1 <- B
+    GET_OPA4(a2)                           #  a2 <- A+
+    GET_VREG(a0, a1)                       #  a0 <- vB (object ref)
+    # is object null?
+    beqz      a0, common_errNullObject     #  yup, fail
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- array length
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a3, a2, t0)              #  vA <- length
+
diff --git a/vm/mterp/mips/OP_BREAKPOINT.S b/vm/mterp/mips/OP_BREAKPOINT.S
new file mode 100644
index 0000000..3624810
--- /dev/null
+++ b/vm/mterp/mips/OP_BREAKPOINT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    move    a0, rPC
+    JAL(dvmGetOriginalOpcode)           # (rPC)
+    FETCH(rINST, 0)                     # reload OP_BREAKPOINT + rest of inst
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    and     rINST, 0xff00
+    or      rINST, rINST, a0
+    GOTO_OPCODE_BASE(a1, a0)
diff --git a/vm/mterp/mips/OP_CHECK_CAST.S b/vm/mterp/mips/OP_CHECK_CAST.S
new file mode 100644
index 0000000..f29a51f
--- /dev/null
+++ b/vm/mterp/mips/OP_CHECK_CAST.S
@@ -0,0 +1,71 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    # check-cast vAA, class                /* BBBB */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- object
+    LOAD_rSELF_methodClassDex(a0)          #  a0 <- pDvmDex
+    LOAD_base_offDvmDex_pResClasses(a0, a0) #  a0 <- pDvmDex->pResClasses
+    # is object null?
+    beqz      rOBJ, .L${opcode}_okay       #  null obj, cast always succeeds
+    LOAD_eas2(a1, a0, a2)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .L${opcode}_resolve      #  not resolved, do it now
+.L${opcode}_resolved:
+    # same class (trivial success)?
+    bne       a0, a1, .L${opcode}_fullcheck #  no, do full check
+.L${opcode}_okay:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0 holds obj->clazz
+     *  a1 holds class resolved from BBBB
+     *  rOBJ holds object
+     */
+.L${opcode}_fullcheck:
+    move      rBIX,a1                      #  avoid ClassObject getting clobbered
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    # failed?
+    bnez      v0, .L${opcode}_okay         #  no, success
+    b         .L${opcode}_castfailure
+%break
+
+.L${opcode}_castfailure:
+    # A cast has failed. We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()                            #  about to throw
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    move      a1,rBIX                      #  r1<- desired class
+    JAL(dvmThrowClassCastException)
+    b         common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a2   holds BBBB
+     *  rOBJ holds object
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      a1, a2                       #  a1 <- BBBB
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    b         .L${opcode}_resolved         #  pick up where we left off
diff --git a/vm/mterp/mips/OP_CMPG_DOUBLE.S b/vm/mterp/mips/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..8e740e3
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_CMPL_DOUBLE.S" { "naninst":"li rTEMP, 1" }
diff --git a/vm/mterp/mips/OP_CMPG_FLOAT.S b/vm/mterp/mips/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..2c4e97b
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_CMPL_FLOAT.S" { "naninst":"li rTEMP, 1" }
diff --git a/vm/mterp/mips/OP_CMPL_DOUBLE.S b/vm/mterp/mips/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..2c824b3
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPL_DOUBLE.S
@@ -0,0 +1,70 @@
+%default { "naninst":"li rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, ${opcode}_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, ${opcode}_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         ${opcode}_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, ${opcode}_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, ${opcode}_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, ${opcode}_finish
+    b         ${opcode}_nan
+#endif
+%break
+
+${opcode}_nan:
+    $naninst
+    b         ${opcode}_finish
+
+#ifdef SOFT_FLOAT
+${opcode}_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, ${opcode}_nan            #  fall thru for finish
+#endif
+
+${opcode}_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
diff --git a/vm/mterp/mips/OP_CMPL_FLOAT.S b/vm/mterp/mips/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..01db920
--- /dev/null
+++ b/vm/mterp/mips/OP_CMPL_FLOAT.S
@@ -0,0 +1,82 @@
+%default { "naninst":"li rTEMP, -1" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, ${opcode}_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, ${opcode}_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         ${opcode}_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, ${opcode}_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, ${opcode}_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, ${opcode}_finish
+    b         ${opcode}_nan
+
+#endif
+
+%break
+
+${opcode}_nan:
+    $naninst
+    b         ${opcode}_finish
+
+#ifdef SOFT_FLOAT
+${opcode}_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, ${opcode}_finish
+    b         ${opcode}_nan
+#endif
+
+${opcode}_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
diff --git a/vm/mterp/mips/OP_CMP_LONG.S b/vm/mterp/mips/OP_CMP_LONG.S
new file mode 100644
index 0000000..fcdfce7
--- /dev/null
+++ b/vm/mterp/mips/OP_CMP_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;	# (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;	# (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, a3)                     #  a2/a3 <- vCC/vCC+1
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    slt       t0, a1, a3                   #  compare hi
+    sgt       t1, a1, a3
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0)
+    bnez      v0, .L${opcode}_finish
+    # at this point x.hi==y.hi
+    sltu      t0, a0, a2                   #  compare lo
+    sgtu      t1, a0, a2
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0) for [< > =]
+
+.L${opcode}_finish:
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST.S b/vm/mterp/mips/OP_CONST.S
new file mode 100644
index 0000000..309b52a
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    # const vAA,                           /* +BBBBbbbb */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a1, a1, 16
+    or        a0, a1, a0                   #  a0 <- BBBBbbbb
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_16.S b/vm/mterp/mips/OP_CONST_16.S
new file mode 100644
index 0000000..69732f4
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    # const/16 vAA,                        /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_4.S b/vm/mterp/mips/OP_CONST_4.S
new file mode 100644
index 0000000..833e373
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    # const/4 vA,                          /* +B */
+    sll       a1, rINST, 16                #  a1 <- Bxxx0000
+    GET_OPA(a0)                            #  a0 <- A+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sra       a1, a1, 28                   #  a1 <- sssssssB (sign-extended)
+    and       a0, a0, 15
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    SET_VREG_GOTO(a1, a0, t0)              #  fp[A] <- a1
+
diff --git a/vm/mterp/mips/OP_CONST_CLASS.S b/vm/mterp/mips/OP_CONST_CLASS.S
new file mode 100644
index 0000000..f63d7c3
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_CLASS.S
@@ -0,0 +1,31 @@
+%verify "executed"
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    # const/class vAA, Class               /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- dvmDex->pResClasses
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResClasses[BBBB]
+
+    bnez      v0, .L${opcode}_resolve      #  v0!=0 => resolved-ok
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  a1: BBBB (Class ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- Class reference
+    # failed==0?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
diff --git a/vm/mterp/mips/OP_CONST_HIGH16.S b/vm/mterp/mips/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..04c6d5d
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    # const/high16 vAA,                    /* +BBBB0000 */
+    FETCH(a0, 1)                           #  a0 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a0, a0, 16                   #  a0 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
diff --git a/vm/mterp/mips/OP_CONST_STRING.S b/vm/mterp/mips/OP_CONST_STRING.S
new file mode 100644
index 0000000..f59b1d6
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_STRING.S
@@ -0,0 +1,33 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    # const/string vAA, String             /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    # not yet resolved?
+    bnez      v0, .L${opcode}_resolve
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1:   BBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+
+
diff --git a/vm/mterp/mips/OP_CONST_STRING_JUMBO.S b/vm/mterp/mips/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..0c3d0bd
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    # const/string vAA, String             /* BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    sll       a1, a1, 16
+    or        a1, a1, a0                   #  a1 <- BBBBbbbb
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    bnez      v0, .L${opcode}_resolve
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1: BBBBBBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.L${opcode}_resolve:
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t1)            #  vAA <- v0
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE.S b/vm/mterp/mips/OP_CONST_WIDE.S
new file mode 100644
index 0000000..ba1c462
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    # const-wide vAA,                      /* +HHHHhhhhBBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (low middle)
+    FETCH(a2, 3)                           #  a2 <- hhhh (high middle)
+    sll       a1, 16 #
+    or        a0, a1                       #  a0 <- BBBBbbbb (low word)
+    FETCH(a3, 4)                           #  a3 <- HHHH (high)
+    GET_OPA(t1)                            #  t1 <- AA
+    sll       a3, 16
+    or        a1, a3, a2                   #  a1 <- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)                  #  advance rPC, load rINST
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, t1)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_16.S b/vm/mterp/mips/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..d431529
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    # const-wide/16 vAA,                   /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_32.S b/vm/mterp/mips/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..9cb9a3f
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_32.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    # const-wide/32 vAA,                   /* +BBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- 0000bbbb (low)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_S(a2, 2)                         #  a2 <- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a2, a2, 16
+    or        a0, a0, a2                   #  a0 <- BBBBbbbb
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S b/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..c56cd26
--- /dev/null
+++ b/vm/mterp/mips/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    # const-wide/high16 vAA,               /* +BBBB000000000000 */
+    FETCH(a1, 1)                           #  a1 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    li        a0, 0                        #  a0 <- 00000000
+    sll       a1, 16                       #  a1 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_DIV_DOUBLE.S b/vm/mterp/mips/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..a7e0302
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__divdf3)", "instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..18e28d7
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__divdf3)", "instr_f":"div.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_FLOAT.S b/vm/mterp/mips/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..59bb8d6
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..a0a546f
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__divsf3)", "instr_f":"div.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_DIV_INT.S b/vm/mterp/mips/OP_DIV_INT.S
new file mode 100644
index 0000000..b845475
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_2ADDR.S b/vm/mterp/mips/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..1f13ad8
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_LIT16.S b/vm/mterp/mips/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..d75d210
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_INT_LIT8.S b/vm/mterp/mips/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..384eb0d
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"div zero, a0, a1; mflo a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_DIV_LONG.S b/vm/mterp/mips/OP_DIV_LONG.S
new file mode 100644
index 0000000..bb39d2a
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_LONG.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide.S" { "arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_DIV_LONG_2ADDR.S b/vm/mterp/mips/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..8e751b6
--- /dev/null
+++ b/vm/mterp/mips/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide2addr.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide2addr.S" {"arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__divdi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..f1e04ea
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"JAL(__truncdfsf2)", "instr_f":"cvt.s.d fv0, fa0"}
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_INT.S b/vm/mterp/mips/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..33199c4
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,80 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"b d2i_doconv", "instr_f":"b d2i_doconv"}
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rTEMP as global to hold arguments (they are not bound to a global var)
+ */
+%break
+
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)                           #  is arg >= maxint?
+
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .L${opcode}_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                           #  is arg <= minint?
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .L${opcode}_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    move      rARG2, rBIX                  #  compare against self
+    move      rARG3, rTEMP
+    JAL(__nedf2)                           #  is arg == self?
+
+    move      t0, v0                       #  zero == no
+    li        v0, 0
+    bnez      t0, .L${opcode}_set_vreg     #  return zero for NaN
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    JAL(__fixdfsi)                         #  convert double to int
+    b         .L${opcode}_set_vreg
+#else
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    l.s       fv0, .LDOUBLE_TO_INT_maxret
+    bc1t      .L${opcode}_set_vreg_f
+
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    l.s       fv0, .LDOUBLE_TO_INT_minret
+    bc1t      .L${opcode}_set_vreg_f
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .L${opcode}_set_vreg_f
+
+    trunc.w.d  fv0, fa0
+    b         .L${opcode}_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword 0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword 0xc1e0000000000000              #  minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word 0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word 0x80000000
diff --git a/vm/mterp/mips/OP_DOUBLE_TO_LONG.S b/vm/mterp/mips/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..153d557
--- /dev/null
+++ b/vm/mterp/mips/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,76 @@
+%verify "executed"
+%include "mips/unflopWide.S" {"instr":"b d2l_doconv", "st_result":"STORE64(rRESULT0, rRESULT1, rOBJ)"}
+%break
+
+d2l_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bgez      t1, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    blez      t1, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    move      rARG2, rBIX
+    move      rARG3, rTEMP
+    JAL(__nedf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    JAL(__fixdfdi)
+
+#else
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .L${opcode}_set_vreg
+
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .L${opcode}_set_vreg
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .L${opcode}_set_vreg
+    JAL(__fixdfdi)
+#endif
+    b         .L${opcode}_set_vreg
+
+
+.LDOUBLE_TO_LONG_max:
+    .dword 0x43e0000000000000              #  maxlong, as a double (high word)
+.LDOUBLE_TO_LONG_min:
+    .dword 0xc3e0000000000000              #  minlong, as a double (high word)
+.LDOUBLE_TO_LONG_ret_max:
+    .dword 0x7fffffffffffffff
+.LDOUBLE_TO_LONG_ret_min:
+    .dword 0x8000000000000000
diff --git a/vm/mterp/mips/OP_EXECUTE_INLINE.S b/vm/mterp/mips/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..34f23ea
--- /dev/null
+++ b/vm/mterp/mips/OP_EXECUTE_INLINE.S
@@ -0,0 +1,104 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                         #  rBIX <- BBBB
+    EXPORT_PC()                            #  can throw
+    and       a2, kSubModeDebugProfile     #  Any going on?
+    bnez      a2, .L${opcode}_debugmode    #  yes - take slow path
+.L${opcode}_resume:
+    addu      a1, rSELF, offThread_retval  #  a1 <- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    # test boolean result of inline
+    beqz      v0, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+%break
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LW pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.L${opcode}_continue:
+    FETCH(rINST, 2)                        #  rINST <- FEDC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    and       t0, rINST, 0xf000            #  isolate F
+    ESRN(t1, rFP, t0, 10)
+    lw        a3, 0(t1)                    #  a3 <- vF (shift right 12, left 2)
+3:
+    and       t0, rINST, 0x0f00            #  isolate E
+    ESRN(t1, rFP, t0, 6)
+    lw        a2, 0(t1)                    #  a2 <- vE
+2:
+    and       t0, rINST, 0x00f0            #  isolate D
+    ESRN(t1, rFP, t0, 2)
+    lw        a1, 0(t1)                    #  a1 <- vD
+1:
+    and       t0, rINST, 0x000f            #  isolate C
+    EASN(t1, rFP, t0, 2)
+    lw        a0, 0(t1)                    #  a0 <- vC
+0:
+    la        rINST, gDvmInlineOpsTable    #  table of InlineOperation
+    EAS4(t1, rINST, rBIX)                  #  t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                           #  sizeof=16, "func" is first entry
+    # (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.L${opcode}_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .L${opcode}_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rINST, v0                    #  save result of inline
+    move      a0, rOBJ                     #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rINST, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..fd964f7
--- /dev/null
+++ b/vm/mterp/mips/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,92 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                       # rBIX<- BBBB
+    EXPORT_PC()                          # can throw
+    and       a2, kSubModeDebugProfile   # Any going on?
+    bnez      a2, .L${opcode}_debugmode  # yes - take slow path
+.L${opcode}_resume:
+    addu      a1, rSELF, offThread_retval # a1<- &self->retval
+    GET_OPA(a0)
+    sw        a1, STACK_OFFSET_ARG04(sp)  # push &self->retval
+    BAL(.L${opcode}_continue)             # make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)     #  restore gp
+    beqz      v0, common_exceptionThrown  # returned false, handle exception
+    FETCH_ADVANCE_INST(3)                 # advance rPC, load rINST
+    GET_INST_OPCODE(t0)                   # extract opcode from rINST
+    GOTO_OPCODE(t0)                       # jump to next instruction
+
+%break
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *  ra = return addr, above  [DO NOT JAL out of here w/o preserving ra]
+     */
+.L${opcode}_continue:
+    FETCH(rOBJ, 2)                       # rOBJ <- CCCC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    add       t0, rOBJ, 3
+    GET_VREG(a3, t0)
+3:
+    add       t0, rOBJ, 2
+    GET_VREG(a2, t0)
+2:
+    add       t0, rOBJ, 1
+    GET_VREG(a1, t0)
+1:
+    GET_VREG(a0, rOBJ)
+0:
+    la        rOBJ, gDvmInlineOpsTable      # table of InlineOperation
+    EAS4(t1, rOBJ, rBIX)                    # t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                            # sizeof=16, "func" is first entry
+    # not reached
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.L${opcode}_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .L${opcode}_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPA(a0)                            #  a0 <- A
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    move      rINST, rOBJ                  #  rINST<- method
+    BAL(.L${opcode}_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rOBJ, v0                     #  save result of inline
+    move      a0, rINST                    #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rOBJ, common_exceptionThrown #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S b/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..2cb225d
--- /dev/null
+++ b/vm/mterp/mips/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,120 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .L${opcode}_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .L${opcode}_continue
+%break
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.L${opcode}_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if $isrange
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .L${opcode}_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if $isrange
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
diff --git a/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..9611796
--- /dev/null
+++ b/vm/mterp/mips/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/mips/OP_FILL_ARRAY_DATA.S b/vm/mterp/mips/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..7a97799
--- /dev/null
+++ b/vm/mterp/mips/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a1, a1, 16                   #  a1 <- BBBBbbbb
+    or        a1, a0, a1                   #  a1 <- BBBBbbbb
+    GET_VREG(a0, a3)                       #  a0 <- vAA (array object)
+    EAS1(a1, rPC, a1)                      #  a1 <- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC()
+    JAL(dvmInterpHandleFillArrayData)      #  fill the array with predefined data
+    # 0 means an exception is thrown
+    beqz      v0, common_exceptionThrown   #  has exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..1e2120d
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"JAL(__extendsfdf2)", "instr_f":"cvt.d.s fv0, fa0"}
diff --git a/vm/mterp/mips/OP_FLOAT_TO_INT.S b/vm/mterp/mips/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..166d685
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_INT.S
@@ -0,0 +1,63 @@
+%verify "executed"
+%include "mips/unflop.S" {"instr":"b f2i_doconv", "instr_f":"b f2i_doconv"}
+%break
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x4f000000               #  (float)maxint
+    move      rBIX, a0
+    JAL(__gesf2)                           #  is arg >= maxint?
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX                     #  recover arg
+    li        a1, 0xcf000000               #  (float)minint
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .L${opcode}_set_vreg
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        v0, 0                        #  return zero for NaN
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfsi)
+    b         .L${opcode}_set_vreg
+#else
+    l.s       fa1, .LFLOAT_TO_INT_max
+    c.ole.s   fcc0, fa1, fa0
+    l.s       fv0, .LFLOAT_TO_INT_ret_max
+    bc1t      .L${opcode}_set_vreg_f
+
+    l.s       fa1, .LFLOAT_TO_INT_min
+    c.ole.s   fcc0, fa0, fa1
+    l.s       fv0, .LFLOAT_TO_INT_ret_min
+    bc1t      .L${opcode}_set_vreg_f
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .L${opcode}_set_vreg_f
+
+    trunc.w.s  fv0, fa0
+    b         .L${opcode}_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+    .word 0x4f000000
+.LFLOAT_TO_INT_min:
+    .word 0xcf000000
+.LFLOAT_TO_INT_ret_max:
+    .word 0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+    .word 0x80000000
+
diff --git a/vm/mterp/mips/OP_FLOAT_TO_LONG.S b/vm/mterp/mips/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..3e76027
--- /dev/null
+++ b/vm/mterp/mips/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,65 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"b f2l_doconv", "instr_f":"b f2l_doconv", "st_result":"STORE64(rRESULT0, rRESULT1, rOBJ)"}
+%break
+
+f2l_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x5f000000
+    move      rBIX, a0
+    JAL(__gesf2)
+
+    move      t0, v0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bgez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    li        a1, 0xdf000000
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    blez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .L${opcode}_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfdi)
+
+#else
+    l.s       fa1, .LLONG_TO_max
+    c.ole.s   fcc0, fa1, fa0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bc1t      .L${opcode}_set_vreg
+
+    l.s       fa1, .LLONG_TO_min
+    c.ole.s   fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    bc1t      .L${opcode}_set_vreg
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .L${opcode}_set_vreg
+
+    JAL(__fixsfdi)
+#endif
+
+    b         .L${opcode}_set_vreg
+
+.LLONG_TO_max:
+    .word 0x5f000000
+
+.LLONG_TO_min:
+    .word 0xdf000000
diff --git a/vm/mterp/mips/OP_GOTO.S b/vm/mterp/mips/OP_GOTO.S
new file mode 100644
index 0000000..27c20e3
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO.S
@@ -0,0 +1,23 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    sll       a0, rINST, 16                #  a0 <- AAxx0000
+    sra       a1, a0, 24                   #  a1 <- ssssssAA (sign-extended)
+    addu      a2, a1, a1                   #  a2 <- byte offset
+    /* If backwards branch refresh rBASE */
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) check for trace hotness
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_GOTO_16.S b/vm/mterp/mips/OP_GOTO_16.S
new file mode 100644
index 0000000..22c29da
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO_16.S
@@ -0,0 +1,21 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(a0, 1)                         #  a0 <- ssssAAAA (sign-extended)
+    addu      a1, a0, a0                   #  a1 <- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) hot trace head?
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_GOTO_32.S b/vm/mterp/mips/OP_GOTO_32.S
new file mode 100644
index 0000000..84598c2
--- /dev/null
+++ b/vm/mterp/mips/OP_GOTO_32.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(a0, 1)                           #  a0 <- aaaa (lo)
+    FETCH(a1, 2)                           #  a1 <- AAAA (hi)
+    sll       a1, a1, 16
+    or        a0, a0, a1                   #  a0 <- AAAAaaaa
+    addu      a1, a0, a0                   #  a1 <- byte offset
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    blez      a1, common_testUpdateProfile # (a0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgtz      a0, 2f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+2:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_IF_EQ.S b/vm/mterp/mips/OP_IF_EQ.S
new file mode 100644
index 0000000..183ec1b
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/mips/OP_IF_EQZ.S b/vm/mterp/mips/OP_IF_EQZ.S
new file mode 100644
index 0000000..5587291
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/mips/OP_IF_GE.S b/vm/mterp/mips/OP_IF_GE.S
new file mode 100644
index 0000000..19bc86f
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/mips/OP_IF_GEZ.S b/vm/mterp/mips/OP_IF_GEZ.S
new file mode 100644
index 0000000..5d4fa0f
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/mips/OP_IF_GT.S b/vm/mterp/mips/OP_IF_GT.S
new file mode 100644
index 0000000..8335bd3
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/mips/OP_IF_GTZ.S b/vm/mterp/mips/OP_IF_GTZ.S
new file mode 100644
index 0000000..3c70c35
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/mips/OP_IF_LE.S b/vm/mterp/mips/OP_IF_LE.S
new file mode 100644
index 0000000..c1524f9
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/mips/OP_IF_LEZ.S b/vm/mterp/mips/OP_IF_LEZ.S
new file mode 100644
index 0000000..fa930aa
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/mips/OP_IF_LT.S b/vm/mterp/mips/OP_IF_LT.S
new file mode 100644
index 0000000..fbda8bc
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/mips/OP_IF_LTZ.S b/vm/mterp/mips/OP_IF_LTZ.S
new file mode 100644
index 0000000..e93dd62
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/mips/OP_IF_NE.S b/vm/mterp/mips/OP_IF_NE.S
new file mode 100644
index 0000000..c484ede
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/mips/OP_IF_NEZ.S b/vm/mterp/mips/OP_IF_NEZ.S
new file mode 100644
index 0000000..24cbb6b
--- /dev/null
+++ b/vm/mterp/mips/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/zcmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/mips/OP_IGET.S b/vm/mterp/mips/OP_IGET.S
new file mode 100644
index 0000000..87dd9b8
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET.S
@@ -0,0 +1,49 @@
+%default { "load":"lw", "barrier":"     # noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    $load a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    $barrier                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_BOOLEAN.S b/vm/mterp/mips/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..4f32dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_BYTE.S b/vm/mterp/mips/OP_IGET_BYTE.S
new file mode 100644
index 0000000..f699e87
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_CHAR.S b/vm/mterp/mips/OP_IGET_CHAR.S
new file mode 100644
index 0000000..cb3a03b
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT.S b/vm/mterp/mips/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..4f32dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S b/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..e4f7d00
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET_QUICK.S"
diff --git a/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..30c6774
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IGET_QUICK.S b/vm/mterp/mips/OP_IGET_QUICK.S
new file mode 100644
index 0000000..7d869ca
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_SHORT.S b/vm/mterp/mips/OP_IGET_SHORT.S
new file mode 100644
index 0000000..f699e87
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "mips/OP_IGET.S"
diff --git a/vm/mterp/mips/OP_IGET_VOLATILE.S b/vm/mterp/mips/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..30c6774
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IGET_WIDE.S b/vm/mterp/mips/OP_IGET_WIDE.S
new file mode 100644
index 0000000..2cdf80c
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE.S
@@ -0,0 +1,49 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if $volatile
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_WIDE_QUICK.S b/vm/mterp/mips/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..f4d8fdb
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    # iget-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1                   #  t0 <- a3 + a1
+    LOAD64(a0, a1, t0)                     #  a0 <- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..1804fb1
--- /dev/null
+++ b/vm/mterp/mips/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_INSTANCE_OF.S b/vm/mterp/mips/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..f296d44
--- /dev/null
+++ b/vm/mterp/mips/OP_INSTANCE_OF.S
@@ -0,0 +1,82 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    # instance-of vA, vB, class            /* CCCC */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- pDvmDex
+    # is object null?
+    beqz      a0, .L${opcode}_store        #  null obj, not an instance, store a0
+    FETCH(a3, 1)                           #  a3 <- CCCC
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- pDvmDex->pResClasses
+    LOAD_eas2(a1, a2, a3)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .L${opcode}_resolve      #  not resolved, do it now
+.L${opcode}_resolved:                   #  a0=obj->clazz, a1=resolved class
+    # same class (trivial success)?
+    beq       a0, a1, .L${opcode}_trivial  #  yes, trivial finish
+    b         .L${opcode}_fullcheck        #  no, do full check
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  rOBJ holds A
+     */
+.L${opcode}_trivial:
+    li        a0, 1                        #  indicate success
+    # fall thru
+    /*
+     * a0   holds boolean result
+     * rOBJ holds A
+     */
+.L${opcode}_store:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, rOBJ)                     #  vA <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0   holds obj->clazz
+     *  a1   holds class resolved from BBBB
+     *  rOBJ holds A
+     */
+.L${opcode}_fullcheck:
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    move      a0, v0                       #  fall through to ${opcode}_store
+    b         .L${opcode}_store
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a3   holds BBBB
+     *  rOBJ holds A
+     */
+.L${opcode}_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    move      a1, a3                       #  a1 <- BBBB
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    b         .L${opcode}_resolved         #  pick up where we left off
+
diff --git a/vm/mterp/mips/OP_INT_TO_BYTE.S b/vm/mterp/mips/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..e9edb97
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"sll a0, a0, 24", "instr":"sra a0, a0, 24"}
diff --git a/vm/mterp/mips/OP_INT_TO_CHAR.S b/vm/mterp/mips/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..5da74da
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"", "instr":"and a0, 0xffff"}
diff --git a/vm/mterp/mips/OP_INT_TO_DOUBLE.S b/vm/mterp/mips/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..5ee4813
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWider.S" {"instr":"JAL(__floatsidf)", "instr_f":"cvt.d.w fv0, fa0"}
diff --git a/vm/mterp/mips/OP_INT_TO_FLOAT.S b/vm/mterp/mips/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..9cf7c48
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflop.S" {"instr":"JAL(__floatsisf)", "instr_f":"cvt.s.w fv0, fa0"}
diff --git a/vm/mterp/mips/OP_INT_TO_LONG.S b/vm/mterp/mips/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..5691ea5
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWider.S" {"instr":"sra a1, a0, 31"}
diff --git a/vm/mterp/mips/OP_INT_TO_SHORT.S b/vm/mterp/mips/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..d1fc349
--- /dev/null
+++ b/vm/mterp/mips/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"preinstr":"sll a0, 16", "instr":"sra a0, 16"}
diff --git a/vm/mterp/mips/OP_INVOKE_DIRECT.S b/vm/mterp/mips/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..9bbf334
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_DIRECT.S
@@ -0,0 +1,42 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!$isrange)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethod${routine} #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
diff --git a/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..ef88011
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_INTERFACE.S b/vm/mterp/mips/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..0924093
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,28 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!$isrange)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
diff --git a/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..6257c8a
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..705be9f
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,44 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(a1, ${cccc})                  # a1<- CCCC
+    GET_VREG(a0, a1)                    # a0<- "this" ptr
+    # check for NULL
+    beqz    a0, common_errNullObject    # export PC and throw NPE
+    LOAD_base_offObject_clazz(a1, a0)   # a1<- obj->clazz
+    LOAD_base_offClassObject_accessFlags(a2, a1) # a2<- clazz->accessFlags
+    and     a2, CLASS_ISFINALIZABLE     # is this class finalizable?
+    beqz    a2, .L${opcode}_finish      # no, go
+
+.L${opcode}_setFinal:
+    EXPORT_PC()                         # can throw
+    JAL(dvmSetFinalizable)              # call dvmSetFinalizable(obj)
+    LOAD_offThread_exception(a0, rSELF)	# a0<- self->exception
+    # exception pending?
+    bnez    a0, common_exceptionThrown  # yes, handle it
+
+.L${opcode}_finish:
+    lhu     a1, offThread_subMode(rSELF)
+    and     a1, kSubModeDebuggerActive  # debugger active?
+    bnez    a1, .L${opcode}_debugger    # Yes - skip optimization
+    FETCH_ADVANCE_INST(${cccc}+1)       # advance to next instr, load rINST
+    GET_INST_OPCODE(t0)                 # t0<- opcode from rINST
+    GOTO_OPCODE(t0)                     # execute it
+
+%break
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    li      t0, OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(a1, t0)            # execute it
diff --git a/vm/mterp/mips/OP_INVOKE_STATIC.S b/vm/mterp/mips/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..ba2d7cc
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_STATIC.S
@@ -0,0 +1,54 @@
+%default { "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethod${routine} #  yes, continue on
+    b         .L${opcode}_resolve
+%break
+
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethod${routine} #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethod${routine} #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethod${routine} #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
diff --git a/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..9b45216
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER.S b/vm/mterp/mips/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..6b44380
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER.S
@@ -0,0 +1,60 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!$isrange)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .L${opcode}_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .L${opcode}_continue
+%break
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.L${opcode}_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .L${opcode}_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethod${routine} #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.L${opcode}_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..eb5465a
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,26 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!$isrange)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..ade7bba
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..7821d31
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..9f6d2c3
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,48 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!$isrange)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .L${opcode}_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .L${opcode}_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+%break
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.L${opcode}_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethod${routine} #  (a0=method, rOBJ="this")
+
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..1952b70
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!$isrange)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethod${routine} #  (a0=method, r9="this")
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..8048895
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..5f86b4b
--- /dev/null
+++ b/vm/mterp/mips/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/mips/OP_IPUT.S b/vm/mterp/mips/OP_IPUT.S
new file mode 100644
index 0000000..3755089
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT.S
@@ -0,0 +1,50 @@
+%default { "store":"sw","postbarrier":"    #  noop", "prebarrier":"    #  noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+    $prebarrier                            #  releasing store
+    $store a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+    $postbarrier
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_BOOLEAN.S b/vm/mterp/mips/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_BYTE.S b/vm/mterp/mips/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_CHAR.S b/vm/mterp/mips/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT.S b/vm/mterp/mips/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..829deab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT.S
@@ -0,0 +1,56 @@
+%default { "store":"sw", "postbarrier":"    #  noop", "prebarrier":"    #  noop", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    #BAL(common_squeak${sqnum})
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t2, rOBJ, a3                 #  form address
+    $prebarrier                            #  releasing store
+    $store a0, (t2)                        #  obj.field (32 bits) <- a0
+    $postbarrier
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..eb0afb4
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,21 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    beqz      a0, 1f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t1, a3, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, 0(t2)
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8320a7d
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IPUT_QUICK.S b/vm/mterp/mips/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..8976265
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick, iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_SHORT.S b/vm/mterp/mips/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4f09dab
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S"
diff --git a/vm/mterp/mips/OP_IPUT_VOLATILE.S b/vm/mterp/mips/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..4cb365f
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_IPUT_WIDE.S b/vm/mterp/mips/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..b8d9690
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE.S
@@ -0,0 +1,48 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .L${opcode}_finish       #  yes, finish up
+    b         common_exceptionThrown
+%break
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.L${opcode}_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if $volatile
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S b/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..f86c403
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    # iput-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPA4(a0)                           #  a0 <- A(+)
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a2, a1)                       #  a2 <- fp[B], the object pointer
+    EAS2(a3, rFP, a0)                      #  a3 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[A]
+    # check object for null
+    beqz      a2, common_errNullObject     #  object was null
+    FETCH(a3, 1)                           #  a3 <- field byte offset
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      a2, a2, a3                   #  obj.field (64 bits, aligned) <- a0/a1
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..784be66
--- /dev/null
+++ b/vm/mterp/mips/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_LONG_TO_DOUBLE.S b/vm/mterp/mips/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..fad9ec0
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unflopWide.S" {"instr":"JAL(__floatdidf)", "ld_arg":"LOAD64(rARG0, rARG1, a3)"}
diff --git a/vm/mterp/mips/OP_LONG_TO_FLOAT.S b/vm/mterp/mips/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..86a143a
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopNarrower.S" {"instr":"JAL(__floatdisf)", "instr_f":"JAL(__floatdisf)", "load":"LOAD64(rARG0, rARG1, a3)"}
diff --git a/vm/mterp/mips/OP_LONG_TO_INT.S b/vm/mterp/mips/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..fe8f865
--- /dev/null
+++ b/vm/mterp/mips/OP_LONG_TO_INT.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef HAVE_BIG_ENDIAN
+    addu      a1, a1, 1
+#endif
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
diff --git a/vm/mterp/mips/OP_MONITOR_ENTER.S b/vm/mterp/mips/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..1f5541e
--- /dev/null
+++ b/vm/mterp/mips/OP_MONITOR_ENTER.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    move      a0, rSELF                    #  a0 <- self
+    EXPORT_PC()                            #  export PC so we can grab stack trace
+    # null object?
+    beqz      a1, common_errNullObject     #  null object, throw an exception
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    JAL(dvmLockObject)                     #  call(self, obj)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MONITOR_EXIT.S b/vm/mterp/mips/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..fc671cb
--- /dev/null
+++ b/vm/mterp/mips/OP_MONITOR_EXIT.S
@@ -0,0 +1,26 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EXPORT_PC()                            #  before fetch: export the PC
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    # null object?
+    beqz      a1, 1f
+    move      a0, rSELF                    #  a0 <- self
+    JAL(dvmUnlockObject)                   #  v0 <- success for unlock(self, obj)
+    # failed?
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    beqz      v0, common_exceptionThrown   #  yes, exception is pending
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    b         common_errNullObject
diff --git a/vm/mterp/mips/OP_MOVE.S b/vm/mterp/mips/OP_MOVE.S
new file mode 100644
index 0000000..dbf7ea4
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
diff --git a/vm/mterp/mips/OP_MOVE_16.S b/vm/mterp/mips/OP_MOVE_16.S
new file mode 100644
index 0000000..8410b93
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
diff --git a/vm/mterp/mips/OP_MOVE_EXCEPTION.S b/vm/mterp/mips/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..1040155
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-exception vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    LOAD_offThread_exception(a3, rSELF)    #  a3 <- dvmGetException bypass
+    li        a1, 0                        #  a1 <- 0
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    SET_VREG(a3, a2)                       #  fp[AA] <- exception obj
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE_offThread_exception(a1, rSELF)   #  dvmClearException bypass
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_FROM16.S b/vm/mterp/mips/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..d018140
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT.S b/vm/mterp/mips/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..7150ed5
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE.S"
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT_16.S b/vm/mterp/mips/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..c3dfae0
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_16.S"
diff --git a/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..1ec1ae9
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/mips/OP_MOVE_RESULT.S b/vm/mterp/mips/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..05f40fa
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
diff --git a/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..74aa091
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S b/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..56671e6
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- retval.j
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE.S b/vm/mterp/mips/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..0a2b140
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[B]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[A] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE_16.S b/vm/mterp/mips/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..16e9d6e
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE_16.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 2)                           #  a3 <- BBBB
+    FETCH(a2, 1)                           #  a2 <- AAAA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AAAA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AAAA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S b/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..7fd217a
--- /dev/null
+++ b/vm/mterp/mips/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 1)                           #  a3 <- BBBB
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MUL_DOUBLE.S b/vm/mterp/mips/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..565ca57
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__muldf3)", "instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..8d1dac1
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__muldf3)", "instr_f":"mul.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_FLOAT.S b/vm/mterp/mips/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..af9bb3b
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__mulsf3)", "instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..726e8a4
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__mulsf3)", "instr_f":"mul.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_MUL_INT.S b/vm/mterp/mips/OP_MUL_INT.S
new file mode 100644
index 0000000..d9d6d2a
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_2ADDR.S b/vm/mterp/mips/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..bbf4d77
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_LIT16.S b/vm/mterp/mips/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..654e76d
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_INT_LIT8.S b/vm/mterp/mips/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..c0278ae
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"mul a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_MUL_LONG.S b/vm/mterp/mips/OP_MUL_LONG.S
new file mode 100644
index 0000000..c16a230
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_LONG.S
@@ -0,0 +1,41 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       t0, a0, 255                  #  a2 <- BB
+    srl       t1, a0, 8                    #  a3 <- CC
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[BB]
+    LOAD64(a0, a1, t0)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t1, rFP, t1)                      #  t0 <- &fp[CC]
+    LOAD64(a2, a3, t1)                     #  a2/a3 <- vCC/vCC+1
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t0, a2, a1                   #  t0= a2a1
+    addu      v1, v1, t1                   #  v1+= hi(a2a0)
+    addu      v1, v1, t0                   #  v1= a3a0 + a2a1;
+
+    GET_OPA(a0)                            #  a0 <- AA
+    EAS2(a0, rFP, a0)                      #  a0 <- &fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    b         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, a0)                    #  vAA::vAA+1 <- v0(low) :: v1(high)
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_MUL_LONG_2ADDR.S b/vm/mterp/mips/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..85de7be
--- /dev/null
+++ b/vm/mterp/mips/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * See comments in OP_MUL_LONG.S
+     */
+    /* mul-long/2addr vA, vB */
+    GET_OPA4(t0)                           #  t0 <- A+
+
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[A]
+    LOAD64(a0, a1, t0)                     #  vAA.low / high
+
+    GET_OPB(t1)                            #  t1 <- B
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[B]
+    LOAD64(a2, a3, t1)                     #  vBB.low / high
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t2, a2, a1                   #  t2= a2a1
+    addu      v1, v1, t1                   #  v1= a3a0 + hi(a2a0)
+    addu      v1, v1, t2                   #  v1= v1 + a2a1;
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    # vAA <- v0 (low)
+    STORE64(v0, v1, t0)                    #  vAA+1 <- v1 (high)
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_NEG_DOUBLE.S b/vm/mterp/mips/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..5707c65
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWide.S" {"instr":"addu a1, a1, 0x80000000"}
diff --git a/vm/mterp/mips/OP_NEG_FLOAT.S b/vm/mterp/mips/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..7e25e55
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"addu a0, a0, 0x80000000"}
diff --git a/vm/mterp/mips/OP_NEG_INT.S b/vm/mterp/mips/OP_NEG_INT.S
new file mode 100644
index 0000000..da87a6a
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"negu a0, a0"}
diff --git a/vm/mterp/mips/OP_NEG_LONG.S b/vm/mterp/mips/OP_NEG_LONG.S
new file mode 100644
index 0000000..a562987
--- /dev/null
+++ b/vm/mterp/mips/OP_NEG_LONG.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%include "mips/unopWide.S" {"result0":"v0", "result1":"v1", "preinstr":"negu v0, a0", "instr":"negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0"}
+
diff --git a/vm/mterp/mips/OP_NEW_ARRAY.S b/vm/mterp/mips/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..2529744
--- /dev/null
+++ b/vm/mterp/mips/OP_NEW_ARRAY.S
@@ -0,0 +1,61 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    FETCH(a2, 1)                           #  a2 <- CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    GET_VREG(a1, a0)                       #  a1 <- vB (array length)
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- resolved class
+    # check length
+    bltz      a1, common_errNegativeArraySize #  negative length, bail - len in a1
+    EXPORT_PC()                            #  req'd for resolve, alloc
+    # already resolved?
+    beqz      a0, .L${opcode}_resolve
+
+    /*
+     * Finish allocation.
+     *
+     *  a0 holds class
+     *  a1 holds array length
+     */
+.L${opcode}_finish:
+    li        a2, ALLOC_DONT_TRACK         #  don't track in local refs table
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(clazz, length, flags)
+    GET_OPA4(a2)                           #  a2 <- A+
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(v0, a2)                       #  vA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+%break
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  a1 holds array length
+     *  a2 holds class ref CCCC
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      rOBJ, a1                     #  rOBJ <- length (save)
+    move      a1, a2                       #  a1 <- CCCC
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a1, rOBJ                     #  a1 <- length (restore)
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a0, v0
+    b         .L${opcode}_finish           #  continue with ${opcode}_finish
+
+
diff --git a/vm/mterp/mips/OP_NEW_INSTANCE.S b/vm/mterp/mips/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..f40ddd5
--- /dev/null
+++ b/vm/mterp/mips/OP_NEW_INSTANCE.S
@@ -0,0 +1,106 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    # new-instance vAA, class              /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX <- &resolved_class
+#endif
+    EXPORT_PC()                            #  req'd for init, resolve, alloc
+    # already resolved?
+    beqz      a0, .L${opcode}_resolve      #  no, resolve it now
+.L${opcode}_resolved:                      #  a0=class
+    lbu       a1, offClassObject_status(a0) #  a1 <- ClassStatus enum
+    # has class been initialized?
+    li        t0, CLASS_INITIALIZED
+    move      rOBJ, a0                     #  save a0
+    bne       a1, t0, .L${opcode}_needinit #  no, init class now
+
+.L${opcode}_initialized:                   #  a0=class
+    LOAD_base_offClassObject_accessFlags(a3, a0) #  a3 <- clazz->accessFlags
+    li        a1, ALLOC_DONT_TRACK         #  flags for alloc call
+    # a0=class
+    JAL(dvmAllocObject)                    #  v0 <- new object
+    GET_OPA(a3)                            #  a3 <- AA
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    lhu       a1, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    and       a1, kSubModeJitTraceBuild    #  under construction?
+    bnez      a1, .L${opcode}_jitCheck
+#else
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+#endif
+    b         .L${opcode}_continue
+
+%break
+
+.L${opcode}_continue:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(v0, a3)                       #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * v0: new object
+     * a3: vAA
+     */
+.L${opcode}_jitCheck:
+    lw        a1, 0(rBIX)                  #  reload resolved class
+    # okay?
+    bnez      a1, .L${opcode}_continue     #  yes, finish
+    move      rOBJ, v0                     #  preserve new object
+    move      rBIX, a3                     #  preserve vAA
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rOBJ, rBIX)                   #  vAA <- new object
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  a0 holds class object
+     */
+.L${opcode}_needinit:
+    JAL(dvmInitClass)                      #  initialize class
+    move      a0, rOBJ                     #  restore a0
+    # check boolean result
+    bnez      v0, .L${opcode}_initialized  #  success, continue
+    b         common_exceptionThrown       #  failed, deal with init exception
+
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a1 holds BBBB
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    move      a0, v0
+    # got null?
+    bnez      v0, .L${opcode}_resolved     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
diff --git a/vm/mterp/mips/OP_NOP.S b/vm/mterp/mips/OP_NOP.S
new file mode 100644
index 0000000..38a5eb4
--- /dev/null
+++ b/vm/mterp/mips/OP_NOP.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    FETCH_ADVANCE_INST(1)                  #  advance to next instr, load rINST
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)                        #  execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type dalvik_inst, @function
+dalvik_inst:
+    .ent dalvik_inst
+    .end dalvik_inst
+#endif
+
diff --git a/vm/mterp/mips/OP_NOT_INT.S b/vm/mterp/mips/OP_NOT_INT.S
new file mode 100644
index 0000000..3402d19
--- /dev/null
+++ b/vm/mterp/mips/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unop.S" {"instr":"not a0, a0"}
diff --git a/vm/mterp/mips/OP_NOT_LONG.S b/vm/mterp/mips/OP_NOT_LONG.S
new file mode 100644
index 0000000..8947c4e
--- /dev/null
+++ b/vm/mterp/mips/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/unopWide.S" {"preinstr":"not a0, a0", "instr":"not a1, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT.S b/vm/mterp/mips/OP_OR_INT.S
new file mode 100644
index 0000000..683242f
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_2ADDR.S b/vm/mterp/mips/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..e63835b
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_LIT16.S b/vm/mterp/mips/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..c12495d
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_INT_LIT8.S b/vm/mterp/mips/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..f2ac2d0
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"or a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_OR_LONG.S b/vm/mterp/mips/OP_OR_LONG.S
new file mode 100644
index 0000000..8b080f6
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_OR_LONG_2ADDR.S b/vm/mterp/mips/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..ef37dbf
--- /dev/null
+++ b/vm/mterp/mips/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_PACKED_SWITCH.S b/vm/mterp/mips/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..add1dac
--- /dev/null
+++ b/vm/mterp/mips/OP_PACKED_SWITCH.S
@@ -0,0 +1,34 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL($func)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_REM_DOUBLE.S b/vm/mterp/mips/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..4329ed3
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(fmod)", "instr_f":"JAL(fmod)"}
diff --git a/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..97cd893
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(fmod)", "instr_f":"JAL(fmod)"}
diff --git a/vm/mterp/mips/OP_REM_FLOAT.S b/vm/mterp/mips/OP_REM_FLOAT.S
new file mode 100644
index 0000000..e68cfb5
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(fmodf)", "instr_f":"JAL(fmodf)"}
diff --git a/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S b/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..f78cbb3
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(fmodf)", "instr_f":"JAL(fmodf)"}
diff --git a/vm/mterp/mips/OP_REM_INT.S b/vm/mterp/mips/OP_REM_INT.S
new file mode 100644
index 0000000..f1dcf37
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_2ADDR.S b/vm/mterp/mips/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..85d616b
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_LIT16.S b/vm/mterp/mips/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..1f31442
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_INT_LIT8.S b/vm/mterp/mips/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..4b5bb82
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"div zero, a0, a1; mfhi a0", "chkzero":"1"}
diff --git a/vm/mterp/mips/OP_REM_LONG.S b/vm/mterp/mips/OP_REM_LONG.S
new file mode 100644
index 0000000..d76221a
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_LONG.S
@@ -0,0 +1,7 @@
+%verify "executed"
+/* ldivmod returns quotient in a0/a1 and remainder in a2/a3 */
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide.S" { "arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_REM_LONG_2ADDR.S b/vm/mterp/mips/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..be194a5
--- /dev/null
+++ b/vm/mterp/mips/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,6 @@
+%verify "executed"
+#ifdef HAVE_LITTLE_ENDIAN
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#else
+%include "mips/binopWide2addr.S" {"arg0":"a1", "arg1":"a0", "arg2":"a3", "arg3":"a2", "result0":"v1", "result1":"v0", "instr":"JAL(__moddi3)", "chkzero":"1"}
+#endif
diff --git a/vm/mterp/mips/OP_RETURN.S b/vm/mterp/mips/OP_RETURN.S
new file mode 100644
index 0000000..acc01cf
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RETURN_OBJECT.S b/vm/mterp/mips/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..4459668
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_RETURN.S"
diff --git a/vm/mterp/mips/OP_RETURN_VOID.S b/vm/mterp/mips/OP_RETURN_VOID.S
new file mode 100644
index 0000000..781f835
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_VOID.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S b/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S
new file mode 100644
index 0000000..4cb5b9b
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_VOID_BARRIER.S
@@ -0,0 +1,3 @@
+%verify "executed"
+    SMP_DMB
+    b         common_returnFromMethod
diff --git a/vm/mterp/mips/OP_RETURN_WIDE.S b/vm/mterp/mips/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..bd93d6a
--- /dev/null
+++ b/vm/mterp/mips/OP_RETURN_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vAA/vAA+1
+    STORE64(a0, a1, a3)                    #  retval <- a0/a1
+    b         common_returnFromMethod
+
diff --git a/vm/mterp/mips/OP_RSUB_INT.S b/vm/mterp/mips/OP_RSUB_INT.S
new file mode 100644
index 0000000..03918ea
--- /dev/null
+++ b/vm/mterp/mips/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "mips/binopLit16.S" {"instr":"subu a0, a1, a0"}
diff --git a/vm/mterp/mips/OP_RSUB_INT_LIT8.S b/vm/mterp/mips/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..75d3d40
--- /dev/null
+++ b/vm/mterp/mips/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"subu a0, a1, a0"}
diff --git a/vm/mterp/mips/OP_SGET.S b/vm/mterp/mips/OP_SGET.S
new file mode 100644
index 0000000..80e1913
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET.S
@@ -0,0 +1,50 @@
+%default { "barrier":"                  #  no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .L${opcode}_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    $barrier                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
diff --git a/vm/mterp/mips/OP_SGET_BOOLEAN.S b/vm/mterp/mips/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_BYTE.S b/vm/mterp/mips/OP_SGET_BYTE.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_CHAR.S b/vm/mterp/mips/OP_SGET_CHAR.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_OBJECT.S b/vm/mterp/mips/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..d880f97
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SGET_SHORT.S b/vm/mterp/mips/OP_SGET_SHORT.S
new file mode 100644
index 0000000..86024ec
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S"
diff --git a/vm/mterp/mips/OP_SGET_VOLATILE.S b/vm/mterp/mips/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..d880f97
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SGET_WIDE.S b/vm/mterp/mips/OP_SGET_WIDE.S
new file mode 100644
index 0000000..0e72992
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_WIDE.S
@@ -0,0 +1,58 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .L${opcode}_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if $volatile
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
diff --git a/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..ca2fce4
--- /dev/null
+++ b/vm/mterp/mips/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_SHL_INT.S b/vm/mterp/mips/OP_SHL_INT.S
new file mode 100644
index 0000000..9981dec
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_INT_2ADDR.S b/vm/mterp/mips/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..0ac0a8f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_INT_LIT8.S b/vm/mterp/mips/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..1110037
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"sll a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHL_LONG.S b/vm/mterp/mips/OP_SHL_LONG.S
new file mode 100644
index 0000000..817ac2f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_LONG.S
@@ -0,0 +1,33 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t2)                            #  t2 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHL_LONG_2ADDR.S b/vm/mterp/mips/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..1191427
--- /dev/null
+++ b/vm/mterp/mips/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(rOBJ, rFP, t2)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHR_INT.S b/vm/mterp/mips/OP_SHR_INT.S
new file mode 100644
index 0000000..c5911e7
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_INT_2ADDR.S b/vm/mterp/mips/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..b979e9f
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_INT_LIT8.S b/vm/mterp/mips/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..6124619
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"sra a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SHR_LONG.S b/vm/mterp/mips/OP_SHR_LONG.S
new file mode 100644
index 0000000..6906978
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_LONG.S
@@ -0,0 +1,33 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t3)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    STORE64(v0, v1, t3)                    #  vAA/VAA+1 <- v0/v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SHR_LONG_2ADDR.S b/vm/mterp/mips/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..439923e
--- /dev/null
+++ b/vm/mterp/mips/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[A]
+    LOAD64(a0, a1, t2)                     #  a0/a1 <- vAA/vAA+1
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_SPARSE_SWITCH.S b/vm/mterp/mips/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..32067de
--- /dev/null
+++ b/vm/mterp/mips/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/mips/OP_SPUT.S b/vm/mterp/mips/OP_SPUT.S
new file mode 100644
index 0000000..722a12f
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT.S
@@ -0,0 +1,50 @@
+%default { "postbarrier":"#  no-op", "prebarrier":"#  no-op" }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .L${opcode}_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .L${opcode}_finish            # resume
+%break
+
+.L${opcode}_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $prebarrier                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    $postbarrier
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_SPUT_BOOLEAN.S b/vm/mterp/mips/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_BYTE.S b/vm/mterp/mips/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_CHAR.S b/vm/mterp/mips/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_OBJECT.S b/vm/mterp/mips/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..0fd3db3
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_OBJECT.S
@@ -0,0 +1,56 @@
+%default { "postbarrier":"#  no-op", "prebarrier":"#  no-op" }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .L${opcode}_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .L${opcode}_finish             # resume
+
+%break
+.L${opcode}_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    $prebarrier                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    $postbarrier
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8b6dc14
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT_OBJECT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SPUT_SHORT.S b/vm/mterp/mips/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..96434b7
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S"
diff --git a/vm/mterp/mips/OP_SPUT_VOLATILE.S b/vm/mterp/mips/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..9e1f1a5
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT.S" {"prebarrier":"SMP_DMB_ST", "postbarrier":"SMP_DMB"}
diff --git a/vm/mterp/mips/OP_SPUT_WIDE.S b/vm/mterp/mips/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..3e1d042
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_WIDE.S
@@ -0,0 +1,58 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .L${opcode}_resolve      #  yes, do resolve
+.L${opcode}_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if $volatile
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.L${opcode}_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .L${opcode}_finish           # resume
diff --git a/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..359b37f
--- /dev/null
+++ b/vm/mterp/mips/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/mips/OP_SUB_DOUBLE.S b/vm/mterp/mips/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..3b6fa6d
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide.S" {"instr":"JAL(__subdf3)", "instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..cdd973e
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflopWide2addr.S" {"instr":"JAL(__subdf3)", "instr_f":"sub.d fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_FLOAT.S b/vm/mterp/mips/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..9096267
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop.S" {"instr":"JAL(__subsf3)", "instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..143b7e6
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binflop2addr.S" {"instr":"JAL(__subsf3)", "instr_f":"sub.s fv0, fa0, fa1"}
diff --git a/vm/mterp/mips/OP_SUB_INT.S b/vm/mterp/mips/OP_SUB_INT.S
new file mode 100644
index 0000000..aaa6a7b
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"subu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SUB_INT_2ADDR.S b/vm/mterp/mips/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..0032229
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"subu a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_SUB_LONG.S b/vm/mterp/mips/OP_SUB_LONG.S
new file mode 100644
index 0000000..700d4ea
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_LONG.S
@@ -0,0 +1,10 @@
+%verify "executed"
+/*
+ * For little endian the code sequence looks as follows:
+ *    subu    v0,a0,a2
+ *    subu    v1,a1,a3
+ *    sltu    a0,a0,v0
+ *    subu    v1,v1,a0
+ */
+%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" }
+
diff --git a/vm/mterp/mips/OP_SUB_LONG_2ADDR.S b/vm/mterp/mips/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..9b12d69
--- /dev/null
+++ b/vm/mterp/mips/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,5 @@
+%verify "executed"
+/*
+ * See comments in OP_SUB_LONG.S
+ */
+%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" }
diff --git a/vm/mterp/mips/OP_THROW.S b/vm/mterp/mips/OP_THROW.S
new file mode 100644
index 0000000..b879b29
--- /dev/null
+++ b/vm/mterp/mips/OP_THROW.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (exception object)
+    EXPORT_PC()                            #  exception handler can throw
+    # null object?
+    beqz      a1, common_errNullObject     #  yes, throw an NPE instead
+    # bypass dvmSetException, just store it
+    STORE_offThread_exception(a1, rSELF)   #  thread->exception <- obj
+    b         common_exceptionThrown
+
diff --git a/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..a68b256
--- /dev/null
+++ b/vm/mterp/mips/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,15 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    EXPORT_PC()                            #  export the PC
+    GET_OPA(a1)                            #  a1 <- AA
+    JAL(dvmThrowVerificationError)         #  always throws
+    b         common_exceptionThrown       #  handle exception
+
diff --git a/vm/mterp/mips/OP_UNUSED_3E.S b/vm/mterp/mips/OP_UNUSED_3E.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_3F.S b/vm/mterp/mips/OP_UNUSED_3F.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_40.S b/vm/mterp/mips/OP_UNUSED_40.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_41.S b/vm/mterp/mips/OP_UNUSED_41.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_42.S b/vm/mterp/mips/OP_UNUSED_42.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_43.S b/vm/mterp/mips/OP_UNUSED_43.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_73.S b/vm/mterp/mips/OP_UNUSED_73.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_79.S b/vm/mterp/mips/OP_UNUSED_79.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_7A.S b/vm/mterp/mips/OP_UNUSED_7A.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E3.S b/vm/mterp/mips/OP_UNUSED_E3.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E3.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E4.S b/vm/mterp/mips/OP_UNUSED_E4.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E4.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E5.S b/vm/mterp/mips/OP_UNUSED_E5.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E5.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E6.S b/vm/mterp/mips/OP_UNUSED_E6.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E6.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E7.S b/vm/mterp/mips/OP_UNUSED_E7.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E7.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E8.S b/vm/mterp/mips/OP_UNUSED_E8.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E8.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_E9.S b/vm/mterp/mips/OP_UNUSED_E9.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_E9.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EA.S b/vm/mterp/mips/OP_UNUSED_EA.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EA.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EB.S b/vm/mterp/mips/OP_UNUSED_EB.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EB.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EC.S b/vm/mterp/mips/OP_UNUSED_EC.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EC.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_ED.S b/vm/mterp/mips/OP_UNUSED_ED.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_ED.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_EF.S b/vm/mterp/mips/OP_UNUSED_EF.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_EF.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_F1.S b/vm/mterp/mips/OP_UNUSED_F1.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_F1.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FC.S b/vm/mterp/mips/OP_UNUSED_FC.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FC.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FD.S b/vm/mterp/mips/OP_UNUSED_FD.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FD.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FE.S b/vm/mterp/mips/OP_UNUSED_FE.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FE.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_UNUSED_FF.S b/vm/mterp/mips/OP_UNUSED_FF.S
new file mode 100644
index 0000000..99ef3cf
--- /dev/null
+++ b/vm/mterp/mips/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "mips/unused.S"
diff --git a/vm/mterp/mips/OP_USHR_INT.S b/vm/mterp/mips/OP_USHR_INT.S
new file mode 100644
index 0000000..7b474b6
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_USHR_INT_2ADDR.S b/vm/mterp/mips/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..71b5e36
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1 "}
diff --git a/vm/mterp/mips/OP_USHR_INT_LIT8.S b/vm/mterp/mips/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..7dbe863
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"preinstr":"and a1, a1, 31", "instr":"srl a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_USHR_LONG.S b/vm/mterp/mips/OP_USHR_LONG.S
new file mode 100644
index 0000000..acd9d15
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- v0/v1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_USHR_LONG_2ADDR.S b/vm/mterp/mips/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..103cc98
--- /dev/null
+++ b/vm/mterp/mips/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    GET_OPA4(t3)                           #  t3 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[A]
+    LOAD64(a0, a1, t3)                     #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t3)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
diff --git a/vm/mterp/mips/OP_XOR_INT.S b/vm/mterp/mips/OP_XOR_INT.S
new file mode 100644
index 0000000..6551e75
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_2ADDR.S b/vm/mterp/mips/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..f93b782
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binop2addr.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_LIT16.S b/vm/mterp/mips/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..add8ef2
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit16.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_INT_LIT8.S b/vm/mterp/mips/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..31fa360
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopLit8.S" {"instr":"xor a0, a0, a1"}
diff --git a/vm/mterp/mips/OP_XOR_LONG.S b/vm/mterp/mips/OP_XOR_LONG.S
new file mode 100644
index 0000000..1f07c84
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"}
diff --git a/vm/mterp/mips/OP_XOR_LONG_2ADDR.S b/vm/mterp/mips/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..dade7a9
--- /dev/null
+++ b/vm/mterp/mips/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "mips/binopWide2addr.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"}
diff --git a/vm/mterp/mips/alt_stub.S b/vm/mterp/mips/alt_stub.S
new file mode 100644
index 0000000..edf71a7
--- /dev/null
+++ b/vm/mterp/mips/alt_stub.S
@@ -0,0 +1,20 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (${opnum} * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
diff --git a/vm/mterp/mips/bincmp.S b/vm/mterp/mips/bincmp.S
new file mode 100644
index 0000000..e2398d0
--- /dev/null
+++ b/vm/mterp/mips/bincmp.S
@@ -0,0 +1,35 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    b${revcmp} a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/mips/binflop.S b/vm/mterp/mips/binflop.S
new file mode 100644
index 0000000..6b02707
--- /dev/null
+++ b/vm/mterp/mips/binflop.S
@@ -0,0 +1,44 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if $chkzero
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    $instr_f                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
diff --git a/vm/mterp/mips/binflop2addr.S b/vm/mterp/mips/binflop2addr.S
new file mode 100644
index 0000000..c20a1c6
--- /dev/null
+++ b/vm/mterp/mips/binflop2addr.S
@@ -0,0 +1,45 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if $chkzero
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    $instr_f
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binflopWide.S b/vm/mterp/mips/binflopWide.S
new file mode 100644
index 0000000..ad61680
--- /dev/null
+++ b/vm/mterp/mips/binflopWide.S
@@ -0,0 +1,52 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if $chkzero
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if $chkzero
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
diff --git a/vm/mterp/mips/binflopWide2addr.S b/vm/mterp/mips/binflopWide2addr.S
new file mode 100644
index 0000000..aacd482
--- /dev/null
+++ b/vm/mterp/mips/binflopWide2addr.S
@@ -0,0 +1,46 @@
+%default {"preinstr":"", "chkzero":"0"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if $chkzero
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if $chkzero
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    $instr_f
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
diff --git a/vm/mterp/mips/binop.S b/vm/mterp/mips/binop.S
new file mode 100644
index 0000000..8bbe0fb
--- /dev/null
+++ b/vm/mterp/mips/binop.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 11-14 instructions */
+
diff --git a/vm/mterp/mips/binop2addr.S b/vm/mterp/mips/binop2addr.S
new file mode 100644
index 0000000..acca20d
--- /dev/null
+++ b/vm/mterp/mips/binop2addr.S
@@ -0,0 +1,30 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binopLit16.S b/vm/mterp/mips/binopLit16.S
new file mode 100644
index 0000000..74b4533
--- /dev/null
+++ b/vm/mterp/mips/binopLit16.S
@@ -0,0 +1,30 @@
+%default {"result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if $chkzero
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-13 instructions */
+
diff --git a/vm/mterp/mips/binopLit8.S b/vm/mterp/mips/binopLit8.S
new file mode 100644
index 0000000..c3d7464
--- /dev/null
+++ b/vm/mterp/mips/binopLit8.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"a0", "chkzero":"0"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if $chkzero
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  $result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result, rOBJ, t0)       #  vAA <- $result
+    /* 10-12 instructions */
+
diff --git a/vm/mterp/mips/binopWide.S b/vm/mterp/mips/binopWide.S
new file mode 100644
index 0000000..3e47ab9
--- /dev/null
+++ b/vm/mterp/mips/binopWide.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"}
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64($arg0, $arg1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64($arg2, $arg3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if $chkzero
+    or        t0, $arg2, $arg3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA/vAA+1 <- $result0/$result1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
diff --git a/vm/mterp/mips/binopWide2addr.S b/vm/mterp/mips/binopWide2addr.S
new file mode 100644
index 0000000..7494604
--- /dev/null
+++ b/vm/mterp/mips/binopWide2addr.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"}
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64($arg2, $arg3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64($arg0, $arg1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if $chkzero
+    or        t0, $arg2, $arg3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA/vAA+1 <- $result0/$result1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
diff --git a/vm/mterp/mips/debug.cpp b/vm/mterp/mips/debug.cpp
new file mode 100644
index 0000000..0de6b67
--- /dev/null
+++ b/vm/mterp/mips/debug.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose MIPS registers, along with some other info.
+ *
+ */
+void dvmMterpDumpMipsRegs(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3)
+{
+    register uint32_t rPC       asm("s0");
+    register uint32_t rFP       asm("s1");
+    register uint32_t rSELF     asm("s2");
+    register uint32_t rIBASE    asm("s3");
+    register uint32_t rINST     asm("s4");
+    register uint32_t rOBJ      asm("s5");
+    register uint32_t rBIX      asm("s6");
+    register uint32_t rTEMP	asm("s7");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: a0=%08x a1=%08x a2=%08x a3=%08x\n", a0, a1, a2, a3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rIBASE=%08x\n",
+        rPC, rFP, rSELF, rIBASE);
+    printf("    : rINST=%08x rOBJ=%08x rBIX=%08x rTEMP=%08x \n", rINST, rOBJ, rBIX, rTEMP);
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->signature);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
diff --git a/vm/mterp/mips/entry.S b/vm/mterp/mips/entry.S
new file mode 100644
index 0000000..8a1b61a
--- /dev/null
+++ b/vm/mterp/mips/entry.S
@@ -0,0 +1,107 @@
+
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align 2
+    .global dvmMterpStdRun
+    .ent dvmMterpStdRun
+    .frame sp, STACK_SIZE, ra
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+
+dvmMterpStdRun:
+    .set noreorder
+    .cpload t9
+    .set reorder
+/* Save to the stack. Frame size = STACK_SIZE */
+    STACK_STORE_FULL()
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+    .cprestore STACK_OFFSET_GP
+
+    addu      fp, sp, STACK_SIZE           #  Move Frame Pointer to the base of frame
+    /* save stack pointer, add magic word for debuggerd */
+    sw        sp, offThread_bailPtr(a0)      # Save SP
+
+    /* set up "named" registers, figure out entry point */
+    move      rSELF, a0                    #  set rSELF
+    LOAD_PC_FROM_SELF()
+    LOAD_FP_FROM_SELF()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()                           #  load rINST from rPC
+    sw        zero, offThread_inJitCodeCache(rSELF)
+#if !defined(WITH_SELF_VERIFICATION)
+    bnez      a0, common_updateProfile     # profiling is enabled
+#else
+    lw       a2, offThread_shadowSpace(rSELF) # to find out the jit exit state
+    beqz     a0, 1f                        # profiling is disabled
+    lw       a3, offShadowSpace_jitExitState(a2) # jit exit state
+    li	     t0, kSVSTraceSelect
+    bne      a3, t0, 2f
+    li       a2, kJitTSelectRequestHot     # ask for trace selection
+    b        common_selectTrace            # go build the trace
+2:
+    li       a4, kSVSNoProfile
+    beq      a3, a4, 1f                    # don't profile the next instruction?
+    b        common_updateProfile          # collect profiles
+#endif
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                           #  load rINST from rPC
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+.Lbad_arg:
+    la        a0, .LstrBadEntryPoint
+    #a1 holds value of entryPoint
+    JAL(printf)
+    JAL(dvmAbort)
+
+    .end dvmMterpStdRun
+
+    .global dvmMterpStdBail
+    .ent dvmMterpStdBail
+
+/* Restore the stack pointer and all the registers stored at sp from the save
+ * point established  on entry. Return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ *   a0    Thread* self
+ */
+dvmMterpStdBail:
+    lw        sp, offThread_bailPtr(a0)      #  Restore sp
+    STACK_LOAD_FULL()
+    jr        ra
+
+    .end dvmMterpStdBail
diff --git a/vm/mterp/mips/footer.S b/vm/mterp/mips/footer.S
new file mode 100644
index 0000000..54da6c3
--- /dev/null
+++ b/vm/mterp/mips/footer.S
@@ -0,0 +1,1173 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      rBIX, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    b       jitSVShadowRunStart                 # resume as if cache hit
+                                                # expects resume addr in rBIX
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    li        a2, kSVSPunt                 #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    move      rPC, a0                      # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a2, kSVSSingleStep           #  a2 <- interpreter entry point
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoProfile            #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSBackwardBranch       #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSNormal               #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoChain              #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+#else                                   /*  WITH_SELF_VERIFICATION */
+
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      a0, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    jr      a0                                  # resume translation
+
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0
+#if defined(WITH_JIT_TUNING)
+    move      a0, ra
+    JAL(dvmBumpPunt)
+#endif
+    EXPORT_PC()
+    sw        zero, offThread_inJitCodeCache(rSELF) # Back to the interp land
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    rPC <= Dalvik PC of instrucion to interpret
+ *    a1 <= Dalvik PC of resume instruction
+ *    ra <= resume point in translation
+ */
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0                       # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        sp, offThread_jitResumeNSP(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a1, 1
+    sw        a1, offThread_singleStepCount(rSELF) # just step once
+    move      a0, rSELF
+    li        a1, kSubModeCountedStep
+    JAL(dvmEnableSubMode)                   # (self, subMode)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    move      a1, rPC                      # arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 2f                       # 0 means translation does not exist
+    jr        a0
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # @ (pc, self)
+    sw        v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    beqz      v0, 2f
+    move      a0, v0
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/* No translation, so request one if profiling isn't disabled */
+2:
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()
+    li        t0, kJitTSelectRequestHot
+    movn      a2, t0, a0                   #  ask for trace selection
+    bnez      a0, common_selectTrace
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNormal)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)           # @ (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    beqz      a0, toInterpreter            #  go if not, otherwise do chain
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, footer235
+
+    jr        a0                           #  continue native execution if so
+footer235:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 1f
+    jr        a0                           #  continue native execution if so
+1:
+#endif                                  /*  WITH_SELF_VERIFICATION */
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+
+toInterpreter:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    lw        a0, offThread_pJitProfTable(rSELF)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    # NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+
+common_testUpdateProfile:
+
+    beqz      a0, 4f
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    srl       a3, rPC, 12                  #  cheap, but fast hash function
+    xor       a3, a3, rPC
+    andi      a3, a3, JIT_PROF_SIZE-1      #  eliminate excess bits
+    addu      t1, a0, a3
+    lbu       a1, (t1)                     #  get counter
+    GET_INST_OPCODE(t0)
+    subu      a1, a1, 1                    #  decrement counter
+    sb        a1, (t1)                     #  and store it
+    beqz      a1, 1f
+    GOTO_OPCODE(t0)                        #  if not threshold, fallthrough otherwise
+1:
+    /* Looks good, reset the counter */
+    lw        a1, offThread_jitThreshold(rSELF)
+    sb        a1, (t1)
+    EXPORT_PC()
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        v0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+
+#if !defined(WITH_SELF_VERIFICATION)
+    li        t0, kJitTSelectRequest       #  ask for trace selection
+    movz      a2, t0, a0
+    beqz      a0, common_selectTrace
+    jr        a0                           #  jump to the translation
+#else
+
+    bne       a0, zero, skip_ask_for_trace_selection
+    li        a2, kJitTSelectRequest       #  ask for trace selection
+    j         common_selectTrace
+
+skip_ask_for_trace_selection:
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    move      rBIX, a0                     #  save target
+    jal       dvmCompilerGetInterpretTemplate
+    # special case?
+    bne       v0, rBIX, jitSVShadowRunStart  #  set up self verification shadow space
+    # Need to clear the inJitCodeCache flag
+    sw        zero, offThread_inJitCodeCache(rSELF) #  back to the interp land
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+
+common_selectTrace:
+    lhu        a0, offThread_subMode(rSELF)
+    andi       a0, (kSubModeJitTraceBuild | kSubModeJitSV)
+    bnez       a0, 3f                      # already doing JIT work, continue
+    sw         a2, offThread_jitState(rSELF)
+    move       a0, rSELF
+
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+
+    EXPORT_PC()
+    SAVE_PC_TO_SELF()
+    SAVE_FP_TO_SELF()
+    JAL(dvmJitCheckTraceRequest)
+3:
+    FETCH_INST()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+4:
+    GET_INST_OPCODE(t0)                    # extract opcode from rINST
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    move      a0, rPC                      #  r0 <- program counter
+    move      a1, rFP                      #  r1 <- frame pointer
+    move      a2, rSELF                    #  r2 <- InterpState pointer
+    move      a3, rBIX                     #  r3 <- target translation
+    jal       dvmSelfVerificationSaveState #  save registers to shadow space
+    lw        rFP, offShadowSpace_shadowFP(v0) #  rFP <- fp in shadow space
+    jr        rBIX                         #  jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    move      a1, rFP                      #  pass ending fp
+    move      a3, rSELF                    #  pass self ptr for convenience
+    jal       dvmSelfVerificationRestoreState #  restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()                 #  restore pc, fp
+    lw        a1, offShadowSpace_svState(a0) #  get self verification state
+    beq       a1, zero, 1f                 #  check for punt condition
+
+    # Setup SV single-stepping
+    move      a0, rSELF
+    li        a1, kSubModeJitSV
+    JAL(dvmEnableSubMode)                  # (self, subMode)
+    li        a2, kJitSelfVerification     #  ask for self verification
+    sw        a2, offThread_jitState(rSELF)
+    # Intentional fallthrough
+
+1:
+    # exit to interpreter without check
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+    .ent common_gotoBail
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                   # export state to "thread"
+    move      a0, rSELF                    # a0 <- self ptr
+    b         dvmMterpStdBail              # call(self, changeInterp)
+    .end common_gotoBail
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    beqz    rOBJ, 1f
+    lw      rOBJ, offObject_clazz(rOBJ)
+1:
+    sw      a0, offThread_methodToCall(rSELF)
+    sw      rOBJ, offThread_callsiteClass(rSELF)
+    jr      ra
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPA(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)              #  rBIX <- stack save area
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)                           #  a1 <- CCCC
+.LinvokeRangeArgs:
+    # a0=methodToCall, a1=CCCC, a2=count, rBIX=outs
+    # (very few methods have > 10 args; could unroll for common cases)
+    EAS2(a3, rFP, a1)
+    sll       t0, a2, 2
+    subu      rBIX, rBIX, t0
+
+1:
+    lw        a1, 0(a3)
+    addu      a3, a3, 4
+    subu      a2, a2, 1
+    sw        a1, 0(rBIX)
+    addu      rBIX, 4
+    bnez      a2, 1b
+    b         .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", "rOBJ is this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPB(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)
+
+    # a0=methodToCall, a1=GFED, a2=count,
+.LinvokeNonRange:
+    beq       a2, 0, 0f
+    beq       a2, 1, 1f
+    beq       a2, 2, 2f
+    beq       a2, 3, 3f
+    beq       a2, 4, 4f
+    beq       a2, 5, 5f
+
+5:
+    and       t0, rINST, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+4:
+    and       t0, a1, 0xf000
+    ESRN(t2, rFP, t0, 10)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+3:
+    and       t0, a1, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+2:
+    and       t0, a1, 0x00f0
+    ESRN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+1:
+    and       t0, a1, 0x000f
+    EASN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+0:
+    #fall through .LinvokeArgsDone
+
+
+.LinvokeArgsDone:                          #  a0=methodToCall
+    lhu       rOBJ, offMethod_registersSize(a0)
+    lhu       a3, offMethod_outsSize(a0)
+    lw        a2, offMethod_insns(a0)
+    lw        rINST, offMethod_clazz(a0)
+    # find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(a1, rFP)              # a1 <- stack save area
+    sll       t0, rOBJ, 2                    #  a1 <- newFp (old savearea - regsSize)
+    subu      a1, a1, t0
+    SAVEAREA_FROM_FP(rBIX, a1)
+    lw        rOBJ, offThread_interpStackEnd(rSELF) #  t3 <- interpStackEnd
+    sll       t2, a3, 2
+    subu      t0, rBIX, t2
+    lhu       ra, offThread_subMode(rSELF)
+    lw        a3, offMethod_accessFlags(a0) #  a3 <- methodToCall->accessFlags
+    bltu      t0, rOBJ, .LstackOverflow      #  yes, this frame will overflow stack
+
+
+    # set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(t0, rFP)
+    sw        t0, offStackSaveArea_prevSave(rBIX)
+#endif
+    sw        rFP, (offStackSaveArea_prevFrame)(rBIX)
+    sw        rPC, (offStackSaveArea_savedPc)(rBIX)
+#if defined(WITH_JIT)
+    sw        zero, (offStackSaveArea_returnAddr)(rBIX)
+#endif
+    sw        a0, (offStackSaveArea_method)(rBIX)
+    # Profiling?
+    bnez       ra, 2f
+1:
+    and       t2, a3, ACC_NATIVE
+    bnez      t2, .LinvokeNative
+    lhu       rOBJ, (a2)           # rOBJ -< load Inst from New PC
+    lw        a3, offClassObject_pDvmDex(rINST)
+    move      rPC, a2              # Publish new rPC
+    # Update state values for the new method
+    # a0=methodToCall, a1=newFp, a3=newMethodClass, rOBJ=newINST
+    sw        a0, offThread_method(rSELF)
+    sw        a3, offThread_methodClassDex(rSELF)
+    li        a2, 1
+    sw        a2, offThread_debugIsMethodEntry(rSELF)
+
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    move      rFP, a1                    # fp = newFp
+    GET_PREFETCHED_OPCODE(t0, rOBJ)      # extract prefetched opcode from rOBJ
+    move      rINST, rOBJ                # publish new rINST
+    sw        a1, offThread_curFrame(rSELF)
+    bnez      a0, common_updateProfile
+    GOTO_OPCODE(t0)
+#else
+    move      rFP, a1
+    GET_PREFETCHED_OPCODE(t0, rOBJ)
+    move      rINST, rOBJ
+    sw        a1, offThread_curFrame(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+2:
+    # Profiling - record method entry.  a0: methodToCall
+    STACK_STORE(a0, 0)
+    STACK_STORE(a1, 4)
+    STACK_STORE(a2, 8)
+    STACK_STORE(a3, 12)
+    sw       rPC, offThread_pc(rSELF)          # update interpSave.pc
+    move     a1, a0
+    move     a0, rSELF
+    JAL(dvmReportInvoke)
+    STACK_LOAD(a3, 12)                         # restore a0-a3
+    STACK_LOAD(a2, 8)
+    STACK_LOAD(a1, 4)
+    STACK_LOAD(a0, 0)
+    b        1b
+.LinvokeNative:
+    # Prep for the native call
+    # a0=methodToCall, a1=newFp, rBIX=newSaveArea
+    lhu       ra, offThread_subMode(rSELF)
+    lw        t3, offThread_jniLocal_topCookie(rSELF)
+    sw        a1, offThread_curFrame(rSELF)
+    sw        t3, offStackSaveArea_localRefCookie(rBIX) # newFp->localRefCookie=top
+    move      a2, a0
+    move      a0, a1
+    addu      a1, rSELF, offThread_retval
+    move      a3, rSELF
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b         .Lskip
+    .ent dalvik_mterp
+dalvik_mterp:
+    STACK_STORE_FULL()
+.Lskip:
+#endif
+    bnez      ra, 11f                          # Any special SubModes active?
+    lw        t9, offMethod_nativeFunc(a2)
+    jalr      t9
+    lw        gp, STACK_OFFSET_GP(sp)
+7:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw        a0, offStackSaveArea_localRefCookie(rBIX)
+    lw        a1, offThread_exception(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    sw        a0, offThread_jniLocal_topCookie(rSELF)    # new top <- old top
+    bnez      a1, common_exceptionThrown
+
+    FETCH_ADVANCE_INST(3)
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+11:
+    # a0=newFp, a1=&retval, a2=methodToCall, a3=self, ra=subModes
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    move      a0, a2                    # a0 <- methodToCall
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPreNativeInvoke)       # (methodToCall, self, fp)
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Call the native method
+    lw       t9, offMethod_nativeFunc(a2)      # t9<-methodToCall->nativeFunc
+    jalr     t9
+    lw       gp, STACK_OFFSET_GP(sp)
+
+    # Restore the pre-call arguments
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Finish up any post-invoke subMode requirements
+    move      a0, a2
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPostNativeInvoke)      # (methodToCall, self, fp)
+    b         7b
+
+
+.LstackOverflow:       # a0=methodToCall
+    move      a1, a0                    #  a1 <- methodToCall
+    move      a0, rSELF                 # a0 <- self
+    JAL(dvmHandleStackOverflow)         #  dvmHandleStackOverflow(self, methodToCall)
+    b         common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .end dalvik_mterp
+#endif
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    lhu       t0, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(a0, rFP)
+    lw        rOBJ, offStackSaveArea_savedPc(a0) # rOBJ = saveArea->savedPc
+    bnez      t0, 19f
+14:
+    lw        rFP, offStackSaveArea_prevFrame(a0) # fp = saveArea->prevFrame
+    lw        a2, (offStackSaveArea_method - sizeofStackSaveArea)(rFP)
+                                               # a2<- method we're returning to
+    # is this a break frame?
+    beqz      a2, common_gotoBail              # break frame, bail out completely
+
+    lw        rBIX, offMethod_clazz(a2)        # rBIX<- method->clazz
+    lw        rIBASE, offThread_curHandlerTable(rSELF) # refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, rOBJ, 3)      # advance rOBJ, update new rINST
+    sw        a2, offThread_method(rSELF)      # self->method = newSave->method
+    lw        a1, offClassObject_pDvmDex(rBIX) # r1<- method->clazz->pDvmDex
+    sw        rFP, offThread_curFrame(rSELF)   # curFrame = fp
+#if defined(WITH_JIT)
+    lw         rBIX, offStackSaveArea_returnAddr(a0)
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    sw         rBIX, offThread_inJitCodeCache(rSELF) # may return to JIT'ed land
+    beqz       rBIX, 15f                       # caller is compiled code
+    move       t9, rBIX
+    jalr       t9
+    lw         gp, STACK_OFFSET_GP(sp)
+15:
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    GOTO_OPCODE(t0)                            # jump to next instruction
+#else
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+19:
+    # Handle special actions
+    # On entry, a0: StackSaveArea
+    lw         a1, offStackSaveArea_prevFrame(a0) # a1<- prevFP
+    sw         rPC, offThread_pc(rSELF)        # update interpSave.pc
+    sw         a1, offThread_curFrame(rSELF)   # update interpSave.curFrame
+    move       a0, rSELF
+    JAL(dvmReportReturn)
+    SAVEAREA_FROM_FP(a0, rFP)                  # restore StackSaveArea
+    b          14b
+
+    .if 0
+    /*
+     * Return handling, calls through "glue code".
+     */
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                       # export state
+    move       a0, rSELF                       # arg to function
+    JAL(dvmMterp_returnFromMethod)
+    b          common_resumeAfterGlueCall
+    .endif
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+    .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+    move     a0, rSELF
+    JAL(dvmCheckSuspendPending)
+    lw       rOBJ, offThread_exception(rSELF)
+    move     a1, rSELF
+    move     a0, rOBJ
+    JAL(dvmAddTrackedAlloc)
+    lhu      a2, offThread_subMode(rSELF)
+    sw       zero, offThread_exception(rSELF)
+
+    # Special subMode?
+    bnez     a2, 7f                     # any special subMode handling needed?
+8:
+    /* set up args and a local for "&fp" */
+    sw       rFP, 20(sp)                 #  store rFP => tmp
+    addu     t0, sp, 20                  #  compute &tmp
+    sw       t0, STACK_OFFSET_ARG04(sp)  #  save it in arg4 as per ABI
+    li       a3, 0                       #  a3 <- false
+    lw       a1, offThread_method(rSELF)
+    move     a0, rSELF
+    lw       a1, offMethod_insns(a1)
+    move     a2, rOBJ
+    subu     a1, rPC, a1
+    sra      a1, a1, 1
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    JAL(dvmFindCatchBlock)           # call(self, relPc, exc, scan?, &fp)
+    lw        rFP, 20(sp)            # retrieve the updated rFP
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    move      a0, v0
+    bltz      v0, .LnotCaughtLocally
+
+    /* fix earlier stack overflow if necessary; Preserve a0 */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 1f
+    move      rBIX, a0
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)
+    move      a0, rBIX
+
+1:
+
+/* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(a1, rFP)           # a1<- new save area
+    lw        a1, offStackSaveArea_method(a1)
+    sw        a1, offThread_method(rSELF)
+    lw        a2, offMethod_clazz(a1)
+    lw        a3, offMethod_insns(a1)
+    lw        a2, offClassObject_pDvmDex(a2)
+    EAS1(rPC, a3, a0)
+    sw        a2, offThread_methodClassDex(rSELF)
+
+    /* release the tracked alloc on the exception */
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+
+    /* restore the exception if the handler wants it */
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    bne       t0, OP_MOVE_EXCEPTION, 2f
+    sw        rOBJ, offThread_exception(rSELF)
+2:
+    GOTO_OPCODE(t0)
+
+    # Manage debugger bookkeeping
+7:
+    sw        rPC, offThread_pc(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmReportExceptionThrow)
+    b         8b
+
+.LnotCaughtLocally:                     #  rOBJ = exception
+    /* fix stack overflow if necessary */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 3f
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)           #  dvmCleanupStackOverflow(self, exception)
+
+3:
+    # may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    lw        a0, offThread_method(rSELF)
+    lw        a1, offMethod_insns(a0)
+    subu      a1, rPC, a1
+    sra       a1, a1, 1
+    JAL(dvmLineNumFromPC)
+    sw        v0, 20(sp)
+    # dvmGetMethodSourceFile(method)
+    lw        a0, offThread_method(rSELF)
+    JAL(dvmGetMethodSourceFile)
+    sw        v0, 16(sp)
+    # exception->clazz->descriptor
+    lw        a3, offObject_clazz(rOBJ)
+    lw        a3, offClassObject_descriptor(a3)
+    la        a2, .LstrExceptionNotCaughtLocally
+    la        a1, .LstrLogTag
+    li        a0, 3
+    JAL(__android_log_print)
+#endif
+    sw        rOBJ, offThread_exception(rSELF)
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+    b         common_gotoBail
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_TO_SELF()                # export state
+    SAVE_FP_TO_SELF()
+    move     a0, rSELF               # arg to function
+    JAL(dvmMterp_exceptionThrown)
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     rBIX: &dvmDex->pResFields[field]
+     *     a0:  field pointer (must preserve)
+     */
+common_verifyField:
+     lhu     a3, offThread_subMode(rSELF)
+     andi    a3, kSubModeJitTraceBuild
+     bnez    a3, 1f                 # Not building trace, continue
+     jr      ra
+1:
+     lw      a1, (rBIX)
+     beqz    a1, 2f                 # resolution complete ?
+     jr      ra
+2:
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(ra, 16)
+    move    a0, rSELF
+    move    a1, rPC
+    JAL(dvmJitEndTraceSelect)        #(self,pc) end trace before this inst)
+    SCRATCH_LOAD(a0, 0)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(ra, 16)
+    jr      ra                       # return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()           #  pull rPC and rFP out of thread
+    lw      rIBASE, offThread_curHandlerTable(rSELF) # refresh
+    FETCH_INST()                     #  load rINST from rPC
+    GET_INST_OPCODE(t0)              #  extract opcode from rINST
+    GOTO_OPCODE(t0)                  #  jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use a1
+ * and a3 because those just happen to be the registers all our callers are
+ * using. We move a3 before calling the C function, but a1 happens to match.
+ * a1: index
+ * a3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    move      a0, a3
+    JAL(dvmThrowArrayIndexOutOfBoundsException)
+    b         common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    la     a0, .LstrDivideByZero
+    JAL(dvmThrowArithmeticException)
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in a1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    move    a0, a1                                # arg0 <- len
+    JAL(dvmThrowNegativeArraySizeException)    # (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in a1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    move     a0, a1
+    JAL(dvmThrowNoSuchMethodError)
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    li      a0, 0
+    JAL(dvmThrowNullPointerException)
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will be in ra. Use a jal to jump here.
+ */
+common_abort:
+    lw      zero,-4(zero)            #  generate SIGSEGV
+
+/*
+ * Spit out a "we were here", preserving all registers.
+ */
+    .macro SQUEAK num
+common_squeak\num:
+    STACK_STORE_RA();
+    la        a0, .LstrSqueak
+    LOAD_IMM(a1, \num);
+    JAL(printf);
+    STACK_LOAD_RA();
+    RETURN;
+    .endm
+
+    SQUEAK 0
+    SQUEAK 1
+    SQUEAK 2
+    SQUEAK 3
+    SQUEAK 4
+    SQUEAK 5
+
+/*
+ * Spit out the number in a0, preserving registers.
+ */
+common_printNum:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrSqueak
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    STACK_STORE_RA()
+    la        a0, .LstrNewline
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+    /*
+     * Print the 32-bit quantity in a0 as a hex value, preserving registers.
+     */
+common_printHex:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrPrintHex
+    JAL(printf)
+    STACK_LOAD_RA()
+RETURN;
+
+/*
+ * Print the 64-bit quantity in a0-a1, preserving registers.
+ */
+common_printLong:
+    STACK_STORE_RA()
+    MOVE_REG(a3, a1)
+    MOVE_REG(a2, a0)
+    la        a0, .LstrPrintLong
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN;
+
+/*
+ * Print full method info.  Pass the Method* in a0.  Preserves regs.
+ */
+common_printMethod:
+    STACK_STORE_RA()
+    JAL(dvmMterpPrintMethod)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if 0
+common_dumpRegs:
+    STACK_STORE_RA()
+    JAL(dvmMterpDumpMipsRegs)
+    STACK_LOAD_RA()
+    RETURN
+    .endif
+
+/*
+ * Zero-terminated ASCII string data.
+ */
+    .data
+
+.LstrBadEntryPoint:
+    .asciiz "Bad entry point %d\n"
+.LstrDivideByZero:
+    .asciiz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciiz "filled-new-array only implemented for 'int'"
+.LstrLogTag:
+    .asciiz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciiz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciiz "\n"
+.LstrSqueak:
+    .asciiz "<%d>"
+.LstrPrintHex:
+    .asciiz "<0x%x>"
+.LstrPrintLong:
+    .asciiz "<%lld>"
diff --git a/vm/mterp/mips/header.S b/vm/mterp/mips/header.S
new file mode 100644
index 0000000..38e922a
--- /dev/null
+++ b/vm/mterp/mips/header.S
@@ -0,0 +1,343 @@
+#include "../common/asm-constants.h"
+#include "../common/mips-defines.h"
+
+#ifdef __mips_hard_float
+#define HARD_FLOAT
+#else
+#define SOFT_FLOAT
+#endif
+
+#if (__mips==32) && (__mips_isa_rev>=2)
+#define MIPS32R2
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		self (Thread) pointer
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+
+/* single-purpose registers, given names for clarity */
+#define rPC s0
+#define rFP s1
+#define rSELF s2
+#define rIBASE s3
+#define rINST s4
+#define rOBJ s5
+#define rBIX s6
+#define rTEMP s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0 a0
+#define rARG1 a1
+#define rARG2 a2
+#define rARG3 a3
+#define rRESULT0 v0
+#define rRESULT1 v1
+#else
+#define rARG0 a1
+#define rARG1 a0
+#define rARG2 a3
+#define rARG3 a2
+#define rRESULT0 v1
+#define rRESULT1 v0
+#endif
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_SELF() lw rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF() sw rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF() lw rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF() sw rFP, offThread_curFrame(rSELF)
+#define LOAD_PC_FP_FROM_SELF() \
+	LOAD_PC_FROM_SELF();   \
+	LOAD_FP_FROM_SELF()
+#define SAVE_PC_FP_TO_SELF()   \
+	SAVE_PC_TO_SELF();     \
+	SAVE_FP_TO_SELF()
+
+#define EXPORT_PC() \
+    sw        rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+    subu      rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST() lhu rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \
+    addu      rPC, rPC, ((_count) * 2)
+
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+    lhu       _dreg, ((_count)*2)(_sreg) ;            \
+    addu      _sreg, _sreg, (_count)*2
+
+#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \
+    lhu       rINST, (rPC)
+
+#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC)
+#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+
+#define GET_PREFETCHED_OPCODE(dreg, sreg)   andi     dreg, sreg, 255
+
+#define GOTO_OPCODE(rd) sll rd, rd, ${handler_size_bits}; \
+    addu      rd, rIBASE, rd; \
+    jr        rd
+
+#define GOTO_OPCODE_BASE(_base, rd)  sll rd, rd, ${handler_size_bits}; \
+    addu      rd, _base, rd; \
+    jr        rd
+
+#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix)
+
+#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix) STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \
+    sll       dst, dst, ${handler_size_bits}; \
+    addu      dst, rIBASE, dst; \
+    sll       t8, rix, 2; \
+    addu      t8, t8, rFP; \
+    jr        dst; \
+    sw        rd, 0(t8); \
+    .set reorder
+
+#define SET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; s.s rd, (AT); .set at
+
+
+#define GET_OPA(rd) srl rd, rINST, 8
+#ifndef MIPS32R2
+#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf
+#else
+#define GET_OPA4(rd) ext rd, rINST, 8, 4
+#endif
+#define GET_OPB(rd) srl rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd, off) lw rd, offThread_##off## (rSELF)
+
+#define LOAD_rSELF_method(rd) LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd) LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd) LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd) LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd) LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd) LOAD_rSELF_OFF(rd, bailPtr)
+#define LOAD_rSELF_SelfSuspendCount(rd) LOAD_rSELF_OFF(rd, SelfSuspendCount)
+
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd, rbase, roff, rshift) .set noat; \
+    sll       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define EAS1(rd, rbase, roff) EASN(rd, rbase, roff, 1)
+#define EAS2(rd, rbase, roff) EASN(rd, rbase, roff, 2)
+#define EAS3(rd, rbase, roff) EASN(rd, rbase, roff, 3)
+#define EAS4(rd, rbase, roff) EASN(rd, rbase, roff, 4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd, rbase, roff, rshift) .set noat; \
+    srl       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; lw rd, 0(AT); .set at
+
+#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; sw rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase)
+#define LOADu2_RB_OFF(rd, rbase, off) lhu rd, off(rbase)
+#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \
+    s.s       rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \
+    l.s       rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, (off+4)(rbase); \
+    s.s       rhi, (off)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, (off+4)(rbase); \
+    l.s       rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0)
+#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0)
+
+#define vSTORE64(rlo, rhi, rbase) vSTORE64_off(rlo, rhi, rbase, 0)
+#define vLOAD64(rlo, rhi, rbase) vLOAD64_off(rlo, rhi, rbase, 0)
+
+#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0)
+#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0)
+
+#define STORE64_lo(rd, rbase) sw rd, 0(rbase)
+#define STORE64_hi(rd, rbase) sw rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd, rbase) LOAD_RB_OFF(rd, rbase, offThread_exception)
+#define LOAD_base_offArrayObject_length(rd, rbase) LOAD_RB_OFF(rd, rbase, offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd, rbase) LOAD_RB_OFF(rd, rbase, offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd, rbase) LOAD_RB_OFF(rd, rbase, offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_name)
+#define LOAD_base_offObject_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd, rbase) LOADu2_RB_OFF(rd, rbase, offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd, rbase) STORE_RB_OFF(rd, rbase, offThread_exception)
+
+
+#define STACK_STORE(rd, off) sw rd, off(sp)
+#define STACK_LOAD(rd, off) lw rd, off(sp)
+#define CREATE_STACK(n) subu sp, sp, n
+#define DELETE_STACK(n) addu sp, sp, n
+
+#define SAVE_RA(offset) STACK_STORE(ra, offset)
+#define LOAD_RA(offset) STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest, addr) la dest, addr
+#define LOAD_IMM(dest, imm) li dest, imm
+#define MOVE_REG(dest, src) move dest, src
+#define RETURN jr ra
+#define STACK_SIZE 128
+
+#define STACK_OFFSET_ARG04 16
+#define STACK_OFFSET_ARG05 20
+#define STACK_OFFSET_ARG06 24
+#define STACK_OFFSET_ARG07 28
+#define STACK_OFFSET_SCR   32
+#define STACK_OFFSET_SCRMX 80
+#define STACK_OFFSET_GP    84
+#define STACK_OFFSET_rFP   112
+
+#define JAL(n) jal n
+#define BAL(n) bal n
+
+#define STACK_STORE_RA() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(gp, STACK_OFFSET_GP); \
+    STACK_STORE(ra, 124)
+
+#define STACK_STORE_S0() STACK_STORE_RA(); \
+    STACK_STORE(s0, 116)
+
+#define STACK_STORE_S0S1() STACK_STORE_S0(); \
+    STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define STACK_LOAD_RA() STACK_LOAD(ra, 124); \
+    STACK_LOAD(gp, STACK_OFFSET_GP); \
+    DELETE_STACK(STACK_SIZE)
+
+#define STACK_LOAD_S0() STACK_LOAD(s0, 116); \
+    STACK_LOAD_RA()
+
+#define STACK_LOAD_S0S1() STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD_S0()
+
+#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(ra, 124); \
+    STACK_STORE(fp, 120); \
+    STACK_STORE(s0, 116); \
+    STACK_STORE(s1, STACK_OFFSET_rFP); \
+    STACK_STORE(s2, 108); \
+    STACK_STORE(s3, 104); \
+    STACK_STORE(s4, 100); \
+    STACK_STORE(s5, 96); \
+    STACK_STORE(s6, 92); \
+    STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \
+    STACK_LOAD(s7, 88); \
+    STACK_LOAD(s6, 92); \
+    STACK_LOAD(s5, 96); \
+    STACK_LOAD(s4, 100); \
+    STACK_LOAD(s3, 104); \
+    STACK_LOAD(s2, 108); \
+    STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD(s0, 116); \
+    STACK_LOAD(fp, 120); \
+    STACK_LOAD(ra, 124); \
+    DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/mips/platform.S b/vm/mterp/mips/platform.S
new file mode 100644
index 0000000..ec1e3ee
--- /dev/null
+++ b/vm/mterp/mips/platform.S
@@ -0,0 +1,32 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    // FIXME: Is this really needed?
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/mips/stub.S b/vm/mterp/mips/stub.S
new file mode 100644
index 0000000..fad2238
--- /dev/null
+++ b/vm/mterp/mips/stub.S
@@ -0,0 +1,10 @@
+    /* (stub) */
+    SAVE_PC_TO_SELF()            # only need to export PC and FP
+    SAVE_FP_TO_SELF()
+    move        a0, rSELF        # self is first arg to function
+    JAL(dvmMterp_${opcode})      # call
+    LOAD_PC_FROM_SELF()          # retrieve updated values
+    LOAD_FP_FROM_SELF()
+    FETCH_INST()                 # load next instruction from rPC
+    GET_INST_OPCODE(t0)          # ...trim down to just the opcode
+    GOTO_OPCODE(t0)              # ...and jump to the handler
diff --git a/vm/mterp/mips/unflop.S b/vm/mterp/mips/unflop.S
new file mode 100644
index 0000000..9018bc9
--- /dev/null
+++ b/vm/mterp/mips/unflop.S
@@ -0,0 +1,32 @@
+%default {"preinstr":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    $preinstr                              #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    $instr                                 #  a0 <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    $instr_f
+
+.L${opcode}_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
diff --git a/vm/mterp/mips/unflopWide.S b/vm/mterp/mips/unflopWide.S
new file mode 100644
index 0000000..3411c2e
--- /dev/null
+++ b/vm/mterp/mips/unflopWide.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "ld_arg":"LOAD64_F(fa0, fa0f, a3)", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    $ld_arg
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0/a1 <- op, a2-a3 changed
+
+.L${opcode}_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    $st_result                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
diff --git a/vm/mterp/mips/unflopWider.S b/vm/mterp/mips/unflopWider.S
new file mode 100644
index 0000000..f6d5718
--- /dev/null
+++ b/vm/mterp/mips/unflopWider.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "st_result":"STORE64_F(fv0, fv0f, rOBJ)"}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  result <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    $instr_f
+
+.L${opcode}_set_vreg:
+    $st_result                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unop.S b/vm/mterp/mips/unop.S
new file mode 100644
index 0000000..52a8f0a
--- /dev/null
+++ b/vm/mterp/mips/unop.S
@@ -0,0 +1,19 @@
+%default {"preinstr":"", "result0":"a0"}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO($result0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
diff --git a/vm/mterp/mips/unopNarrower.S b/vm/mterp/mips/unopNarrower.S
new file mode 100644
index 0000000..85a94b7
--- /dev/null
+++ b/vm/mterp/mips/unopNarrower.S
@@ -0,0 +1,37 @@
+%default {"preinstr":"", "load":"LOAD64_F(fa0, fa0f, a3)"}
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    $load
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+#ifdef SOFT_FLOAT
+    $instr                                 #  a0 <- op, a0-a3 changed
+
+.L${opcode}_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    $instr_f
+
+.L${opcode}_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unopWide.S b/vm/mterp/mips/unopWide.S
new file mode 100644
index 0000000..00e4e17
--- /dev/null
+++ b/vm/mterp/mips/unopWide.S
@@ -0,0 +1,22 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1"}
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
diff --git a/vm/mterp/mips/unopWider.S b/vm/mterp/mips/unopWider.S
new file mode 100644
index 0000000..f601c11
--- /dev/null
+++ b/vm/mterp/mips/unopWider.S
@@ -0,0 +1,20 @@
+%default {"preinstr":"", "result0":"a0", "result1":"a1"}
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    $preinstr                              #  optional op
+    $instr                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64($result0, $result1, rOBJ)      #  vA/vA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
diff --git a/vm/mterp/mips/unused.S b/vm/mterp/mips/unused.S
new file mode 100644
index 0000000..d91dafb
--- /dev/null
+++ b/vm/mterp/mips/unused.S
@@ -0,0 +1,2 @@
+    BAL(common_abort)
+
diff --git a/vm/mterp/mips/zcmp.S b/vm/mterp/mips/zcmp.S
new file mode 100644
index 0000000..aaac52d
--- /dev/null
+++ b/vm/mterp/mips/zcmp.S
@@ -0,0 +1,33 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    b${revcmp} a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
diff --git a/vm/mterp/out/InterpAsm-allstubs.S b/vm/mterp/out/InterpAsm-allstubs.S
new file mode 100644
index 0000000..779fd5f
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-allstubs.S
@@ -0,0 +1,34 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+    .balign 64
+.L_OP_NOP:   /* dummy */
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
new file mode 100644
index 0000000..a173c72
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -0,0 +1,16877 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #24                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #24                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, lsr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    faddd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuld   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    faddd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fsubd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fmuld   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fdivd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     0
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     0
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
new file mode 100644
index 0000000..7b6c9d1
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -0,0 +1,17335 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB_ST
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r2, r2, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    and     r2, r2, #15                 @ r2<- A
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPL_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/OP_CMPG_FLOAT.S */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * The straightforward implementation requires 3 calls to functions
+     * that return a result in r0.  We can do it with two calls if our
+     * EABI library supports __aeabi_cfcmple (only one if we want to check
+     * for NaN directly):
+     *   check x <= y
+     *     if <, return -1
+     *     if ==, return 0
+     *   check y <= x
+     *     if <, return 1
+     *   return {-1,1}
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ copy to arg registers
+    mov     r1, r10
+    bl      __aeabi_cfcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPG_FLOAT_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPL_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_DOUBLE_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/OP_CMPG_DOUBLE.S */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r9, r0, #255                @ r9<- BB
+    mov     r10, r0, lsr #8             @ r10<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[BB]
+    add     r10, rFP, r10, lsl #2       @ r10<- &fp[CC]
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r10, {r2-r3}                @ r2/r3<- vCC/vCC+1
+    bl      __aeabi_cdcmple             @ cmp <=: C clear if <, Z set if eq
+    bhi     .LOP_CMPG_DOUBLE_gt_or_nan       @ C set and Z clear, disambiguate
+    mvncc   r1, #0                      @ (less than) r1<- -1
+    moveq   r1, #0                      @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_DOUBLE_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0,#0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/OP_INT_TO_FLOAT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_i2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/OP_INT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_i2d                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/OP_FLOAT_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_f2iz                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl      f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x4f000000             @ (float)maxint
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (7fffffff)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xcf000000             @ (float)minint
+    bl      __aeabi_fcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    ldmeqfd sp!, {r4, pc}               @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2iz                @ convert float to int
+    ldmfd   sp!, {r4, pc}
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/OP_FLOAT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      __aeabi_f2d                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/OP_DOUBLE_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_d2iz                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl      d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r2, #0x80000000             @ maxint, as a double (low word)
+    mov     r2, r2, asr #9              @  0xffc00000
+    sub     sp, sp, #4                  @ align for EABI
+    mvn     r3, #0xbe000000             @ maxint, as a double (high word)
+    sub     r3, r3, #0x00200000         @  0x41dfffff
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxint?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0x80000000             @ return maxint (0x7fffffff)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc1000000             @ minint, as a double (high word)
+    add     r3, r3, #0x00e00000         @  0xc1e00000
+    mov     r2, #0                      @ minint, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minint?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0x80000000             @ return minint (80000000)
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    beq     1f                          @ return zero for NaN
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2iz                @ convert double to int
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/OP_DOUBLE_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    and     r9, r9, #15
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_d2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #24                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #24                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, lsr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB
+    and     r9, r9, #15
+    mov     r0, r0, asl #16                           @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r0, r0, asr #16                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/OP_ADD_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fadd                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/OP_SUB_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fsub                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/OP_MUL_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fmul                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/OP_DIV_FLOAT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_fdiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/OP_ADD_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dadd                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/OP_SUB_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dsub                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/OP_MUL_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dmul                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/OP_DIV_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ddiv                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/OP_ADD_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fadd                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/OP_SUB_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fsub                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/OP_MUL_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fmul                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/OP_DIV_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_fdiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r3, rINST, lsr #12          @ r3<- B
+    and     r9, r9, #15
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/OP_ADD_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dadd                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/OP_SUB_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dsub                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/OP_MUL_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_dmul                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/OP_DIV_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ddiv                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r9, r9, #15
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 1
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r2)                    @ r0<- vB
+    and     r9, r9, #15
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl     __aeabi_idiv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 1
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_idivmod                              @ r1<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)               @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    and     r2, r2, #15
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r0, rINST, lsr #8           @ r0<- A(+)
+    mov     r1, rINST, lsr #12          @ r1<- B
+    and     r0, r0, #15
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPL_FLOAT_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPL_FLOAT_finish
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPL_FLOAT_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     OP_CMPL_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       OP_CMPL_FLOAT_continue
+@%break
+
+OP_CMPL_FLOAT_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     OP_CMPL_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     OP_CMPL_FLOAT_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+OP_CMPL_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+OP_CMPL_FLOAT_nan:
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       OP_CMPL_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPG_FLOAT */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPG_FLOAT_gt_or_nan:
+    mov     r1, r9                      @ reverse order
+    mov     r0, r10
+    bl      __aeabi_cfcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPG_FLOAT_finish
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPG_FLOAT_finish
+
+
+#if 0       /* "clasic" form */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r9, r2)                    @ r9<- vBB
+    GET_VREG(r10, r3)                   @ r10<- vCC
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpeq              @ r0<- (vBB == vCC)
+    cmp     r0, #0                      @ equal?
+    movne   r1, #0                      @ yes, result is 0
+    bne     OP_CMPG_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmplt              @ r0<- (vBB < vCC)
+    cmp     r0, #0                      @ less than?
+    b       OP_CMPG_FLOAT_continue
+@%break
+
+OP_CMPG_FLOAT_continue:
+    mvnne   r1, #0                      @ yes, result is -1
+    bne     OP_CMPG_FLOAT_finish
+    mov     r0, r9                      @ r0<- vBB
+    mov     r1, r10                     @ r1<- vCC
+    bl      __aeabi_fcmpgt              @ r0<- (vBB > vCC)
+    cmp     r0, #0                      @ greater than?
+    beq     OP_CMPG_FLOAT_nan               @ no, must be NaN
+    mov     r1, #1                      @ yes, result is 1
+    @ fall through to _finish
+
+OP_CMPG_FLOAT_finish:
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r3)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * This is expected to be uncommon, so we double-branch (once to here,
+     * again back to _finish).
+     */
+OP_CMPG_FLOAT_nan:
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       OP_CMPG_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPL_DOUBLE */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPL_DOUBLE_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPL_DOUBLE_finish
+    mvn     r1, #0                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+    @ Test for NaN with a second comparison.  EABI forbids testing bit
+    @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+    @ make the library call.
+.LOP_CMPG_DOUBLE_gt_or_nan:
+    ldmia   r10, {r0-r1}                @ reverse order
+    ldmia   r9, {r2-r3}
+    bl      __aeabi_cdcmple             @ r0<- Z set if eq, C clear if <
+    @bleq    common_abort
+    movcc   r1, #1                      @ (greater than) r1<- 1
+    bcc     .LOP_CMPG_DOUBLE_finish
+    mov     r1, #1                            @ r1<- 1 or -1 for NaN
+    b       .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     0
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     0
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a-neon.S b/vm/mterp/out/InterpAsm-armv7-a-neon.S
new file mode 100644
index 0000000..d830053
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S
@@ -0,0 +1,16786 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb ish
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb ishst
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: arm-vfp/OP_LONG_TO_DOUBLE.S */
+    /*
+     * Specialised 64-bit floating point operation.
+     *
+     * Note: The result will be returned in d2.
+     *
+     * For: long-to-double
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    vldr    d0, [r3]                    @ d0<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    vcvt.f64.s32    d1, s1              @ d1<- (double)(vAAh)
+    vcvt.f64.u32    d2, s0              @ d2<- (double)(vAAl)
+    vldr            d3, constvalOP_LONG_TO_DOUBLE
+    vmla.f64        d2, d1, d3          @ d2<- vAAh*2^32 + vAAl
+
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    vstr.64 d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* literal pool helper */
+constvalOP_LONG_TO_DOUBLE:
+    .8byte          0x41f0000000000000
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxtb    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    uxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv7-a/OP_DIV_INT.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl    __aeabi_idiv                  @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv7-a/OP_REM_INT.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls  r1, r1, r2, r0                 @ r1<- op, r0-r2 changed
+#else
+    bl   __aeabi_idivmod                @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    faddd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuld   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv6t2/OP_ADD_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv6t2/OP_SUB_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv6t2/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv7-a/OP_DIV_INT_2ADDR.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv7-a/OP_REM_INT_2ADDR.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl      __aeabi_idivmod             @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv6t2/OP_AND_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv6t2/OP_OR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv6t2/OP_XOR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv6t2/OP_SHL_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv6t2/OP_SHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv6t2/OP_USHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv6t2/OP_ADD_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv6t2/OP_SUB_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv6t2/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv6t2/OP_DIV_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv6t2/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv6t2/OP_AND_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv6t2/OP_OR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv6t2/OP_XOR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv6t2/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv6t2/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv6t2/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv6t2/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    faddd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fsubd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fmuld   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fdivd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv6t2/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv6t2/OP_ADD_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv6t2/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv6t2/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv7-a/OP_DIV_INT_LIT16.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv7-a/OP_REM_INT_LIT16.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl     __aeabi_idivmod              @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv6t2/OP_AND_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv6t2/OP_OR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv6t2/OP_XOR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv7-a/OP_DIV_INT_LIT8.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl   __aeabi_idiv                   @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv7-a/OP_REM_INT_LIT8.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl       __aeabi_idivmod            @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
new file mode 100644
index 0000000..65c4a70
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -0,0 +1,16782 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.  If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending".  Only the arguments that don't fit in the first 4
+registers are placed on the stack.  "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+  reg nick      purpose
+  r4  rPC       interpreted program counter, used for fetching instructions
+  r5  rFP       interpreted frame pointer, used for accessing locals and args
+  r6  rSELF     self (Thread) pointer
+  r7  rINST     first 16-bit code unit of current instruction
+  r8  rIBASE    interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations.  Each macro MUST emit only
+one instruction to make instruction-counting easier.  They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC     r4
+#define rFP     r5
+#define rSELF   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the thread struct */
+#define LOAD_PC_FROM_SELF()     ldr     rPC, [rSELF, #offThread_pc]
+#define SAVE_PC_TO_SELF()       str     rPC, [rSELF, #offThread_pc]
+#define LOAD_FP_FROM_SELF()     ldr     rFP, [rSELF, #offThread_curFrame]
+#define SAVE_FP_TO_SELF()       str     rFP, [rSELF, #offThread_curFrame]
+#define LOAD_PC_FP_FROM_SELF()  ldmia   rSELF, {rPC, rFP}
+#define SAVE_PC_FP_TO_SELF()    stmia   rSELF, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+    str     rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    sub     _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            ldrh    rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset.  Advances rPC
+ * to point to the next instruction.  "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss.  (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh    rINST, [rPC, #((_count)*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+        ldrh    _dreg, [_sreg, #((_count)*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg.  Updates
+ * rPC to point to the next instruction.  "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #1]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh    rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC.  The
+ * "_count" value is in 16-bit code units.  Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count)     ldrh    _reg, [rPC, #((_count)*2)]
+#define FETCH_S(_reg, _count)   ldrsh   _reg, [rPC, #((_count)*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC.  Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb     _reg, [rPC, #((_count)*2+(_byte))]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg)   and     _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg)   and     _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg.  Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg)       add     pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_BASE(_base,_reg)  add     pc, _base, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg)  addeq   pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg)  addne   pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   ldr     _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg)   str     _reg, [rFP, _vreg, lsl #2]
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+        add     _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb ish
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    dmb ishst
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack.  From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame.  If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align  2
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+    .save {r4-r10,fp,lr}; \
+    stmfd   sp!, {r4-r10,fp,lr}         @ save 9 regs
+#define MTERP_ENTRY2 \
+    .pad    #4; \
+    sub     sp, sp, #4                  @ align 64
+
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+
+    /* save stack pointer, add magic word for debuggerd */
+    str     sp, [r0, #offThread_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rSELF, r0                   @ set rSELF
+    LOAD_PC_FP_FROM_SELF()              @ load rPC and rFP from "thread"
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable] @ set rIBASE
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    cmp     r0,#0                       @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+    bne     common_updateProfile        @ profiling is enabled
+#else
+    ldr     r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
+    beq     1f                          @ profiling is disabled
+    ldr     r3, [r2, #offShadowSpace_jitExitState]  @ jit exit state
+    cmp     r3, #kSVSTraceSelect        @ hot trace following?
+    moveq   r2,#kJitTSelectRequestHot   @ ask for trace selection
+    beq     common_selectTrace          @ go build the trace
+    cmp     r3, #kSVSNoProfile          @ don't profile the next instruction?
+    beq     1f                          @ intrepret the next instruction
+    b       common_updateProfile        @ collect profiles
+#endif
+1:
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+0:  add     r0, pc
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+    .size   dvmMterpStdRun, .-dvmMterpStdRun
+
+strBadEntryPoint:
+    .word   PCREL_REF(.LstrBadEntryPoint,0b)
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR.  Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ *  r0  Thread* self
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offThread_bailPtr]    @ sp<- saved SP
+    add     sp, sp, #4                      @ un-align 64
+    ldmfd   sp!, {r4-r10,fp,pc}             @ restore 9 regs and return
+
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type   dalvik_inst, %function
+dalvik_inst:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+    .fnend
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    ubfx    r0, rINST, #8, #4           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[B]
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 1)                        @ r3<- BBBB
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    FETCH(r3, 2)                        @ r3<- BBBB
+    FETCH(r2, 1)                        @ r2<- AAAA
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BBBB]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AAAA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[BBBB]
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(r1, 2)                        @ r1<- BBBB
+    FETCH(r0, 1)                        @ r0<- AAAA
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[BBBB]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[AAAA]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- retval.j
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    stmia   r2, {r0-r1}                 @ fp[AA]<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r0, [rSELF, #offThread_retval]    @ r0<- self->retval.i
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[AA]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [rSELF, #offThread_exception]  @ r3<- dvmGetException bypass
+    mov     r1, #0                      @ r1<- 0
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    SET_VREG(r3, r2)                    @ fp[AA]<- exception obj
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r1, [rSELF, #offThread_exception]  @ dvmClearException bypass
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[AA]
+    add     r3, rSELF, #offThread_retval  @ r3<- &self->retval
+    ldmia   r2, {r0-r1}                 @ r0/r1 <- vAA/vAA+1
+    stmia   r3, {r0-r1}                 @ retval<- r0/r1
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r0, r2)                    @ r0<- vAA
+    str     r0, [rSELF, #offThread_retval] @ retval.i <- vAA
+    b       common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    mov     r1, rINST, lsl #16          @ r1<- Bxxx0000
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r1, asr #28             @ r1<- sssssssB (sign-extended)
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r1, r0)                    @ fp[A]<- r1
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    FETCH(r0, 1)                        @ r0<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, r0, lsl #16             @ r0<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    FETCH_S(r0, 1)                      @ r0<- ssssBBBB (sign-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- 0000bbbb (low)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH_S(r2, 2)                      @ r2<- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    orr     r0, r0, r2, lsl #16         @ r0<- BBBBbbbb
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    mov     r1, r0, asr #31             @ r1<- ssssssss
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (low middle)
+    FETCH(r2, 3)                        @ r2<- hhhh (high middle)
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb (low word)
+    FETCH(r3, 4)                        @ r3<- HHHH (high)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    orr     r1, r2, r3, lsl #16         @ r1<- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)               @ advance rPC, load rINST
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    FETCH(r1, 1)                        @ r1<- 0000BBBB (zero-extended)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    mov     r0, #0                      @ r0<- 00000000
+    mov     r1, r1, lsl #16             @ r1<- BBBB0000
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+    /* const/string vAA, String@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_STRING_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+    /* const/string vAA, String@BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (low)
+    FETCH(r1, 2)                        @ r1<- BBBB (high)
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResStrings]   @ r2<- dvmDex->pResStrings
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResStrings[BBBB]
+    cmp     r0, #0
+    beq     .LOP_CONST_STRING_JUMBO_resolve
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+    /* const/class vAA, Class@BBBB */
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [rSELF, #offThread_methodClassDex]  @ r2<- self->methodClassDex
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r2, #offDvmDex_pResClasses]   @ r2<- dvmDex->pResClasses
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- pResClasses[BBBB]
+    cmp     r0, #0                      @ not yet resolved?
+    beq     .LOP_CONST_CLASS_resolve
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    mov     r0, rSELF                   @ r0<- self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    EXPORT_PC()                         @ before fetch: export the PC
+    GET_VREG(r1, r2)                    @ r1<- vAA (object)
+    cmp     r1, #0                      @ null object?
+    beq     1f                          @ yes
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmUnlockObject             @ r0<- success for unlock(self, obj)
+    cmp     r0, #0                      @ failed?
+    FETCH_ADVANCE_INST(1)               @ before throw: advance rPC, load rINST
+    beq     common_exceptionThrown      @ yes, exception is pending
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)               @ advance before throw
+    b      common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    FETCH(r2, 1)                        @ r2<- BBBB
+    GET_VREG(r9, r3)                    @ r9<- object
+    ldr     r0, [rSELF, #offThread_methodClassDex]    @ r0<- pDvmDex
+    cmp     r9, #0                      @ is object null?
+    ldr     r0, [r0, #offDvmDex_pResClasses]    @ r0<- pDvmDex->pResClasses
+    beq     .LOP_CHECK_CAST_okay            @ null obj, cast always succeeds
+    ldr     r1, [r0, r2, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_CHECK_CAST_resolve         @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    cmp     r0, r1                      @ same class (trivial success)?
+    bne     .LOP_CHECK_CAST_fullcheck       @ no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    and     r9, r9, #15                 @ r9<- A
+    cmp     r0, #0                      @ is object null?
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- pDvmDex
+    beq     .LOP_INSTANCE_OF_store           @ null obj, not an instance, store r0
+    FETCH(r3, 1)                        @ r3<- CCCC
+    ldr     r2, [r2, #offDvmDex_pResClasses]    @ r2<- pDvmDex->pResClasses
+    ldr     r1, [r2, r3, lsl #2]        @ r1<- resolved class
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    cmp     r1, #0                      @ have we resolved this before?
+    beq     .LOP_INSTANCE_OF_resolve         @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+    cmp     r0, r1                      @ same class (trivial success)?
+    beq     .LOP_INSTANCE_OF_trivial         @ yes, trivial finish
+    b       .LOP_INSTANCE_OF_fullcheck       @ no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    GET_VREG(r0, r1)                    @ r0<- vB (object ref)
+    cmp     r0, #0                      @ is object null?
+    beq     common_errNullObject        @ yup, fail
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- array length
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r3, r2)                    @ vB<- length
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_class
+#endif
+    EXPORT_PC()                         @ req'd for init, resolve, alloc
+    cmp     r0, #0                      @ already resolved?
+    beq     .LOP_NEW_INSTANCE_resolve         @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved:   @ r0=class
+    ldrb    r1, [r0, #offClassObject_status]    @ r1<- ClassStatus enum
+    cmp     r1, #CLASS_INITIALIZED      @ has class been initialized?
+    bne     .LOP_NEW_INSTANCE_needinit        @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+    mov     r1, #ALLOC_DONT_TRACK       @ flags for alloc call
+    bl      dvmAllocObject              @ r0<- new object
+    b       .LOP_NEW_INSTANCE_finish          @ continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    FETCH(r2, 1)                        @ r2<- CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    GET_VREG(r1, r0)                    @ r1<- vB (array length)
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    cmp     r1, #0                      @ check length
+    ldr     r0, [r3, r2, lsl #2]        @ r0<- resolved class
+    bmi     common_errNegativeArraySize @ negative length, bail - len in r1
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ req'd for resolve, alloc
+    bne     .LOP_NEW_ARRAY_finish          @ resolved, continue
+    b       .LOP_NEW_ARRAY_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResClasses]    @ r3<- pDvmDex->pResClasses
+    EXPORT_PC()                         @ need for resolve and alloc
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved class
+    mov     r10, rINST, lsr #8          @ r10<- AA or BA
+    cmp     r0, #0                      @ already resolved?
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_continue        @ yes, continue on
+8:  ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r1, r0, r1, lsl #16         @ r1<- BBBBbbbb
+    GET_VREG(r0, r3)                    @ r0<- vAA (array object)
+    add     r1, rPC, r1, lsl #1         @ r1<- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC();
+    bl      dvmInterpHandleFillArrayData@ fill the array with predefined data
+    cmp     r0, #0                      @ 0 means an exception is thrown
+    beq     common_exceptionThrown      @ has exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    GET_VREG(r1, r2)                    @ r1<- vAA (exception object)
+    EXPORT_PC()                         @ exception handler can throw
+    cmp     r1, #0                      @ null object?
+    beq     common_errNullObject        @ yes, throw an NPE instead
+    @ bypass dvmSetException, just store it
+    str     r1, [rSELF, #offThread_exception]  @ thread->exception<- obj
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    /* tuning: use sbfx for 6t2+ targets */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r1, r0, asr #24             @ r1<- ssssssAA (sign-extended)
+    add     r2, r1, r1                  @ r2<- byte offset, set flags
+       @ If backwards branch refresh rIBASE
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) check for trace hotness
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(r0, 1)                      @ r0<- ssssAAAA (sign-extended)
+    adds    r1, r0, r0                  @ r1<- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    bmi     common_testUpdateProfile    @ (r0) hot trace head?
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".  Because
+     * we need the V bit set, we'll use an adds to convert from Dalvik
+     * offset to byte offset.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    orr     r0, r0, r1, lsl #16         @ r0<- AAAAaaaa
+    adds    r1, r0, r0                  @ r1<- byte offset
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ble     common_testUpdateProfile    @ (r0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandlePackedSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(r0, 1)                        @ r0<- bbbb (lo)
+    FETCH(r1, 2)                        @ r1<- BBBB (hi)
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    orr     r0, r0, r1, lsl #16         @ r0<- BBBBbbbb
+    GET_VREG(r1, r3)                    @ r1<- vAA
+    add     r0, rPC, r0, lsl #1         @ r0<- PC + BBBBbbbb*2
+    bl      dvmInterpHandleSparseSwitch                       @ r0<- code-unit branch offset
+    adds    r1, r0, r0                  @ r1<- byte offset; clear V
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrle   rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh handler base
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    flds    s0, [r2]                    @ s0<- vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    fcmpes  s0, s1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_FLOAT_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else {
+     *         return -1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mvn     r0, #0                      @ r0<- -1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    movgt   r0, #1                      @ (greater than) r1<- 1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPL_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * int compare(x, y) {
+     *     if (x == y) {
+     *         return 0;
+     *     } else if (x < y) {
+     *         return -1;
+     *     } else if (x > y) {
+     *         return 1;
+     *     } else {
+     *         return 1;
+     *     }
+     * }
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fcmped  d0, d1                      @ compare (vBB, vCC)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    mov     r0, #1                      @ r0<- 1 (default)
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fmstat                              @ export status flags
+    mvnmi   r0, #0                      @ (less than) r1<- -1
+    moveq   r0, #0                      @ (equal) r1<- 0
+    b       .LOP_CMPG_DOUBLE_finish          @ argh
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     *
+     * We load the full values with LDM, but in practice many values could
+     * be resolved by only looking at the high word.  This could be made
+     * faster or slower by splitting the LDM into a pair of LDRs.
+     *
+     * If we just wanted to set condition flags, we could do this:
+     *  subs    ip, r0, r2
+     *  sbcs    ip, r1, r3
+     *  subeqs  ip, r0, r2
+     * Leaving { <0, 0, >0 } in ip.  However, we have to set it to a specific
+     * integer value, which we can do with 2 conditional mov/mvn instructions
+     * (set 1, set -1; if they're equal we already have 0 in ip), giving
+     * us a constant 5-cycle path plus a branch at the end to the
+     * instruction epilogue code.  The multi-compare approach below needs
+     * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+     * in the worst case (the 64-bit values are equal).
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    cmp     r1, r3                      @ compare (vBB+1, vCC+1)
+    blt     .LOP_CMP_LONG_less            @ signed compare on high part
+    bgt     .LOP_CMP_LONG_greater
+    subs    r1, r0, r2                  @ r1<- r0 - r2
+    bhi     .LOP_CMP_LONG_greater         @ unsigned compare on low part
+    bne     .LOP_CMP_LONG_less
+    b       .LOP_CMP_LONG_finish          @ equal; r1 already holds 0
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movne r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    moveq r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movge r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movlt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movle r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r3, r1)                    @ r3<- vB
+    GET_VREG(r2, r0)                    @ r2<- vA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, r3                      @ compare (vA, vB)
+    movgt r1, #2                 @ r1<- BYTE branch dist for not-taken
+    adds    r2, r1, r1                  @ convert to bytes, check sign
+    FETCH_ADVANCE_INST_RB(r2)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    cmp     r0, #0
+    bne     common_updateProfile
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movne r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    moveq r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movge r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movlt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movle r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    GET_VREG(r2, r0)                    @ r2<- vAA
+    FETCH_S(r1, 1)                      @ r1<- branch offset, in code units
+    cmp     r2, #0                      @ compare (vA, 0)
+    movgt r1, #2                 @ r1<- inst branch dist for not-taken
+    adds    r1, r1, r1                  @ convert to bytes & set flags
+    FETCH_ADVANCE_INST_RB(r1)           @ update rPC, load rINST
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+    cmp     r0,#0
+    bne     common_updateProfile        @ test for JIT off at target
+#else
+    ldrmi   rIBASE, [rSELF, #offThread_curHandlerTable]   @ refresh table base
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_AGET_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsb   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrsh   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r2, r9)                    @ vAA<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #2     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #3          @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    bcc     .LOP_APUT_WIDE_finish          @ okay, continue below
+    b       common_errArrayIndex        @ index >= length, bail
+    @ May want to swap the order of these two branches depending on how the
+    @ branch prediction (if any) handles conditional forward branches vs.
+    @ unconditional forward branches.
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    GET_VREG(rINST, r2)                 @ rINST<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     rINST, #0                   @ null array object?
+    GET_VREG(r9, r9)                    @ r9<- vAA
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [rINST, #offArrayObject_length]   @ r3<- arrayObj->length
+    add     r10, rINST, r1, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcc     .LOP_APUT_OBJECT_finish          @ we're okay, continue on
+    b       common_errArrayIndex        @ index >= length, bail
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #0     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strb  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(r2, 1, 0)                   @ r2<- BB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    FETCH_B(r3, 1, 1)                   @ r3<- CC
+    GET_VREG(r0, r2)                    @ r0<- vBB (array object)
+    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)
+    cmp     r0, #0                      @ null array object?
+    beq     common_errNullObject        @ yes, bail
+    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length
+    add     r0, r0, r1, lsl #1     @ r0<- arrayObj + index*width
+    cmp     r1, r3                      @ compare unsigned index, length
+    bcs     common_errArrayIndex        @ index >= length, bail
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r2, r9)                    @ r2<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strh  r2, [r0, #offArrayObject_contents]  @ vBB[vCC]<- r2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_finish
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BOOLEAN_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_BYTE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_CHAR_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_SHORT_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BOOLEAN_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_BYTE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_BYTE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_CHAR_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_CHAR_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_SHORT_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_SHORT_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_resolve         @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 0
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_BYTE_resolve         @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_CHAR_resolve         @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_SHORT_resolve         @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_resolve         @ yes, do resolve
+.LOP_SPUT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 0
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                         @ releasing store
+    b       .LOP_SPUT_OBJECT_end
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BOOLEAN_resolve         @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_BYTE_resolve         @ yes, do resolve
+.LOP_SPUT_BYTE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_CHAR_resolve         @ yes, do resolve
+.LOP_SPUT_CHAR_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_SHORT_resolve         @ yes, do resolve
+.LOP_SPUT_SHORT_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    @ no-op 
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_resolve         @ do resolve now
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ yes, continue on
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_VIRTUAL_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r9, #0                      @ null "this"?
+    ldr     r10, [rSELF, #offThread_method] @ r10<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r10, [r10, #offMethod_clazz]  @ r10<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ resolved, continue on
+    b       .LOP_INVOKE_SUPER_RANGE_resolve         @ do resolve now
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r9, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ r0=method, r9="this"
+    b       common_errNullObject        @ yes, throw exception
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    mov     r9, #0                      @ null "this" in delay slot
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+#if defined(WITH_JIT)
+    add     r10, r3, r1, lsl #2         @ r10<- &resolved_methodToCall
+#endif
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+    b       .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r2, 2)                        @ r2<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r2, r2, #15                 @ r2<- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                         @ must export for invoke
+    GET_VREG(r9, r2)                    @ r9<- first arg ("this")
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- methodClassDex
+    cmp     r9, #0                      @ null obj?
+    ldr     r2, [rSELF, #offThread_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- thisPtr->clazz
+    bl      dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yes, handle exception
+    b       common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsb     r0, r0, #0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    rsbs    r0, r0, #0                           @ optional op; may set condition codes
+    rsc     r1, r1, #0                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mvn     r0, r0                           @ optional op; may set condition codes
+    mvn     r1, r1                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r0, r0, #0x80000000                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r1, r1, #0x80000000                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    mov     r1, r0, asr #31                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitos  s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fsitod  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
+    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    GET_VREG(r2, r1)                    @ r2<- fp[B]
+    and     r0, r0, #15
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    SET_VREG(r2, r0)                    @ fp[A]<- r2
+    GOTO_OPCODE(ip)                     @ execute next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0/r1", where
+     * "result" is a 32-bit quantity in r0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     *
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vB/vB+1
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2f                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv6t2/OP_LONG_TO_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      __aeabi_l2d                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+    /*
+     * Generic 32-bit unary floating-point operation.  Provide an "instr"
+     * line that specifies an instruction that performs "s1 = op s0".
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizs s1, s0                              @ s1<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s1, [r9]                    @ vA<- s1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl      __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op r0", where
+     * "result" is a 64-bit quantity in r0/r1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      f2l_doconv                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vA/vA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+    /*
+     * Generic 32bit-to-64bit floating point unary operation.  Provide an
+     * "instr" line that specifies an instruction that performs "d0 = op s0".
+     *
+     * For: int-to-double, float-to-double
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    flds    s0, [r3]                    @ s0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtds  d0, s0                              @ d0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fstd    d0, [r9]                    @ vA<- d0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    ftosizd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl      __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0/r1".
+     * This could be an ARM instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vAA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      d2l_doconv                              @ r0/r1<- op, r2-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary floating point operation.  Provide an
+     * "instr" line that specifies an instruction that performs "s0 = op d0".
+     *
+     * For: double-to-int, double-to-float
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    fldd    d0, [r3]                    @ d0<- vB
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    and     r9, r9, #15                 @ r9<- A
+    fcvtsd  s0, d0                              @ s0<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    fsts    s0, [r9]                    @ vA<- s0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxtb    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    uxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op r0".
+     * This could be an ARM instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r3)                    @ r0<- vB
+                               @ optional op; may set condition codes
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    sxth    r0, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 8-9 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv7-a/OP_DIV_INT.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl    __aeabi_idiv                  @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv7-a/OP_REM_INT.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int
+     *
+     */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls  r1, r1, r2, r0                 @ r1<- op, r0-r2 changed
+#else
+    bl   __aeabi_idivmod                @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+     *        WX
+     *      x YZ
+     *  --------
+     *     ZW ZX
+     *  YW YX
+     *
+     * The low word of the result holds ZX, the high word holds
+     * (ZW+YX) + (the high overflow from ZX).  YW doesn't matter because
+     * it doesn't fit in the low 64 bits.
+     *
+     * Unlike most ARM math operations, multiply instructions have
+     * restrictions on using the same register more than once (Rd and Rm
+     * cannot be the same).
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    add     r0, rFP, r0, lsl #2         @ r0<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r3, r0, #255                @ r3<- BB
+    mov     r0, r0, lsr #8              @ r0<- CC
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[BB]
+    GET_VREG(r2, r0)                    @ r2<- vCC
+    ldmia   r3, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    and     r2, r2, #63                 @ r0<- r0 & 0x3f
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    b       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+    /*
+     * Generic 32-bit floating-point operation.  Provide an "instr" line that
+     * specifies an instruction that performs "s2 = s0 op s1".  Because we
+     * use the "softfp" ABI, this must be an instruction, not a function call.
+     *
+     * For: add-float, sub-float, mul-float, div-float
+     */
+    /* floatop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    flds    s1, [r3]                    @ s1<- vCC
+    flds    s0, [r2]                    @ s0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+     *      mul-float, div-float, rem-float
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    GET_VREG(r1, r3)                    @ r1<- vCC
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 11-14 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    faddd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fsubd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fmuld   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+    /*
+     * Generic 64-bit double-precision floating point binary operation.
+     * Provide an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * for: add-double, sub-double, mul-double, div-double
+     */
+    /* doubleop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    mov     r3, r0, lsr #8              @ r3<- CC
+    and     r2, r0, #255                @ r2<- BB
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vCC
+    VREG_INDEX_TO_ADDR(r2, r2)          @ r2<- &vBB
+    fldd    d1, [r3]                    @ d1<- vCC
+    fldd    d0, [r2]                    @ d0<- vBB
+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    fdivd   d2, d0, d1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vAA
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(r0, 1)                        @ r0<- CCBB
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r0, #255                @ r2<- BB
+    mov     r3, r0, lsr #8              @ r3<- CC
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    add     r2, rFP, r2, lsl #2         @ r2<- &fp[BB]
+    add     r3, rFP, r3, lsl #2         @ r3<- &fp[CC]
+    ldmia   r2, {r0-r1}                 @ r0/r1<- vBB/vBB+1
+    ldmia   r3, {r2-r3}                 @ r2/r3<- vCC/vCC+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 14-17 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv6t2/OP_ADD_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv6t2/OP_SUB_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    sub     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv6t2/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv7-a/OP_DIV_INT_2ADDR.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv7-a/OP_REM_INT_2ADDR.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/2addr
+     *
+     */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl      __aeabi_idivmod             @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv6t2/OP_AND_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv6t2/OP_OR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv6t2/OP_XOR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv6t2/OP_SHL_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv6t2/OP_SHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv6t2/OP_USHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv6t2/OP_ADD_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    adds    r0, r0, r2                           @ optional op; may set condition codes
+    adc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv6t2/OP_SUB_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    subs    r0, r0, r2                           @ optional op; may set condition codes
+    sbc     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv6t2/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, "/2addr" version.
+     *
+     * See OP_MUL_LONG for an explanation.
+     *
+     * We get a little tight on registers, so to avoid looking up &fp[A]
+     * again we stuff it into rINST.
+     */
+    /* mul-long/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     rINST, rFP, r9, lsl #2      @ rINST<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   rINST, {r0-r1}              @ r0/r1<- vAA/vAA+1
+    mul     ip, r2, r1                  @  ip<- ZxW
+    umull   r9, r10, r2, r0             @  r9/r10 <- ZxX
+    mla     r2, r0, r3, ip              @  r2<- YxX + (ZxW)
+    mov     r0, rINST                   @ r0<- &fp[A] (free up rINST)
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv6t2/OP_DIV_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv6t2/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 1
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      __aeabi_ldivmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2,r3}     @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv6t2/OP_AND_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    and     r0, r0, r2                           @ optional op; may set condition codes
+    and     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv6t2/OP_OR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    orr     r0, r0, r2                           @ optional op; may set condition codes
+    orr     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv6t2/OP_XOR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+    eor     r0, r0, r2                           @ optional op; may set condition codes
+    eor     r1, r1, r3                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv6t2/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r1, r1, asl r2              @  r1<- r1 << r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r1, r1, r0, lsr r3          @  r1<- r1 | (r0 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r1, r0, asl ip              @  if r2 >= 32, r1<- r0 << (r2-32)
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    b       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv6t2/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, asr ip              @  if r2 >= 32, r0<-r1 >> (r2-32)
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    b       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv6t2/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r2, r3)                    @ r2<- vB
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    and     r2, r2, #63                 @ r2<- r2 & 0x3f
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+
+    mov     r0, r0, lsr r2              @  r0<- r2 >> r2
+    rsb     r3, r2, #32                 @  r3<- 32 - r2
+    orr     r0, r0, r1, asl r3          @  r0<- r0 | (r1 << (32-r2))
+    subs    ip, r2, #32                 @  ip<- r2 - 32
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    movpl   r0, r1, lsr ip              @  if r2 >= 32, r0<-r1 >>> (r2-32)
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    b       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fadds   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fsubs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fmuls   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+    /*
+     * Generic 32-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "s2 = s0 op s1".
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    flds    s1, [r3]                    @ s1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    flds    s0, [r9]                    @ s0<- vA
+
+    fdivs   s2, s0, s1                              @ s2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fsts    s2, [r9]                    @ vAA<- s2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv6t2/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv6t2/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r1, r3)                    @ r1<- vB
+    GET_VREG(r0, r9)                    @ r0<- vA
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmodf                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    faddd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fsubd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fmuld   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+    /*
+     * Generic 64-bit floating point "/2addr" binary operation.  Provide
+     * an "instr" line that specifies an instruction that performs
+     * "d2 = d0 op d1".
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *      div-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r3, rINST, lsr #12          @ r3<- B
+    mov     r9, rINST, lsr #8           @ r9<- A+
+    VREG_INDEX_TO_ADDR(r3, r3)          @ r3<- &vB
+    and     r9, r9, #15                 @ r9<- A
+    fldd    d1, [r3]                    @ d1<- vB
+    VREG_INDEX_TO_ADDR(r9, r9)          @ r9<- &vA
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    fldd    d0, [r9]                    @ d0<- vA
+
+    fdivd   d2, d0, d1                              @ d2<- op
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    fstd    d2, [r9]                    @ vAA<- d2
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv6t2/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv6t2/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+     *      sub-double/2addr, mul-double/2addr, div-double/2addr,
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    add     r1, rFP, r1, lsl #2         @ r1<- &fp[B]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[A]
+    ldmia   r1, {r2-r3}                 @ r2/r3<- vBB/vBB+1
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    .if 0
+    orrs    ip, r2, r3                  @ second arg (r2-r3) is zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    bl      fmod                              @ result<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0,r1}     @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 12-15 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv6t2/OP_ADD_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv6t2/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv6t2/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv7-a/OP_DIV_INT_LIT16.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl       __aeabi_idiv               @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv7-a/OP_REM_INT_LIT16.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit16
+     *
+     */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl     __aeabi_idivmod              @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv6t2/OP_AND_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv6t2/OP_OR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv6t2/OP_XOR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    FETCH_S(r1, 1)                      @ r1<- ssssCCCC (sign-extended)
+    mov     r2, rINST, lsr #12          @ r2<- B
+    ubfx    r9, rINST, #8, #4           @ r9<- A
+    GET_VREG(r0, r2)                    @ r0<- vB
+    .if 0
+    cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-13 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    add     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    rsb     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    mul     r0, r1, r0                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv7-a/OP_DIV_INT_LIT8.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r0 = r0 div r1". The selection between sdiv or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * div-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r0, r0, r1                  @ r0<- op
+#else
+    bl   __aeabi_idiv                   @ r0<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv7-a/OP_REM_INT_LIT8.S */
+    /*
+     * Specialized 32-bit binary operation
+     *
+     * Performs "r1 = r0 rem r1". The selection between sdiv block or the gcc helper
+     * depends on the compile time value of __ARM_ARCH_EXT_IDIV__ (defined for
+     * ARMv7 CPUs that have hardware division support).
+     *
+     * NOTE: idivmod returns quotient in r0 and remainder in r1
+     *
+     * rem-int/lit8
+     *
+     */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    @cmp     r1, #0                     @ is second operand zero?
+    beq     common_errDivideByZero
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+#ifdef __ARM_ARCH_EXT_IDIV__
+    sdiv    r2, r0, r1
+    mls     r1, r1, r2, r0              @ r1<- op
+#else
+    bl       __aeabi_idivmod            @ r1<- op, r0-r3 changed
+#endif
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    and     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    orr     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+                               @ optional op; may set condition codes
+    eor     r0, r0, r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asl r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, asr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an ARM instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (r1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    FETCH_S(r3, 1)                      @ r3<- ssssCCBB (sign-extended for CC)
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    and     r2, r3, #255                @ r2<- BB
+    GET_VREG(r0, r2)                    @ r0<- vBB
+    movs    r1, r3, asr #8              @ r1<- ssssssCC (sign extended)
+    .if 0
+    @cmp     r1, #0                      @ is second operand zero?
+    beq     common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+
+    and     r1, r1, #31                           @ optional op; may set condition codes
+    mov     r0, r0, lsr r1                              @ r0<- op, r0-r3 changed
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)               @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+    /* 10-12 instructions */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    SMP_DMB
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_OBJECT_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    /* iget-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IGET_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0
+    bne     .LOP_IGET_WIDE_VOLATILE_finish
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method] @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_WIDE_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    .if 1
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+    .endif
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rSELF, #offThread_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r0, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r10, r1, lsl #2]        @ r2<- resolved StaticField ptr
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    cmp     r2, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_WIDE_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r0-r1}                 @ r0/r1<- vAA/vAA+1
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if 1
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.
+     */
+    mov     r0, rPC
+    bl      dvmGetOriginalOpcode        @ (rPC)
+    FETCH(rINST, 0)                     @ reload OP_BREAKPOINT + rest of inst
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    and     rINST, #0xff00
+    orr     rINST, rINST, r0
+    GOTO_OPCODE_BASE(r1, r0)
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    FETCH(r2, 1)                        @ r2<- BBBB
+    EXPORT_PC()                         @ export the PC
+    mov     r1, rINST, lsr #8           @ r1<- AA
+    bl      dvmThrowVerificationError   @ always throws
+    b       common_exceptionThrown      @ handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in r0-r3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    FETCH(r10, 1)                       @ r10<- BBBB
+    EXPORT_PC()                         @ can throw
+    ands    r2, #kSubModeDebugProfile   @ Any going on?
+    bne     .LOP_EXECUTE_INLINE_RANGE_debugmode       @ yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    add     r1, rSELF, #offThread_retval  @ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    add     sp, sp, #8                  @ pop stack
+    cmp     r0, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(r1, 2)                  @ r1<- CCCC
+    GET_VREG(r0, r1)                    @ r0<- "this" ptr
+    cmp     r0, #0                      @ check for NULL
+    beq     common_errNullObject        @ export PC and throw NPE
+    ldr     r1, [r0, #offObject_clazz]  @ r1<- obj->clazz
+    ldr     r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags
+    tst     r2, #CLASS_ISFINALIZABLE    @ is this class finalizable?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_setFinal        @ yes, go
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeDebuggerActive @ debugger active?
+    bne     .LOP_INVOKE_OBJECT_INIT_RANGE_debugger        @ Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB_ST
+    b       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+    /* iget-wide-quick vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(ip, 1)                        @ ip<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r3, ip]                @ r0<- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- object we're operating on
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    ldr     r0, [r3, r1]                @ r0<- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r3, #0                      @ check object for null
+    beq     common_errNullObject        @ object was null
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+    /* iput-wide-quick vA, vB, offset@CCCC */
+    mov     r1, rINST, lsr #12          @ r1<- B
+    ubfx    r0, rINST, #8, #4           @ r0<- A
+    GET_VREG(r2, r1)                    @ r2<- fp[B], the object pointer
+    add     r3, rFP, r0, lsl #2         @ r3<- &fp[A]
+    cmp     r2, #0                      @ check object for null
+    ldmia   r3, {r0-r1}                 @ r0/r1<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH(r3, 1)                        @ r3<- field byte offset
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    strd    r0, [r2, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    mov     r2, rINST, lsr #12          @ r2<- B
+    GET_VREG(r3, r2)                    @ r3<- fp[B], the object pointer
+    FETCH(r1, 1)                        @ r1<- field byte offset
+    cmp     r3, #0                      @ check object for null
+    mov     r2, rINST, lsr #8           @ r2<- A(+)
+    beq     common_errNullObject        @ object was null
+    and     r2, r2, #15
+    GET_VREG(r0, r2)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r0, [r3, r1]                @ obj.field (always 32 bits)<- r0
+    cmp     r0, #0
+    strneb  r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!0)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r3, 2)                        @ r3<- FEDC or CCCC
+    FETCH(r1, 1)                        @ r1<- BBBB
+    .if     (!1)
+    and     r3, r3, #15                 @ r3<- C (or stays CCCC)
+    .endif
+    GET_VREG(r9, r3)                    @ r9<- vC ("this" ptr)
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r9, #offObject_clazz]  @ r2<- thisPtr->clazz
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- thisPtr->clazz->vtable
+    EXPORT_PC()                         @ invoke must export
+    ldr     r0, [r2, r1, lsl #2]        @ r3<- vtable[BBBB]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!0)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(r10, 2)                       @ r10<- GFED or CCCC
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    .if     (!1)
+    and     r10, r10, #15               @ r10<- D (or stays CCCC)
+    .endif
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r2, [r2, #offMethod_clazz]  @ r2<- method->clazz
+    EXPORT_PC()                         @ must export for invoke
+    ldr     r2, [r2, #offClassObject_super]     @ r2<- method->clazz->super
+    GET_VREG(r9, r10)                   @ r9<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r9, #0                      @ null "this" ref?
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- super->vtable[BBBB]
+    beq     common_errNullObject        @ "this" is null, throw exception
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    /* op vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rSELF, #offThread_methodClassDex]    @ r3<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref CCCC
+    ldr     r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+    GET_VREG(r9, r0)                    @ r9<- fp[B], the object pointer
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ no, already resolved
+8:  ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveInstField         @ r0<- resolved InstField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_IPUT_OBJECT_VOLATILE_finish          @ yes, finish up
+    b       common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SGET_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+    ldr     r1, [r0, #offStaticField_value] @ r1<- field value
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r2)                    @ fp[AA]<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+    /*
+     * 32-bit SPUT handler for objects
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    ldr     r2, [rSELF, #offThread_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r10, [r2, #offDvmDex_pResFields] @ r10<- dvmDex->pResFields
+    ldr     r0, [r10, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    beq     .LOP_SPUT_OBJECT_VOLATILE_resolve         @ yes, do resolve
+.LOP_SPUT_OBJECT_VOLATILE_finish:   @ field ptr in r0
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_VREG(r1, r2)                    @ r1<- fp[AA]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB_ST                        @ releasing store
+    b       .LOP_SPUT_OBJECT_VOLATILE_end
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+    .balign 64
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  r1: BBBBBBBB (String ref)
+     *  r9: target register
+     */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveString            @ r0<- String reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  r1: BBBB (Class ref)
+     *  r9: target register
+     */
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC()
+    ldr     r0, [rSELF, #offThread_method] @ r0<- self->method
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- Class reference
+    cmp     r0, #0                      @ failed?
+    beq     common_exceptionThrown      @ yup, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds desired class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    mov     r10, r1                     @ avoid ClassObject getting clobbered
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    cmp     r0, #0                      @ failed?
+    bne     .LOP_CHECK_CAST_okay            @ no, success
+
+    @ A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC()                         @ about to throw
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz (actual class)
+    mov     r1, r10                     @ r1<- desired class
+    bl      dvmThrowClassCastException
+    b       common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r2 holds BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r1, r2                      @ r1<- BBBB
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_CHECK_CAST_resolved        @ pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  r0 holds obj->clazz
+     *  r1 holds class resolved from BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    bl      dvmInstanceofNonTrivial     @ r0<- boolean result
+    @ fall through to OP_INSTANCE_OF_store
+
+    /*
+     * r0 holds boolean result
+     * r9 holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    mov     r0, #1                      @ indicate success
+    @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r9)                    @ vA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r3 holds BBBB
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                         @ resolve() could throw
+    ldr     r0, [rSELF, #offThread_method]    @ r0<- self->method
+    mov     r1, r3                      @ r1<- BBBB
+    mov     r2, #1                      @ r2<- true
+    ldr     r0, [r0, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    beq     common_exceptionThrown      @ yes, handle exception
+    mov     r1, r0                      @ r1<- class resolved from BBB
+    mov     r3, rINST, lsr #12          @ r3<- B
+    GET_VREG(r0, r3)                    @ r0<- vB (object)
+    ldr     r0, [r0, #offObject_clazz]  @ r0<- obj->clazz
+    b       .LOP_INSTANCE_OF_resolved        @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+    .balign 32                          @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+    mov     r3, rINST, lsr #8           @ r3<- AA
+    cmp     r0, #0                      @ failed?
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    ldrh    r1, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown      @ yes, handle the exception
+    ands    r1, #kSubModeJitTraceBuild  @ under construction?
+    bne     .LOP_NEW_INSTANCE_jitCheck
+#else
+    beq     common_exceptionThrown      @ yes, handle the exception
+#endif
+.LOP_NEW_INSTANCE_end:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * r0: new object
+     * r3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    ldr     r1, [r10]                   @ reload resolved class
+    cmp     r1, #0                      @ okay?
+    bne     .LOP_NEW_INSTANCE_end             @ yes, finish
+    mov     r9, r0                      @ preserve new object
+    mov     r10, r3                     @ preserve vAA
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self, pc)
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r9, r10)                   @ vAA<- new object
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  r0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    mov     r9, r0                      @ save r0
+    bl      dvmInitClass                @ initialize class
+    cmp     r0, #0                      @ check boolean result
+    mov     r0, r9                      @ restore r0
+    bne     .LOP_NEW_INSTANCE_initialized     @ success, continue
+    b       common_exceptionThrown      @ failed, deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  r1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- resolved ClassObject ptr
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_NEW_INSTANCE_resolved        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  r1 holds array length
+     *  r2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    mov     r9, r1                      @ r9<- length (save)
+    mov     r1, r2                      @ r1<- CCCC
+    mov     r2, #0                      @ r2<- false
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveClass             @ r0<- call(clazz, ref)
+    cmp     r0, #0                      @ got null?
+    mov     r1, r9                      @ r1<- length (restore)
+    beq     common_exceptionThrown      @ yes, handle exception
+    @ fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation.
+     *
+     *  r0 holds class
+     *  r1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    mov     r2, #ALLOC_DONT_TRACK       @ don't track in local refs table
+    bl      dvmAllocArrayByClass        @ r0<- call(clazz, length, flags)
+    cmp     r0, #0                      @ failed?
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ vA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     0
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     0
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  r0 holds array class
+     *  r10 holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    ldr     r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+    mov     r2, #ALLOC_DONT_TRACK       @ r2<- alloc flags
+    ldrb    rINST, [r3, #1]             @ rINST<- descriptor[1]
+    .if     1
+    mov     r1, r10                     @ r1<- AA (length)
+    .else
+    mov     r1, r10, lsr #4             @ r1<- B (length)
+    .endif
+    cmp     rINST, #'I'                 @ array of ints?
+    cmpne   rINST, #'L'                 @ array of objects?
+    cmpne   rINST, #'['                 @ array of arrays?
+    mov     r9, r1                      @ save length in r9
+    bne     .LOP_FILLED_NEW_ARRAY_RANGE_notimpl         @ no, not handled yet
+    bl      dvmAllocArrayByClass        @ r0<- call(arClass, length, flags)
+    cmp     r0, #0                      @ null return?
+    beq     common_exceptionThrown      @ alloc failed, handle exception
+
+    FETCH(r1, 2)                        @ r1<- FEDC or CCCC
+    str     r0, [rSELF, #offThread_retval]      @ retval.l <- new array
+    str     rINST, [rSELF, #offThread_retval+4] @ retval.h <- type
+    add     r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+    subs    r9, r9, #1                  @ length--, check for neg
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    bmi     2f                          @ was zero, bail
+
+    @ copy values from registers into the array
+    @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+    .if     1
+    add     r2, rFP, r1, lsl #2         @ r2<- &fp[CCCC]
+1:  ldr     r3, [r2], #4                @ r3<- *r2++
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .else
+    cmp     r9, #4                      @ length was initially 5?
+    and     r2, r10, #15                @ r2<- A
+    bne     1f                          @ <= 4 args, branch
+    GET_VREG(r3, r2)                    @ r3<- vA
+    sub     r9, r9, #1                  @ count--
+    str     r3, [r0, #16]               @ contents[4] = vA
+1:  and     r2, r1, #15                 @ r2<- F/E/D/C
+    GET_VREG(r3, r2)                    @ r3<- vF/vE/vD/vC
+    mov     r1, r1, lsr #4              @ r1<- next reg in low 4
+    subs    r9, r9, #1                  @ count--
+    str     r3, [r0], #4                @ *contents++ = vX
+    bpl     1b
+    @ continue at 2
+    .endif
+
+2:
+    ldr     r0, [rSELF, #offThread_retval]     @ r0<- object
+    ldr     r1, [rSELF, #offThread_retval+4]   @ r1<- type
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    GET_INST_OPCODE(ip)                      @ ip<- opcode from rINST
+    cmp     r1, #'I'                         @ Is int array?
+    strneb  r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+    GOTO_OPCODE(ip)                          @ execute it
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    ldr     r0, .L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE
+3:  add     r0, pc
+    bl      dvmThrowInternalError
+    b       common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+.L_strFilledNewArrayNotImpl_OP_FILLED_NEW_ARRAY_RANGE:
+    .word   PCREL_REF(.LstrFilledNewArrayNotImpl,3b)
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+    mvn     r1, #0                      @ r1<- -1
+    @ Want to cond code the next mov so we can avoid branch, but don't see it;
+    @ instead, we just replicate the tail end.
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    mov     r1, #1                      @ r1<- 1
+    @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r1, r9)                    @ vAA<- r1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldrd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    add     r9, rFP, r9, lsl #2         @ r9<- &fp[AA]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r2-r3}                 @ vAA/vAA+1<- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r9, {r2-r3}                 @ r2/r3<- vAA/vAA+1
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r2, [r0, #offArrayObject_contents]  @ r2/r3<- vBB[vCC]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  r9 = vAA (obj)
+     *  r10 = offset into array (vBB + vCC * width)
+     */
+.LOP_APUT_OBJECT_finish:
+    cmp     r9, #0                      @ storing null reference?
+    beq     .LOP_APUT_OBJECT_skip_check      @ yes, skip type checks
+    ldr     r0, [r9, #offObject_clazz]  @ r0<- obj->clazz
+    ldr     r1, [rINST, #offObject_clazz]  @ r1<- arrayObj->clazz
+    bl      dvmCanPutArrayElement       @ test object type vs. array type
+    cmp     r0, #0                      @ okay?
+    beq     .LOP_APUT_OBJECT_throw           @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rSELF, #offThread_cardTable]     @ get biased CT base
+    add     r10, #offArrayObject_contents   @ r0<- pointer to slot
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10]                   @ vBB[vCC]<- vAA
+    strb    r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+.LOP_APUT_OBJECT_throw:
+    @ The types don't match.  We need to throw an ArrayStoreException.
+    ldr     r0, [r9, #offObject_clazz]
+    ldr     r1, [rINST, #offObject_clazz]
+    EXPORT_PC()
+    bl      dvmThrowArrayStoreExceptionIncompatibleElement
+    b       common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BOOLEAN_finish:
+    @bl      common_squeak1
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_BYTE_finish:
+    @bl      common_squeak2
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_CHAR_finish:
+    @bl      common_squeak3
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_SHORT_finish:
+    @bl      common_squeak4
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    @ no-op                             @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_finish:
+    @bl      common_squeak0
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    ubfx    r1, rINST, #8, #4           @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    ubfx    r2, rINST, #8, #4           @ r2<- A
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    @bl      common_squeak1
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    @bl      common_squeak2
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    @bl      common_squeak3
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    @bl      common_squeak4
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    @ no-op                         @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    @ no-op 
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_finish
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_finish          @ resume
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_finish
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BOOLEAN_finish
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_BYTE_finish
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_CHAR_finish
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_SHORT_finish
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_finish          @ resume
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    @ no-op 
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_finish          @ resume
+
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BOOLEAN_finish          @ resume
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_BYTE_finish          @ resume
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_CHAR_finish          @ resume
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_SHORT_finish          @ resume
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC */
+
+
+.LOP_INVOKE_STATIC_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodNoRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodNoRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodNoRange     @ whew, finally!
+#else
+    bne     common_invokeMethodNoRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(r9, r10)                   @ r9<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r9, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ (r0=method, r9="this")
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r10 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r10, #offClassObject_super]    @ r1<- method->clazz->super
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    ldr     r3, [r1, #offClassObject_vtableCount]   @ r3<- super->vtableCount
+    EXPORT_PC()                         @ must export for invoke
+    cmp     r2, r3                      @ compare (methodIndex, vtableCount)
+    bcs     .LOP_INVOKE_SUPER_RANGE_nsm             @ method not present in superclass
+    ldr     r1, [r1, #offClassObject_vtable]    @ r1<- ...clazz->super->vtable
+    ldr     r0, [r1, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    mov     r0, r10                     @ r0<- method->clazz
+    mov     r2, #METHOD_VIRTUAL         @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_SUPER_RANGE_continue        @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  r0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    ldr     r1, [r0, #offMethod_name]   @ r1<- method name
+    b       common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_DIRECT          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    ldr     r3, [rSELF, #offThread_method] @ r3<- self->method
+    ldr     r0, [r3, #offMethod_clazz]  @ r0<- method->clazz
+    mov     r2, #METHOD_STATIC          @ resolver method type
+    bl      dvmResolveMethod            @ r0<- call(clazz, ref, flags)
+    cmp     r0, #0                      @ got null?
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * r10: &resolved_methodToCall
+     */
+    ldrh    r2, [rSELF, #offThread_subMode]
+    beq     common_exceptionThrown            @ null, handle exception
+    ands    r2, #kSubModeJitTraceBuild        @ trace under construction?
+    beq     common_invokeMethodRange     @ no (r0=method, r9="this")
+    ldr     r1, [r10]                         @ reload resolved method
+    cmp     r1, #0                            @ finished resolving?
+    bne     common_invokeMethodRange     @ yes (r0=method, r9="this")
+    mov     r10, r0                           @ preserve method
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect              @ (self, pc)
+    mov     r0, r10
+    b       common_invokeMethodRange     @ whew, finally!
+#else
+    bne     common_invokeMethodRange     @ (r0=method, r9="this")
+    b       common_exceptionThrown            @ yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+    stmfd   sp!, {r4, lr}
+    mov     r1, #0x5f000000             @ (float)maxlong
+    mov     r4, r0
+    bl      __aeabi_fcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffff)
+    mvnne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, #0xdf000000             @ (float)minlong
+    bl      __aeabi_fcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (80000000)
+    movne   r1, #0x80000000
+    ldmnefd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r4
+    bl      __aeabi_fcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    ldmeqfd sp!, {r4, pc}
+
+    mov     r0, r4                      @ recover arg
+    bl      __aeabi_f2lz                @ convert float to long
+    ldmfd   sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+    stmfd   sp!, {r4, r5, lr}           @ save regs
+    mov     r3, #0x43000000             @ maxlong, as a double (high word)
+    add     r3, #0x00e00000             @  0x43e00000
+    mov     r2, #0                      @ maxlong, as a double (low word)
+    sub     sp, sp, #4                  @ align for EABI
+    mov     r4, r0                      @ save a copy of r0
+    mov     r5, r1                      @  and r1
+    bl      __aeabi_dcmpge              @ is arg >= maxlong?
+    cmp     r0, #0                      @ nonzero == yes
+    mvnne   r0, #0                      @ return maxlong (7fffffffffffffff)
+    mvnne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r3, #0xc3000000             @ minlong, as a double (high word)
+    add     r3, #0x00e00000             @  0xc3e00000
+    mov     r2, #0                      @ minlong, as a double (low word)
+    bl      __aeabi_dcmple              @ is arg <= minlong?
+    cmp     r0, #0                      @ nonzero == yes
+    movne   r0, #0                      @ return minlong (8000000000000000)
+    movne   r1, #0x80000000
+    bne     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    mov     r2, r4                      @ compare against self
+    mov     r3, r5
+    bl      __aeabi_dcmpeq              @ is arg == self?
+    cmp     r0, #0                      @ zero == no
+    moveq   r1, #0                      @ return zero for NaN
+    beq     1f
+
+    mov     r0, r4                      @ recover arg
+    mov     r1, r5
+    bl      __aeabi_d2lz                @ convert double to long
+
+1:
+    add     sp, sp, #4
+    ldmfd   sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r0, {r9-r10}                @ vAA/vAA+1<- r9/r10
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    mov     r0, r0, asl r2              @  r0<- r0 << r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    mov     r1, r1, asr r2              @  r1<- r1 >> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+    mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r9, {r0-r1}                 @ vAA/vAA+1<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_VOLATILE_finish
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_VOLATILE_finish          @ resume
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    ldr   r0, [r9, r3]                @ r0<- obj.field (8/16/32 bits)
+    SMP_DMB                            @ acquiring load
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r2)                    @ fp[A]<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    cmp     r9, #0                      @ check object for null
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    beq     common_errNullObject        @ object was null
+    .if     1
+    add     r0, r9, r3                  @ r0<- address of field
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldrd    r0, [r9, r3]                @ r0/r1<- obj.field (64-bit align ok)
+    .endif
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    and     r2, r2, #15                 @ r2<- A
+    add     r3, rFP, r2, lsl #2         @ r3<- &fp[A]
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r3, {r0-r1}                 @ fp[A]<- r0/r1
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    mov     r2, rINST, lsr #8           @ r2<- A+
+    cmp     r9, #0                      @ check object for null
+    and     r2, r2, #15                 @ r2<- A
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    add     r2, rFP, r2, lsl #2         @ r3<- &fp[A]
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldmia   r2, {r0-r1}                 @ r0/r1<- fp[A]
+    GET_INST_OPCODE(r10)                @ extract opcode from rINST
+    .if     1
+    add     r2, r9, r3                  @ r2<- target address
+    bl      dvmQuasiAtomicSwap64Sync    @ stores r0/r1 into addr r2
+    .else
+    strd    r0, [r9, r3]                @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r1<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r9:  &fp[AA]
+     *  r10: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    mov     r2, r0                      @ copy to r2
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_WIDE_VOLATILE_finish          @ resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LDR pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(rINST, 2)                     @ rINST<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, rINST, #0xf000          @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, rINST, #0x0f00          @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, rINST, #0x00f0          @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, rINST, #0x000f          @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     rINST, .LOP_EXECUTE_INLINE_table    @ table of InlineOperation
+5:  add     rINST, pc
+    ldr     pc, [rINST, r10, lsl #4]    @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_continue        @ make call; will return after
+    mov     rINST, r0                   @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, r9                      @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit @ (method, self)
+    cmp     rINST, #0                   @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  r0 = #of args (0-4)
+     *  r10 = call index
+     *  lr = return addr, above  [DO NOT bl out of here w/o preserving LR]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    rsb     r0, r0, #4                  @ r0<- 4-r0
+    FETCH(r9, 2)                        @ r9<- CCCC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  add     ip, r9, #3                  @ base+3
+    GET_VREG(r3, ip)                    @ r3<- vBase[3]
+3:  add     ip, r9, #2                  @ base+2
+    GET_VREG(r2, ip)                    @ r2<- vBase[2]
+2:  add     ip, r9, #1                  @ base+1
+    GET_VREG(r1, ip)                    @ r1<- vBase[1]
+1:  add     ip, r9, #0                  @ (nop)
+    GET_VREG(r0, ip)                    @ r0<- vBase[0]
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_RANGE_table       @ table of InlineOperation
+5:  add     r9, pc
+    ldr     pc, [r9, r10, lsl #4]       @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+
+    /*
+     * We're debugging or profiling.
+     * r10: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    mov     r0, r10
+    bl      dvmResolveInlineNative
+    cmp     r0, #0                      @ did it resolve?
+    beq     .LOP_EXECUTE_INLINE_RANGE_resume          @ no, just move on
+    mov     r9, r0                      @ remember method
+    mov     r1, rSELF
+    bl      dvmFastMethodTraceEnter     @ (method, self)
+    add     r1, rSELF, #offThread_retval@ r1<- &self->retval
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- B
+    mov     rINST, r9                   @ rINST<- method
+    str     r1, [sp]                    @ push &self->retval
+    bl      .LOP_EXECUTE_INLINE_RANGE_continue        @ make call; will return after
+    mov     r9, r0                      @ save result of inline
+    add     sp, sp, #8                  @ pop stack
+    mov     r0, rINST                   @ r0<- method
+    mov     r1, rSELF
+    bl      dvmFastNativeMethodTraceExit  @ (method, self)
+    cmp     r9, #0                      @ test boolean result of inline
+    beq     common_exceptionThrown      @ returned false, handle exception
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+
+
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   PCREL_REF(gDvmInlineOpsTable,5b)
+
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         @ can throw
+    bl      dvmSetFinalizable           @ call dvmSetFinalizable(obj)
+    ldr     r0, [rSELF, #offThread_exception] @ r0<- self->exception
+    cmp     r0, #0                      @ exception pending?
+    bne     common_exceptionThrown      @ yes, handle it
+    b       .LOP_INVOKE_OBJECT_INIT_RANGE_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    ldr     r1, [rSELF, #offThread_mainHandlerTable]
+    mov     ip, #OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(r1,ip)             @ execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  r0 holds resolved field
+     *  r9 holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    @bl      common_squeak0
+    mov     r1, rINST, lsr #8           @ r1<- A+
+    ldr     r3, [r0, #offInstField_byteOffset]  @ r3<- byte offset of field
+    and     r1, r1, #15                 @ r1<- A
+    cmp     r9, #0                      @ check object for null
+    GET_VREG(r0, r1)                    @ r0<- fp[A]
+    ldr     r2, [rSELF, #offThread_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    SMP_DMB_ST                        @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    SMP_DMB
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     r0, #0                      @ stored a null reference?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card if not
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1:  BBBB field ref
+     *  r10: dvmDex->pResFields
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SGET_OBJECT_VOLATILE_finish
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_end:
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    SMP_DMB
+    cmp     r1, #0                      @ stored a null object?
+    strneb  r2, [r2, r9, lsr #GC_CARD_SHIFT]  @ mark card based on obj head
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /* Continuation if the field has not yet been resolved.
+     * r1:  BBBB field ref
+     * r10: dvmDex->pResFields
+     */
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rSELF, #offThread_method]    @ r2<- current method
+#if defined(WITH_JIT)
+    add     r10, r10, r1, lsl #2        @ r10<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r2, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    beq     common_exceptionThrown      @ no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    bl      common_verifyField
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish          @ resume
+
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (0 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (1 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (2 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (3 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (4 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (5 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (6 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (7 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (8 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (9 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (10 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (11 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (12 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (13 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (14 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (15 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (16 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (17 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (18 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (19 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (20 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (21 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (22 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (23 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (24 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (25 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (26 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (27 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (28 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (29 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (30 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (31 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (32 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (33 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (34 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (35 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (36 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (37 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (38 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (39 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (40 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (41 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (42 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (43 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (44 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (45 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (46 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (47 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (48 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (49 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (50 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (51 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (52 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (53 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (54 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (55 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (56 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (57 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (58 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (59 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (60 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (61 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (62 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (63 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (64 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (65 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (66 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (67 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (68 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (69 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (70 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (71 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (72 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (73 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (74 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (75 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (76 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (77 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (78 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (79 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (80 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (81 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (82 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (83 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (84 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (85 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (86 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (87 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (88 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (89 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (90 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (91 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (92 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (93 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (94 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (95 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (96 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (97 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (98 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (99 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (100 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (101 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (102 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (103 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (104 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (105 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (106 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (107 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (108 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (109 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (110 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (111 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (112 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (113 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (114 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (115 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (116 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (117 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (118 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (119 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (120 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (121 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (122 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (123 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (124 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (125 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (126 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (127 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (128 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (129 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (130 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (131 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (132 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (133 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (134 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (135 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (136 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (137 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (138 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (139 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (140 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (141 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (142 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (143 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (144 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (145 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (146 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (147 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (148 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (149 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (150 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (151 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (152 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (153 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (154 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (155 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (156 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (157 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (158 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (159 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (160 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (161 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (162 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (163 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (164 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (165 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (166 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (167 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (168 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (169 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (170 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (171 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (172 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (173 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (174 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (175 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (176 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (177 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (178 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (179 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (180 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (181 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (182 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (183 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (184 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (185 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (186 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (187 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (188 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (189 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (190 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (191 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (192 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (193 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (194 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (195 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (196 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (197 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (198 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (199 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (200 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (201 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (202 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (203 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (204 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (205 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (206 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (207 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (208 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (209 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (210 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (211 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (212 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (213 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (214 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (215 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (216 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (217 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (218 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (219 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (220 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (221 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (222 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (223 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (224 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (225 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (226 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (227 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (228 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (229 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (230 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (231 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (232 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (233 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (234 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (235 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (236 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (237 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (238 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (239 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (240 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (241 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (242 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (243 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (244 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (245 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (246 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (247 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (248 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (249 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (250 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (251 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (252 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (253 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (254 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+/* ------------------------------ */
+    .balign 64
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    ldrb   r3, [rSELF, #offThread_breakFlags]
+    adrl   lr, dvmAsmInstructionStart + (255 * 64)
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    cmp    r3, #0
+    bxeq   lr                   @ nothing to do - jump to real handler
+    EXPORT_PC()
+    mov    r0, rPC              @ arg0
+    mov    r1, rFP              @ arg1
+    mov    r2, rSELF            @ arg2
+    b      dvmCheckBefore       @ (dPC,dFP,self) tail call
+
+    .balign 64
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r10, [rSELF,#offThread_jitResumeNPC]  @ resume address
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    b      jitSVShadowRunStart                   @ resume as if cache hit
+                                                 @ expects resume addr in r10
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation              @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    mov    rSELF, r0                             @ restore self
+    mov    rPC, r1                               @ restore Dalvik pc
+    mov    rFP, r2                               @ restore Dalvik fp
+    ldr    r0, [rSELF,#offThread_jitResumeNPC]
+    mov    r2, #0
+    str    r2, [rSELF,#offThread_jitResumeNPC]   @ reset resume address
+    ldr    sp, [rSELF,#offThread_jitResumeNSP]   @ cut back native stack
+    bx     r0                                    @ resume translation
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ *    dPC <= Dalvik PC of instrucion to interpret
+ *    lr <= resume point in translation
+ *    r1 <= Dalvik PC of next instruction
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    mov    rPC, r0              @ set up dalvik pc
+    EXPORT_PC()
+    str    lr, [rSELF,#offThread_jitResumeNPC]
+    str    sp, [rSELF,#offThread_jitResumeNSP]
+    str    r1, [rSELF,#offThread_jitResumeDPC]
+    mov    r1, #1
+    str    r1, [rSELF,#offThread_singleStepCount]  @ just step once
+    mov    r0, rSELF
+    mov    r1, #kSubModeCountedStep
+    bl     dvmEnableSubMode     @ (self, newMode)
+    ldr    rIBASE, [rSELF,#offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ !0 means translation exists
+    bxne   r0                       @ continue native execution if so
+    b      2f                       @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    2f
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    FETCH_INST()
+    cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
+    bne    common_selectTrace
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    rPC,[lr, #-1]           @ get our target PC
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST,#-4               @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNormal
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    cmp    r0,#0
+    beq    toInterpreter            @ go if not, otherwise do chain
+    mov    r1,rINST
+    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0                    @ successful chain?
+    bxne   r0                       @ continue native execution
+    b      toInterpreter            @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+    bl     dvmBumpNoChain
+#endif
+    mov    r0,rPC
+    mov    r1,rSELF
+    bl     dvmJitGetTraceAddrThread @ (pc, self)
+    str    r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    ldr    r0, [rSELF, #offThread_pJitProfTable]
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    @ NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+common_testUpdateProfile:
+    cmp     r0, #0               @ JIT switched off?
+    beq     4f                   @ return to interp if so
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
+    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+    GET_INST_OPCODE(ip)
+    subs    r1,r1,#1           @ decrement counter
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
+
+    /* Looks good, reset the counter */
+    ldr     r1, [rSELF, #offThread_jitThreshold]
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    mov     r1,rSELF
+    bl      dvmJitGetTraceAddrThread    @ (pc, self)
+    str     r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov     r1, rPC                     @ arg1 of translation may need this
+    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
+    cmp     r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+    bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
+#else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
+    beq     common_selectTrace
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    mov     r10, r0                     @ save target
+    bl      dvmCompilerGetInterpretTemplate
+    cmp     r0, r10                     @ special case?
+    bne     jitSVShadowRunStart         @ set up self verification shadow space
+    @ Need to clear the inJitCodeCache flag
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+common_selectTrace:
+    ldrh    r0,[rSELF,#offThread_subMode]
+    ands    r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+    bne     3f                         @ already doing JIT work, continue
+    str     r2,[rSELF,#offThread_jitState]
+    mov     r0, rSELF
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+    EXPORT_PC()
+    SAVE_PC_FP_TO_SELF()                 @ copy of pc/fp to Thread
+    bl      dvmJitCheckTraceRequest
+3:
+    FETCH_INST()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    mov     r0,rPC                      @ r0<- program counter
+    mov     r1,rFP                      @ r1<- frame pointer
+    mov     r2,rSELF                    @ r2<- self (Thread) pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ *   r0:  dPC
+ *   r2:  self verification state
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    mov    r3,rSELF                      @ pass self ptr for convenience
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()               @ restore pc, fp
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    @ Set up SV single-stepping
+    mov    r0, rSELF
+    mov    r1, #kSubModeJitSV
+    bl     dvmEnableSubMode              @ (self, subMode)
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rSELF,#offThread_jitState]
+    @ intentional fallthrough
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                @ export state to "thread"
+    mov     r0, rSELF                   @ r0<- self ptr
+    b       dvmMterpStdBail             @ call(self, changeInterp)
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     r9, #0
+    ldrne   r9, [r9, #offObject_clazz]
+    str     r0, [rSELF, #offThread_methodToCall]
+    str     r9, [rSELF, #offThread_callsiteClass]
+    bx      lr
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    beq     .LinvokeArgsDone            @ if no args, skip the rest
+    FETCH(r1, 2)                        @ r1<- CCCC
+
+.LinvokeRangeArgs:
+    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+    @ (very few methods have > 10 args; could unroll for common cases)
+    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
+    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    ldrh    r1, [rSELF, #offThread_subMode]
+    ands    r1, #kSubModeJitTraceBuild
+    blne    save_callsiteinfo
+#endif
+    @ prepare to copy args to "outs" area of current frame
+    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
+    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
+    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+    rsb     r2, r2, #5                  @ r2<- 5-r2
+    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+5:  and     ip, rINST, #0x0f00          @ isolate A
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vA
+4:  and     ip, r1, #0xf000             @ isolate G
+    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vG
+3:  and     ip, r1, #0x0f00             @ isolate F
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vF
+2:  and     ip, r1, #0x00f0             @ isolate E
+    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vE
+1:  and     ip, r1, #0x000f             @ isolate D
+    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
+    mov     r0, r0                      @ nop
+    str     r2, [r10, #-4]!             @ *--outs = vD
+0:  @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
+    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
+    @ find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
+    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
+@    bl      common_dumpRegs
+    ldr     r9, [rSELF, #offThread_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+    blo     .LstackOverflow             @ yes, this frame will overflow stack
+
+    @ set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
+    str     ip, [r10, #offStackSaveArea_prevSave]
+#endif
+    str     rFP, [r10, #offStackSaveArea_prevFrame]
+    str     rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+    mov     r9, #0
+    str     r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+    str     r0, [r10, #offStackSaveArea_method]
+
+    @ Profiling?
+    cmp     lr, #0                      @ any special modes happening?
+    bne     2f                          @ go if so
+1:
+    tst     r3, #ACC_NATIVE
+    bne     .LinvokeNative
+
+    /*
+    stmfd   sp!, {r0-r3}
+    bl      common_printNewline
+    mov     r0, rFP
+    mov     r1, #0
+    bl      dvmDumpFp
+    ldmfd   sp!, {r0-r3}
+    stmfd   sp!, {r0-r3}
+    mov     r0, r1
+    mov     r1, r10
+    bl      dvmDumpFp
+    bl      common_printNewline
+    ldmfd   sp!, {r0-r3}
+    */
+
+    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
+    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    mov     rPC, r2                         @ publish new rPC
+
+    @ Update state values for the new method
+    @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+    str     r0, [rSELF, #offThread_method]    @ self->method = methodToCall
+    str     r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+    mov     r2, #1
+    str     r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+    ldr     r0, [rSELF, #offThread_pJitProfTable]
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    cmp     r0,#0
+    bne     common_updateProfile
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#else
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+2:
+    @ Profiling - record method entry.  r0: methodToCall
+    stmfd   sp!, {r0-r3}                @ preserve r0-r3
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    mov     r1, r0
+    mov     r0, rSELF
+    bl      dvmReportInvoke             @ (self, method)
+    ldmfd   sp!, {r0-r3}                @ restore r0-r3
+    b       1b
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldrh    lr, [rSELF, #offThread_subMode]
+    ldr     r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+    str     r1, [rSELF, #offThread_curFrame]   @ curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rSELF, #offThread_retval  @ r1<- &retval
+    mov     r3, rSELF                   @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b       .Lskip
+    .type   dalvik_mterp, %function
+dalvik_mterp:
+    .fnstart
+    MTERP_ENTRY1
+    MTERP_ENTRY2
+.Lskip:
+#endif
+
+    cmp     lr, #0                      @ any special SubModes active?
+    bne     11f                         @ go handle them if so
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+7:
+
+    @ native return; r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [rSELF, #offThread_exception] @ check for exception
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+    bne     common_exceptionThrown      @ no, handle exception
+
+    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+11:
+    @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+    stmfd   sp!, {r0-r3}                @ save all but subModes
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPreNativeInvoke    @ (methodToCall, self, fp)
+    ldmfd   sp, {r0-r3}                 @ refresh.  NOTE: no sp autoincrement
+
+    @ Call the native method
+    ldr     ip, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    blx     ip
+
+    @ Restore the pre-call arguments
+    ldmfd   sp!, {r0-r3}                @ r2<- methodToCall (others unneeded)
+
+    @ Finish up any post-invoke subMode requirements
+    mov     r0, r2                      @ r0<- methodToCall
+    mov     r1, rSELF
+    mov     r2, rFP
+    bl      dvmReportPostNativeInvoke   @ (methodToCall, self, fp)
+    b       7b                          @ resume
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    mov     r0, rSELF                   @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+    .size   dalvik_mterp, .-dalvik_mterp
+#endif
+
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+     .if    0
+.LinvokeOld:
+    sub     sp, sp, #8                  @ space for args + pad
+    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
+    mov     r2, r0                      @ A2<- methodToCall
+    mov     r0, rSELF                   @ A0<- self
+    SAVE_PC_FP_TO_SELF()                @ export state to "self"
+    mov     r1, r9                      @ A1<- methodCallRange
+    mov     r3, rINST, lsr #8           @ A3<- AA
+    str     ip, [sp, #0]                @ A4<- ip
+    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
+    add     sp, sp, #8                  @ remove arg area
+    b       common_resumeAfterGlueCall  @ continue to next instruction
+    .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    ldrh    lr, [rSELF, #offThread_subMode]
+    SAVEAREA_FROM_FP(r0, rFP)
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    cmp     lr, #0                      @ any special subMode handling needed?
+    bne     19f
+14:
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    cmp     r2, #0                      @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+    /* Don't use conditional loads if the HW defect exists */
+    beq     15f
+    ldr     r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rSELF, #offThread_method]@ self->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [rSELF, #offThread_curFrame]  @ curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    str     r10, [rSELF, #offThread_inJitCodeCache]  @ may return to JIT'ed land
+    cmp     r10, #0                      @ caller is compiled code
+    blxne   r10
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rSELF, #offThread_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+19:
+    @ Handle special actions
+    @ On entry, r0: StackSaveArea
+    ldr     r1, [r0, #offStackSaveArea_prevFrame]  @ r2<- prevFP
+    str     rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+    str     r1, [rSELF, #offThread_curFrame]   @ update interpSave.curFrame
+    mov     r0, rSELF
+    bl      dvmReportReturn             @ (self)
+    SAVEAREA_FROM_FP(r0, rFP)           @ restore StackSaveArea
+    b       14b                         @ continue
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_returnFromMethod
+    b       common_resumeAfterGlueCall
+    .endif
+
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+     .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+
+    mov     r0, rSELF
+    bl      dvmCheckSuspendPending
+
+    ldr     r9, [rSELF, #offThread_exception] @ r9<- self->exception
+    mov     r1, rSELF                   @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    ldrh    r2, [rSELF, #offThread_subMode]  @ get subMode flags
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+    @ Special subMode?
+    cmp     r2, #0                      @ any special subMode handling needed?
+    bne     7f                          @ go if so
+8:
+    /* set up args and a local for "&fp" */
+    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
+    str     rFP, [sp, #-4]!             @ *--sp = fp
+    mov     ip, sp                      @ ip<- &fp
+    mov     r3, #0                      @ r3<- false
+    str     ip, [sp, #-4]!              @ *--sp = &fp
+    ldr     r1, [rSELF, #offThread_method] @ r1<- self->method
+    mov     r0, rSELF                   @ r0<- self
+    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
+    mov     r2, r9                      @ r2<- exception
+    sub     r1, rPC, r1                 @ r1<- pc - method->insns
+    mov     r1, r1, asr #1              @ r1<- offset in code units
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, rSELF                   @ r0<- self
+    mov     r1, r9                      @ r1<- exception
+    bl      dvmCleanupStackOverflow     @ call(self)
+    mov     r0, rFP                     @ restore result
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
+    cmp     r0, #0                      @ is catchRelPc < 0?
+    add     sp, sp, #8                  @ restore stack
+    bmi     .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rSELF, #offThread_method]  @ self->method = new method
+    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
+    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
+    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
+    str     r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    ldr    rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh rIBASE
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
+    streq   r9, [rSELF, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    @ Manage debugger bookkeeping
+7:
+    str     rPC, [rSELF, #offThread_pc]     @ update interpSave.pc
+    str     rFP, [rSELF, #offThread_curFrame]     @ update interpSave.curFrame
+    mov     r0, rSELF                       @ arg0<- self
+    mov     r1, r9                          @ arg1<- exception
+    bl      dvmReportExceptionThrow         @ (self, exception)
+    b       8b                              @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+    /* fix stack overflow if necessary */
+    ldrb    r1, [rSELF, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, rSELF                   @ if yes: r0<- self
+    movne   r1, r9                      @ if yes: r1<- exception
+    blne    dvmCleanupStackOverflow     @ if yes: call(self)
+
+    @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    @ dvmLineNumFromPC(method, pc - method->insns)
+    ldr     r0, [rSELF, #offThread_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rSELF, #offThread_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+0:  add     r2, pc
+    ldr     r1, strLogTag
+1:  add     r1, pc
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [rSELF, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, rSELF                   @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    b       common_gotoBail             @ bail out
+
+strExceptionNotCaughtLocally:
+    .word   PCREL_REF(.LstrExceptionNotCaughtLocally,0b)
+strLogTag:
+    .word   PCREL_REF(.LstrLogTag,1b)
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_SELF()                @ export state
+    mov     r0, rSELF                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     r10: &dvmDex->pResFields[field]
+     *     r0:  field pointer (must preserve)
+     */
+common_verifyField:
+    ldrh    r3, [rSELF, #offThread_subMode]  @ r3 <- submode byte
+    ands    r3, #kSubModeJitTraceBuild
+    bxeq    lr                          @ Not building trace, continue
+    ldr     r1, [r10]                   @ r1<- reload resolved StaticField ptr
+    cmp     r1, #0                      @ resolution complete?
+    bxne    lr                          @ yes, continue
+    stmfd   sp!, {r0-r2,lr}             @ save regs
+    mov     r0, rSELF
+    mov     r1, rPC
+    bl      dvmJitEndTraceSelect        @ (self,pc) end trace before this inst
+    ldmfd   sp!, {r0-r2, lr}
+    bx      lr                          @ return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()              @ pull rPC and rFP out of thread
+    ldr     rIBASE, [rSELF, #offThread_curHandlerTable]  @ refresh
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    mov     r0, r3
+    bl      dvmThrowArrayIndexOutOfBoundsException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strDivideByZero
+0:  add     r0, pc
+    bl      dvmThrowArithmeticException
+    b       common_exceptionThrown
+
+strDivideByZero:
+    .word   PCREL_REF(.LstrDivideByZero,0b)
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    mov     r0, r1                                @ arg0 <- len
+    bl      dvmThrowNegativeArraySizeException    @ (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    mov     r0, r1
+    bl      dvmThrowNoSuchMethodError
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    mov     r0, #0
+    bl      dvmThrowNullPointerException
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault.  The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+    ldr     pc, .LdeadFood
+.LdeadFood:
+    .word   0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers.  (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+    .macro  SQUEAK num
+common_squeak\num:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strSqueak\num
+0:  add     r0, pc
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak\num:
+    .word   PCREL_REF(.LstrSqueak,0b)
+    .endm
+
+    SQUEAK  0
+    SQUEAK  1
+    SQUEAK  2
+    SQUEAK  3
+    SQUEAK  4
+    SQUEAK  5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strSqueak
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strSqueak:
+    .word   PCREL_REF(.LstrSqueak,0b)
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strNewline:
+    .word   PCREL_REF(.LstrNewline,0b)
+
+    /*
+     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+     */
+common_printHex:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r1, r0
+    ldr     r0, strPrintHex
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintHex:
+    .word   PCREL_REF(.LstrPrintHex,0b)
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    mov     r3, r1
+    mov     r2, r0
+    ldr     r0, strPrintLong
+0:  add     r0, pc
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+strPrintLong:
+    .word   PCREL_REF(.LstrPrintLong,0b)
+
+/*
+ * Print full method info.  Pass the Method* in r0.  Preserves regs.
+ */
+common_printMethod:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpPrintMethod
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if     0
+common_dumpRegs:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bl      dvmMterpDumpArmRegs
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+    and     r0, r0, r1                  @ make sure no stray bits are set
+    fmrx    r2, fpscr                   @ get VFP reg
+    mvn     r1, r1                      @ bit-invert mask
+    and     r2, r2, r1                  @ clear masked bits
+    orr     r2, r2, r0                  @ set specified bits
+    fmxr    fpscr, r2                   @ set VFP reg
+    mov     r0, r2                      @ return new value
+    bx      lr
+
+    .align  2
+    .global dvmConfigureFP
+    .type   dvmConfigureFP, %function
+dvmConfigureFP:
+    stmfd   sp!, {ip, lr}
+    /* 0x03000000 sets DN/FZ */
+    /* 0x00009f00 clears the six exception enable flags */
+    bl      common_squeak0
+    mov     r0, #0x03000000             @ r0<- 0x03000000
+    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
+    bl      setFPSCR
+    ldmfd   sp!, {ip, pc}
+#endif
+
+
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly.  ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+    .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<%#x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-mips.S b/vm/mterp/out/InterpAsm-mips.S
new file mode 100644
index 0000000..af4ad23
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-mips.S
@@ -0,0 +1,18584 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: mips/header.S */
+#include "../common/asm-constants.h"
+#include "../common/mips-defines.h"
+
+#ifdef __mips_hard_float
+#define HARD_FLOAT
+#else
+#define SOFT_FLOAT
+#endif
+
+#if (__mips==32) && (__mips_isa_rev>=2)
+#define MIPS32R2
+#endif
+
+/* MIPS definitions and declarations
+
+   reg	nick		purpose
+   s0	rPC		interpreted program counter, used for fetching instructions
+   s1	rFP		interpreted frame pointer, used for accessing locals and args
+   s2	rSELF		self (Thread) pointer
+   s3	rIBASE		interpreted instruction base pointer, used for computed goto
+   s4	rINST		first 16-bit code unit of current instruction
+*/
+
+
+/* single-purpose registers, given names for clarity */
+#define rPC s0
+#define rFP s1
+#define rSELF s2
+#define rIBASE s3
+#define rINST s4
+#define rOBJ s5
+#define rBIX s6
+#define rTEMP s7
+
+/* The long arguments sent to function calls in Big-endian mode should be register
+swapped when sent to functions in little endian mode. In other words long variable
+sent as a0(MSW), a1(LSW) for a function call in LE mode should be sent as a1, a0 in
+Big Endian mode */
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define rARG0 a0
+#define rARG1 a1
+#define rARG2 a2
+#define rARG3 a3
+#define rRESULT0 v0
+#define rRESULT1 v1
+#else
+#define rARG0 a1
+#define rARG1 a0
+#define rARG2 a3
+#define rARG3 a2
+#define rRESULT0 v1
+#define rRESULT1 v0
+#endif
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_SELF() lw rPC, offThread_pc(rSELF)
+#define SAVE_PC_TO_SELF() sw rPC, offThread_pc(rSELF)
+#define LOAD_FP_FROM_SELF() lw rFP, offThread_curFrame(rSELF)
+#define SAVE_FP_TO_SELF() sw rFP, offThread_curFrame(rSELF)
+#define LOAD_PC_FP_FROM_SELF() \
+	LOAD_PC_FROM_SELF();   \
+	LOAD_FP_FROM_SELF()
+#define SAVE_PC_FP_TO_SELF()   \
+	SAVE_PC_TO_SELF();     \
+	SAVE_FP_TO_SELF()
+
+#define EXPORT_PC() \
+    sw        rPC, (offStackSaveArea_currentPc - sizeofStackSaveArea)(rFP)
+
+#define SAVEAREA_FROM_FP(rd, _fpreg) \
+    subu      rd, _fpreg, sizeofStackSaveArea
+
+#define FETCH_INST() lhu rINST, (rPC)
+
+#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \
+    addu      rPC, rPC, ((_count) * 2)
+
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+    lhu       _dreg, ((_count)*2)(_sreg) ;            \
+    addu      _sreg, _sreg, (_count)*2
+
+#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \
+    lhu       rINST, (rPC)
+
+#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC)
+#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+
+#else
+
+#define FETCH_B(rd, _count) lbu rd, ((_count) * 2 + 1)(rPC)
+#define FETCH_C(rd, _count) lbu rd, ((_count) * 2)(rPC)
+
+#endif
+
+#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+
+#define GET_PREFETCHED_OPCODE(dreg, sreg)   andi     dreg, sreg, 255
+
+#define GOTO_OPCODE(rd) sll rd, rd, 7; \
+    addu      rd, rIBASE, rd; \
+    jr        rd
+
+#define GOTO_OPCODE_BASE(_base, rd)  sll rd, rd, 7; \
+    addu      rd, _base, rd; \
+    jr        rd
+
+#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix)
+
+#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; l.s rd, (AT); .set at
+
+#define SET_VREG(rd, rix) STORE_eas2(rd, rFP, rix)
+
+#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \
+    sll       dst, dst, 7; \
+    addu      dst, rIBASE, dst; \
+    sll       t8, rix, 2; \
+    addu      t8, t8, rFP; \
+    jr        dst; \
+    sw        rd, 0(t8); \
+    .set reorder
+
+#define SET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \
+    .set noat; s.s rd, (AT); .set at
+
+
+#define GET_OPA(rd) srl rd, rINST, 8
+#ifndef MIPS32R2
+#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf
+#else
+#define GET_OPA4(rd) ext rd, rINST, 8, 4
+#endif
+#define GET_OPB(rd) srl rd, rINST, 12
+
+#define LOAD_rSELF_OFF(rd, off) lw rd, offThread_##off## (rSELF)
+
+#define LOAD_rSELF_method(rd) LOAD_rSELF_OFF(rd, method)
+#define LOAD_rSELF_methodClassDex(rd) LOAD_rSELF_OFF(rd, methodClassDex)
+#define LOAD_rSELF_interpStackEnd(rd) LOAD_rSELF_OFF(rd, interpStackEnd)
+#define LOAD_rSELF_retval(rd) LOAD_rSELF_OFF(rd, retval)
+#define LOAD_rSELF_pActiveProfilers(rd) LOAD_rSELF_OFF(rd, pActiveProfilers)
+#define LOAD_rSELF_bailPtr(rd) LOAD_rSELF_OFF(rd, bailPtr)
+#define LOAD_rSELF_SelfSuspendCount(rd) LOAD_rSELF_OFF(rd, SelfSuspendCount)
+
+
+/*
+ * Form an Effective Address rd = rbase + roff<<n;
+ * Uses reg AT
+ */
+#define EASN(rd, rbase, roff, rshift) .set noat; \
+    sll       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define EAS1(rd, rbase, roff) EASN(rd, rbase, roff, 1)
+#define EAS2(rd, rbase, roff) EASN(rd, rbase, roff, 2)
+#define EAS3(rd, rbase, roff) EASN(rd, rbase, roff, 3)
+#define EAS4(rd, rbase, roff) EASN(rd, rbase, roff, 4)
+
+/*
+ * Form an Effective Shift Right rd = rbase + roff>>n;
+ * Uses reg AT
+ */
+#define ESRN(rd, rbase, roff, rshift) .set noat; \
+    srl       AT, roff, rshift; \
+    addu      rd, rbase, AT; \
+    .set at
+
+#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; lw rd, 0(AT); .set at
+
+#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \
+    .set noat; sw rd, 0(AT); .set at
+
+#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase)
+#define LOADu2_RB_OFF(rd, rbase, off) lhu rd, off(rbase)
+#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase)
+
+#ifdef HAVE_LITTLE_ENDIAN
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \
+    sw        rhi, (off+4)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \
+    lw        rhi, (off+4)(rbase)
+
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \
+    s.s       rhi, (off+4)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \
+    l.s       rhi, (off+4)(rbase)
+#else
+
+#define STORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define vSTORE64_off(rlo, rhi, rbase, off) sw rlo, (off+4)(rbase); \
+    sw        rhi, (off)(rbase)
+#define vLOAD64_off(rlo, rhi, rbase, off) lw rlo, (off+4)(rbase); \
+    lw        rhi, (off)(rbase)
+#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, (off+4)(rbase); \
+    s.s       rhi, (off)(rbase)
+#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, (off+4)(rbase); \
+    l.s       rhi, (off)(rbase)
+#endif
+
+#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0)
+#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0)
+
+#define vSTORE64(rlo, rhi, rbase) vSTORE64_off(rlo, rhi, rbase, 0)
+#define vLOAD64(rlo, rhi, rbase) vLOAD64_off(rlo, rhi, rbase, 0)
+
+#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0)
+#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0)
+
+#define STORE64_lo(rd, rbase) sw rd, 0(rbase)
+#define STORE64_hi(rd, rbase) sw rd, 4(rbase)
+
+
+#define LOAD_offThread_exception(rd, rbase) LOAD_RB_OFF(rd, rbase, offThread_exception)
+#define LOAD_base_offArrayObject_length(rd, rbase) LOAD_RB_OFF(rd, rbase, offArrayObject_length)
+#define LOAD_base_offClassObject_accessFlags(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_accessFlags)
+#define LOAD_base_offClassObject_descriptor(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_descriptor)
+#define LOAD_base_offClassObject_super(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_super)
+
+#define LOAD_base_offClassObject_vtable(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtable)
+#define LOAD_base_offClassObject_vtableCount(rd, rbase) LOAD_RB_OFF(rd, rbase, offClassObject_vtableCount)
+#define LOAD_base_offDvmDex_pResClasses(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResClasses)
+#define LOAD_base_offDvmDex_pResFields(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResFields)
+
+#define LOAD_base_offDvmDex_pResMethods(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResMethods)
+#define LOAD_base_offDvmDex_pResStrings(rd, rbase) LOAD_RB_OFF(rd, rbase, offDvmDex_pResStrings)
+#define LOAD_base_offInstField_byteOffset(rd, rbase) LOAD_RB_OFF(rd, rbase, offInstField_byteOffset)
+#define LOAD_base_offStaticField_value(rd, rbase) LOAD_RB_OFF(rd, rbase, offStaticField_value)
+#define LOAD_base_offMethod_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_clazz)
+
+#define LOAD_base_offMethod_name(rd, rbase) LOAD_RB_OFF(rd, rbase, offMethod_name)
+#define LOAD_base_offObject_clazz(rd, rbase) LOAD_RB_OFF(rd, rbase, offObject_clazz)
+
+#define LOADu2_offMethod_methodIndex(rd, rbase) LOADu2_RB_OFF(rd, rbase, offMethod_methodIndex)
+
+
+#define STORE_offThread_exception(rd, rbase) STORE_RB_OFF(rd, rbase, offThread_exception)
+
+
+#define STACK_STORE(rd, off) sw rd, off(sp)
+#define STACK_LOAD(rd, off) lw rd, off(sp)
+#define CREATE_STACK(n) subu sp, sp, n
+#define DELETE_STACK(n) addu sp, sp, n
+
+#define SAVE_RA(offset) STACK_STORE(ra, offset)
+#define LOAD_RA(offset) STACK_LOAD(ra, offset)
+
+#define LOAD_ADDR(dest, addr) la dest, addr
+#define LOAD_IMM(dest, imm) li dest, imm
+#define MOVE_REG(dest, src) move dest, src
+#define RETURN jr ra
+#define STACK_SIZE 128
+
+#define STACK_OFFSET_ARG04 16
+#define STACK_OFFSET_ARG05 20
+#define STACK_OFFSET_ARG06 24
+#define STACK_OFFSET_ARG07 28
+#define STACK_OFFSET_SCR   32
+#define STACK_OFFSET_SCRMX 80
+#define STACK_OFFSET_GP    84
+#define STACK_OFFSET_rFP   112
+
+#define JAL(n) jal n
+#define BAL(n) bal n
+
+#define STACK_STORE_RA() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(gp, STACK_OFFSET_GP); \
+    STACK_STORE(ra, 124)
+
+#define STACK_STORE_S0() STACK_STORE_RA(); \
+    STACK_STORE(s0, 116)
+
+#define STACK_STORE_S0S1() STACK_STORE_S0(); \
+    STACK_STORE(s1, STACK_OFFSET_rFP)
+
+#define STACK_LOAD_RA() STACK_LOAD(ra, 124); \
+    STACK_LOAD(gp, STACK_OFFSET_GP); \
+    DELETE_STACK(STACK_SIZE)
+
+#define STACK_LOAD_S0() STACK_LOAD(s0, 116); \
+    STACK_LOAD_RA()
+
+#define STACK_LOAD_S0S1() STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD_S0()
+
+#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \
+    STACK_STORE(ra, 124); \
+    STACK_STORE(fp, 120); \
+    STACK_STORE(s0, 116); \
+    STACK_STORE(s1, STACK_OFFSET_rFP); \
+    STACK_STORE(s2, 108); \
+    STACK_STORE(s3, 104); \
+    STACK_STORE(s4, 100); \
+    STACK_STORE(s5, 96); \
+    STACK_STORE(s6, 92); \
+    STACK_STORE(s7, 88);
+
+#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \
+    STACK_LOAD(s7, 88); \
+    STACK_LOAD(s6, 92); \
+    STACK_LOAD(s5, 96); \
+    STACK_LOAD(s4, 100); \
+    STACK_LOAD(s3, 104); \
+    STACK_LOAD(s2, 108); \
+    STACK_LOAD(s1, STACK_OFFSET_rFP); \
+    STACK_LOAD(s0, 116); \
+    STACK_LOAD(fp, 120); \
+    STACK_LOAD(ra, 124); \
+    DELETE_STACK(STACK_SIZE)
+
+/*
+ * first 8 words are reserved for function calls
+ * Maximum offset is STACK_OFFSET_SCRMX-STACK_OFFSET_SCR
+ */
+#define SCRATCH_STORE(r,off) \
+    STACK_STORE(r, STACK_OFFSET_SCR+off);
+#define SCRATCH_LOAD(r,off) \
+    STACK_LOAD(r, STACK_OFFSET_SCR+off);
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: mips/platform.S */
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/*
+ * Macro for data memory barrier (store/store variant).
+ */
+.macro  SMP_DMB_ST
+#if ANDROID_SMP != 0
+    // FIXME: Is this really needed?
+    sync
+#else
+    /* not SMP */
+#endif
+.endm
+
+/* File: mips/entry.S */
+
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+#define ASSIST_DEBUGGER 1
+
+    .text
+    .align 2
+    .global dvmMterpStdRun
+    .ent dvmMterpStdRun
+    .frame sp, STACK_SIZE, ra
+/*
+ * On entry:
+ *  r0  Thread* self
+ *
+ * The return comes via a call to dvmMterpStdBail().
+ */
+
+dvmMterpStdRun:
+    .set noreorder
+    .cpload t9
+    .set reorder
+/* Save to the stack. Frame size = STACK_SIZE */
+    STACK_STORE_FULL()
+/* This directive will make sure all subsequent jal restore gp at a known offset */
+    .cprestore STACK_OFFSET_GP
+
+    addu      fp, sp, STACK_SIZE           #  Move Frame Pointer to the base of frame
+    /* save stack pointer, add magic word for debuggerd */
+    sw        sp, offThread_bailPtr(a0)      # Save SP
+
+    /* set up "named" registers, figure out entry point */
+    move      rSELF, a0                    #  set rSELF
+    LOAD_PC_FROM_SELF()
+    LOAD_FP_FROM_SELF()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    /* Entry is always a possible trace start */
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()                           #  load rINST from rPC
+    sw        zero, offThread_inJitCodeCache(rSELF)
+#if !defined(WITH_SELF_VERIFICATION)
+    bnez      a0, common_updateProfile     # profiling is enabled
+#else
+    lw       a2, offThread_shadowSpace(rSELF) # to find out the jit exit state
+    beqz     a0, 1f                        # profiling is disabled
+    lw       a3, offShadowSpace_jitExitState(a2) # jit exit state
+    li	     t0, kSVSTraceSelect
+    bne      a3, t0, 2f
+    li       a2, kJitTSelectRequestHot     # ask for trace selection
+    b        common_selectTrace            # go build the trace
+2:
+    li       a4, kSVSNoProfile
+    beq      a3, a4, 1f                    # don't profile the next instruction?
+    b        common_updateProfile          # collect profiles
+#endif
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#else
+    /* start executing the instruction at rPC */
+    FETCH_INST()                           #  load rINST from rPC
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+.Lbad_arg:
+    la        a0, .LstrBadEntryPoint
+    #a1 holds value of entryPoint
+    JAL(printf)
+    JAL(dvmAbort)
+
+    .end dvmMterpStdRun
+
+    .global dvmMterpStdBail
+    .ent dvmMterpStdBail
+
+/* Restore the stack pointer and all the registers stored at sp from the save
+ * point established  on entry. Return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ *   a0    Thread* self
+ */
+dvmMterpStdBail:
+    lw        sp, offThread_bailPtr(a0)      #  Restore sp
+    STACK_LOAD_FULL()
+    jr        ra
+
+    .end dvmMterpStdBail
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOP: /* 0x00 */
+/* File: mips/OP_NOP.S */
+    FETCH_ADVANCE_INST(1)                  #  advance to next instr, load rINST
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)                        #  execute it
+
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    .type dalvik_inst, @function
+dalvik_inst:
+    .ent dalvik_inst
+    .end dalvik_inst
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE: /* 0x01 */
+/* File: mips/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: mips/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_16: /* 0x03 */
+/* File: mips/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: mips/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[B]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[A] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: mips/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 1)                           #  a3 <- BBBB
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: mips/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */
+    FETCH(a3, 2)                           #  a3 <- BBBB
+    FETCH(a2, 1)                           #  a2 <- AAAA
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BBBB]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AAAA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[BBBB]
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AAAA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: mips/OP_MOVE_OBJECT.S */
+/* File: mips/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: mips/OP_MOVE_OBJECT_FROM16.S */
+/* File: mips/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    GET_OPA(a0)                            #  a0 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AA] <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: mips/OP_MOVE_OBJECT_16.S */
+/* File: mips/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    FETCH(a1, 2)                           #  a1 <- BBBB
+    FETCH(a0, 1)                           #  a0 <- AAAA
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_VREG(a2, a1)                       #  a2 <- fp[BBBB]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[AAAA] <- a2 and jump
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: mips/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: mips/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- retval.j
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    STORE64(a0, a1, a2)                    #  fp[AA] <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: mips/OP_MOVE_RESULT_OBJECT.S */
+/* File: mips/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_rSELF_retval(a0)                  #  a0 <- self->retval.i
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a2, t0)              #  fp[AA] <- a0
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: mips/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    LOAD_offThread_exception(a3, rSELF)    #  a3 <- dvmGetException bypass
+    li        a1, 0                        #  a1 <- 0
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    SET_VREG(a3, a2)                       #  fp[AA] <- exception obj
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE_offThread_exception(a1, rSELF)   #  dvmClearException bypass
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: mips/OP_RETURN_VOID.S */
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN: /* 0x0f */
+/* File: mips/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: mips/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[AA]
+    addu      a3, rSELF, offThread_retval  #  a3 <- &self->retval
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vAA/vAA+1
+    STORE64(a0, a1, a3)                    #  retval <- a0/a1
+    b         common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: mips/OP_RETURN_OBJECT.S */
+/* File: mips/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "thread"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a0, a2)                       #  a0 <- vAA
+    sw        a0, offThread_retval(rSELF)  #  retval.i <- vAA
+    b         common_returnFromMethod
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_4: /* 0x12 */
+/* File: mips/OP_CONST_4.S */
+    # const/4 vA,                          /* +B */
+    sll       a1, rINST, 16                #  a1 <- Bxxx0000
+    GET_OPA(a0)                            #  a0 <- A+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sra       a1, a1, 28                   #  a1 <- sssssssB (sign-extended)
+    and       a0, a0, 15
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    SET_VREG_GOTO(a1, a0, t0)              #  fp[A] <- a1
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_16: /* 0x13 */
+/* File: mips/OP_CONST_16.S */
+    # const/16 vAA,                        /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST: /* 0x14 */
+/* File: mips/OP_CONST.S */
+    # const vAA,                           /* +BBBBbbbb */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a1, a1, 16
+    or        a0, a1, a0                   #  a0 <- BBBBbbbb
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: mips/OP_CONST_HIGH16.S */
+    # const/high16 vAA,                    /* +BBBB0000 */
+    FETCH(a0, 1)                           #  a0 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a0, a0, 16                   #  a0 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, a3, t0)              #  vAA <- a0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: mips/OP_CONST_WIDE_16.S */
+    # const-wide/16 vAA,                   /* +BBBB */
+    FETCH_S(a0, 1)                         #  a0 <- ssssBBBB (sign-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: mips/OP_CONST_WIDE_32.S */
+    # const-wide/32 vAA,                   /* +BBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- 0000bbbb (low)
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH_S(a2, 2)                         #  a2 <- ssssBBBB (high)
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    sll       a2, a2, 16
+    or        a0, a0, a2                   #  a0 <- BBBBbbbb
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    sra       a1, a0, 31                   #  a1 <- ssssssss
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: mips/OP_CONST_WIDE.S */
+    # const-wide vAA,                      /* +HHHHhhhhBBBBbbbb */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (low middle)
+    FETCH(a2, 3)                           #  a2 <- hhhh (high middle)
+    sll       a1, 16 #
+    or        a0, a1                       #  a0 <- BBBBbbbb (low word)
+    FETCH(a3, 4)                           #  a3 <- HHHH (high)
+    GET_OPA(t1)                            #  t1 <- AA
+    sll       a3, 16
+    or        a1, a3, a2                   #  a1 <- HHHHhhhh (high word)
+    FETCH_ADVANCE_INST(5)                  #  advance rPC, load rINST
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, t1)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: mips/OP_CONST_WIDE_HIGH16.S */
+    # const-wide/high16 vAA,               /* +BBBB000000000000 */
+    FETCH(a1, 1)                           #  a1 <- 0000BBBB (zero-extended)
+    GET_OPA(a3)                            #  a3 <- AA
+    li        a0, 0                        #  a0 <- 00000000
+    sll       a1, 16                       #  a1 <- BBBB0000
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: mips/OP_CONST_STRING.S */
+    # const/string vAA, String             /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    # not yet resolved?
+    bnez      v0, .LOP_CONST_STRING_resolve
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1:   BBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_STRING_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: mips/OP_CONST_STRING_JUMBO.S */
+    # const/string vAA, String             /* BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (low)
+    FETCH(a1, 2)                           #  a1 <- BBBB (high)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResStrings(a2, a2) #  a2 <- dvmDex->pResStrings
+    sll       a1, a1, 16
+    or        a1, a1, a0                   #  a1 <- BBBBbbbb
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResStrings[BBBB]
+    bnez      v0, .LOP_CONST_STRING_JUMBO_resolve
+
+    /*
+     * Continuation if the String has not yet been resolved.
+     *  a1: BBBBBBBB (String ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveString)                  #  v0 <- String reference
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_STRING_JUMBO_resolve:
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t1)            #  vAA <- v0
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: mips/OP_CONST_CLASS.S */
+    # const/class vAA, Class               /* BBBB */
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- self->methodClassDex
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- dvmDex->pResClasses
+    LOAD_eas2(v0, a2, a1)                  #  v0 <- pResClasses[BBBB]
+
+    bnez      v0, .LOP_CONST_CLASS_resolve      #  v0!=0 => resolved-ok
+    /*
+     * Continuation if the Class has not yet been resolved.
+     *  a1: BBBB (Class ref)
+     *  rOBJ: target register
+     */
+    EXPORT_PC()
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- Class reference
+    # failed==0?
+    beqz      v0, common_exceptionThrown   #  yup, handle the exception
+
+.LOP_CONST_CLASS_resolve:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(v0, rOBJ, t0)            #  vAA <- v0
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: mips/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    move      a0, rSELF                    #  a0 <- self
+    EXPORT_PC()                            #  export PC so we can grab stack trace
+    # null object?
+    beqz      a1, common_errNullObject     #  null object, throw an exception
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    JAL(dvmLockObject)                     #  call(self, obj)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: mips/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    EXPORT_PC()                            #  before fetch: export the PC
+    GET_VREG(a1, a2)                       #  a1 <- vAA (object)
+    # null object?
+    beqz      a1, 1f
+    move      a0, rSELF                    #  a0 <- self
+    JAL(dvmUnlockObject)                   #  v0 <- success for unlock(self, obj)
+    # failed?
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    beqz      v0, common_exceptionThrown   #  yes, exception is pending
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+1:
+    FETCH_ADVANCE_INST(1)                  #  before throw: advance rPC, load rINST
+    b         common_errNullObject
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: mips/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    # check-cast vAA, class                /* BBBB */
+    GET_OPA(a3)                            #  a3 <- AA
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- object
+    LOAD_rSELF_methodClassDex(a0)          #  a0 <- pDvmDex
+    LOAD_base_offDvmDex_pResClasses(a0, a0) #  a0 <- pDvmDex->pResClasses
+    # is object null?
+    beqz      rOBJ, .LOP_CHECK_CAST_okay       #  null obj, cast always succeeds
+    LOAD_eas2(a1, a0, a2)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .LOP_CHECK_CAST_resolve      #  not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+    # same class (trivial success)?
+    bne       a0, a1, .LOP_CHECK_CAST_fullcheck #  no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0 holds obj->clazz
+     *  a1 holds class resolved from BBBB
+     *  rOBJ holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    move      rBIX,a1                      #  avoid ClassObject getting clobbered
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    # failed?
+    bnez      v0, .LOP_CHECK_CAST_okay         #  no, success
+    b         .LOP_CHECK_CAST_castfailure
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: mips/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    # instance-of vA, vB, class            /* CCCC */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- pDvmDex
+    # is object null?
+    beqz      a0, .LOP_INSTANCE_OF_store        #  null obj, not an instance, store a0
+    FETCH(a3, 1)                           #  a3 <- CCCC
+    LOAD_base_offDvmDex_pResClasses(a2, a2) #  a2 <- pDvmDex->pResClasses
+    LOAD_eas2(a1, a2, a3)                  #  a1 <- resolved class
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    # have we resolved this before?
+    beqz      a1, .LOP_INSTANCE_OF_resolve      #  not resolved, do it now
+.LOP_INSTANCE_OF_resolved:                   #  a0=obj->clazz, a1=resolved class
+    # same class (trivial success)?
+    beq       a0, a1, .LOP_INSTANCE_OF_trivial  #  yes, trivial finish
+    b         .LOP_INSTANCE_OF_fullcheck        #  no, do full check
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    li        a0, 1                        #  indicate success
+    # fall thru
+    /*
+     * a0   holds boolean result
+     * rOBJ holds A
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, rOBJ)                     #  vA <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: mips/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+    GET_OPB(a1)                            #  a1 <- B
+    GET_OPA4(a2)                           #  a2 <- A+
+    GET_VREG(a0, a1)                       #  a0 <- vB (object ref)
+    # is object null?
+    beqz      a0, common_errNullObject     #  yup, fail
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- array length
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a3, a2, t0)              #  vA <- length
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: mips/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    # new-instance vAA, class              /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX <- &resolved_class
+#endif
+    EXPORT_PC()                            #  req'd for init, resolve, alloc
+    # already resolved?
+    beqz      a0, .LOP_NEW_INSTANCE_resolve      #  no, resolve it now
+.LOP_NEW_INSTANCE_resolved:                      #  a0=class
+    lbu       a1, offClassObject_status(a0) #  a1 <- ClassStatus enum
+    # has class been initialized?
+    li        t0, CLASS_INITIALIZED
+    move      rOBJ, a0                     #  save a0
+    bne       a1, t0, .LOP_NEW_INSTANCE_needinit #  no, init class now
+
+.LOP_NEW_INSTANCE_initialized:                   #  a0=class
+    LOAD_base_offClassObject_accessFlags(a3, a0) #  a3 <- clazz->accessFlags
+    li        a1, ALLOC_DONT_TRACK         #  flags for alloc call
+    # a0=class
+    JAL(dvmAllocObject)                    #  v0 <- new object
+    GET_OPA(a3)                            #  a3 <- AA
+#if defined(WITH_JIT)
+    /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    lhu       a1, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    and       a1, kSubModeJitTraceBuild    #  under construction?
+    bnez      a1, .LOP_NEW_INSTANCE_jitCheck
+#else
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+#endif
+    b         .LOP_NEW_INSTANCE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: mips/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    FETCH(a2, 1)                           #  a2 <- CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    GET_VREG(a1, a0)                       #  a1 <- vB (array length)
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- resolved class
+    # check length
+    bltz      a1, common_errNegativeArraySize #  negative length, bail - len in a1
+    EXPORT_PC()                            #  req'd for resolve, alloc
+    # already resolved?
+    beqz      a0, .LOP_NEW_ARRAY_resolve
+
+    /*
+     * Finish allocation.
+     *
+     *  a0 holds class
+     *  a1 holds array length
+     */
+.LOP_NEW_ARRAY_finish:
+    li        a2, ALLOC_DONT_TRACK         #  don't track in local refs table
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(clazz, length, flags)
+    GET_OPA4(a2)                           #  a2 <- A+
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle the exception
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(v0, a2)                       #  vA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: mips/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .LOP_FILLED_NEW_ARRAY_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: mips/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: mips/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, type       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResClasses(a3, a3) #  a3 <- pDvmDex->pResClasses
+    EXPORT_PC()                            #  need for resolve and alloc
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved class
+    GET_OPA(rOBJ)                          #  rOBJ <- AA or BA
+    # already resolved?
+    bnez      a0, .LOP_FILLED_NEW_ARRAY_RANGE_continue     #  yes, continue on
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: mips/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       a1, a1, 16                   #  a1 <- BBBBbbbb
+    or        a1, a0, a1                   #  a1 <- BBBBbbbb
+    GET_VREG(a0, a3)                       #  a0 <- vAA (array object)
+    EAS1(a1, rPC, a1)                      #  a1 <- PC + BBBBbbbb*2 (array data off.)
+    EXPORT_PC()
+    JAL(dvmInterpHandleFillArrayData)      #  fill the array with predefined data
+    # 0 means an exception is thrown
+    beqz      v0, common_exceptionThrown   #  has exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_THROW: /* 0x27 */
+/* File: mips/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    GET_OPA(a2)                            #  a2 <- AA
+    GET_VREG(a1, a2)                       #  a1 <- vAA (exception object)
+    EXPORT_PC()                            #  exception handler can throw
+    # null object?
+    beqz      a1, common_errNullObject     #  yes, throw an NPE instead
+    # bypass dvmSetException, just store it
+    STORE_offThread_exception(a1, rSELF)   #  thread->exception <- obj
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO: /* 0x28 */
+/* File: mips/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    sll       a0, rINST, 16                #  a0 <- AAxx0000
+    sra       a1, a0, 24                   #  a1 <- ssssssAA (sign-extended)
+    addu      a2, a1, a1                   #  a2 <- byte offset
+    /* If backwards branch refresh rBASE */
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) check for trace hotness
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO_16: /* 0x29 */
+/* File: mips/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto/16 +AAAA */
+    FETCH_S(a0, 1)                         #  a0 <- ssssAAAA (sign-extended)
+    addu      a1, a0, a0                   #  a1 <- byte offset, flags set
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgez      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bltz      a1, common_testUpdateProfile #  (a0) hot trace head?
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_GOTO_32: /* 0x2a */
+/* File: mips/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     *
+     * Unlike most opcodes, this one is allowed to branch to itself, so
+     * our "backward branch" test must be "<=0" instead of "<0".
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(a0, 1)                           #  a0 <- aaaa (lo)
+    FETCH(a1, 2)                           #  a1 <- AAAA (hi)
+    sll       a1, a1, 16
+    or        a0, a0, a1                   #  a0 <- AAAAaaaa
+    addu      a1, a0, a0                   #  a1 <- byte offset
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    blez      a1, common_testUpdateProfile # (a0) hot trace head?
+#else
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+    bgtz      a0, 2f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+2:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: mips/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL(dvmInterpHandlePackedSwitch)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: mips/OP_SPARSE_SWITCH.S */
+/* File: mips/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * When the JIT is present, all targets are considered treated as
+     * a potential trace heads regardless of branch direction.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    FETCH(a0, 1)                           #  a0 <- bbbb (lo)
+    FETCH(a1, 2)                           #  a1 <- BBBB (hi)
+    GET_OPA(a3)                            #  a3 <- AA
+    sll       t0, a1, 16
+    or        a0, a0, t0                   #  a0 <- BBBBbbbb
+    GET_VREG(a1, a3)                       #  a1 <- vAA
+    EAS1(a0, rPC, a0)                      #  a0 <- PC + BBBBbbbb*2
+    JAL(dvmInterpHandleSparseSwitch)                             #  a0 <- code-unit branch offset
+    addu      a1, v0, v0                   #  a1 <- byte offset
+    bgtz      a1, 1f
+    lw        rIBASE, offThread_curHandlerTable(rSELF) #  refresh handler base
+1:
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bnez      a0, common_updateProfile
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: mips/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, OP_CMPL_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPL_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         OP_CMPL_FLOAT_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPL_FLOAT_finish
+    b         OP_CMPL_FLOAT_nan
+
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: mips/OP_CMPG_FLOAT.S */
+/* File: mips/OP_CMPL_FLOAT.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * The operation we're implementing is:
+     *   if (x == y)
+     *     return 0;
+     *   else if (x < y)
+     *     return -1;
+     *   else if (x > y)
+     *     return 1;
+     *   else
+     *     return {-1,1};  // one or both operands was NaN
+     *
+     * for: cmpl-float, cmpg-float
+     */
+    /* op vAA, vBB, vCC */
+
+    /* "clasic" form */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8
+#ifdef SOFT_FLOAT
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- vBB
+    GET_VREG(rBIX, a3)                     #  rBIX <- vCC
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__eqsf2)                           #  a0 <- (vBB == vCC)
+    li        rTEMP, 0                     # set rTEMP to 0
+    beqz      v0, OP_CMPG_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    JAL(__ltsf2)                           #  a0 <- (vBB < vCC)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPG_FLOAT_finish
+    move      a0, rOBJ                     #  a0 <- vBB
+    move      a1, rBIX                     #  a1 <- vCC
+    b         OP_CMPG_FLOAT_continue
+#else
+    GET_VREG_F(ft0, a2)
+    GET_VREG_F(ft1, a3)
+    c.olt.s   fcc0, ft0, ft1               # Is ft0 < ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    c.olt.s   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    c.eq.s    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPG_FLOAT_finish
+    b         OP_CMPG_FLOAT_nan
+
+#endif
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: mips/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, OP_CMPL_DOUBLE_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPL_DOUBLE_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         OP_CMPL_DOUBLE_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPL_DOUBLE_finish
+    b         OP_CMPL_DOUBLE_nan
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: mips/OP_CMPG_DOUBLE.S */
+/* File: mips/OP_CMPL_DOUBLE.S */
+    /*
+     * Compare two floating-point values.  Puts 0, 1, or -1 into the
+     * destination register based on the results of the comparison.
+     *
+     * Provide a "naninst" instruction that puts 1 or -1 into a1 depending
+     * on what value we'd like to return when one of the operands is NaN.
+     *
+     * See OP_CMPL_FLOAT for an explanation.
+     *
+     * For: cmpl-double, cmpg-double
+     */
+    /* op vAA, vBB, vCC */
+
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       rOBJ, a0, 255                #  s0 <- BB
+    srl       rBIX, a0, 8                  #  t0 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s0 <- &fp[BB]
+    EAS2(rBIX, rFP, rBIX)                  #  t0 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__eqdf2)                           #  cmp <=: C clear if <, Z set if eq
+    li        rTEMP, 0
+    beqz      v0, OP_CMPG_DOUBLE_finish
+
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__ltdf2)
+    li        rTEMP, -1
+    bltz      v0, OP_CMPG_DOUBLE_finish
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vBB/vBB+1
+    b         OP_CMPG_DOUBLE_continue
+#else
+    LOAD64_F(ft0, ft0f, rOBJ)
+    LOAD64_F(ft1, ft1f, rBIX)
+    c.olt.d   fcc0, ft0, ft1
+    li        rTEMP, -1
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    c.olt.d   fcc0, ft1, ft0
+    li        rTEMP, 1
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    c.eq.d    fcc0, ft0, ft1
+    li        rTEMP, 0
+    bc1t      fcc0, OP_CMPG_DOUBLE_finish
+    b         OP_CMPG_DOUBLE_nan
+#endif
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: mips/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values
+     *    x = y     return  0
+     *    x < y     return -1
+     *    x > y     return  1
+     *
+     * I think I can improve on the ARM code by the following observation
+     *    slt   t0,  x.hi, y.hi;	# (x.hi < y.hi) ? 1:0
+     *    sgt   t1,  x.hi, y.hi;	# (y.hi > x.hi) ? 1:0
+     *    subu  v0, t0, t1              # v0= -1:1:0 for [ < > = ]
+     */
+    /* cmp-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, a3)                     #  a2/a3 <- vCC/vCC+1
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    slt       t0, a1, a3                   #  compare hi
+    sgt       t1, a1, a3
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0)
+    bnez      v0, .LOP_CMP_LONG_finish
+    # at this point x.hi==y.hi
+    sltu      t0, a0, a2                   #  compare lo
+    sgtu      t1, a0, a2
+    subu      v0, t1, t0                   #  v0 <- (-1, 1, 0) for [< > =]
+
+.LOP_CMP_LONG_finish:
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_EQ: /* 0x32 */
+/* File: mips/OP_IF_EQ.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bne a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_NE: /* 0x33 */
+/* File: mips/OP_IF_NE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    beq a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LT: /* 0x34 */
+/* File: mips/OP_IF_LT.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bge a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GE: /* 0x35 */
+/* File: mips/OP_IF_GE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    blt a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GT: /* 0x36 */
+/* File: mips/OP_IF_GT.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    ble a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LE: /* 0x37 */
+/* File: mips/OP_IF_LE.S */
+/* File: mips/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    GET_OPA4(a0)                           #  a0 <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a3, a1)                       #  a3 <- vB
+    GET_VREG(a2, a0)                       #  a2 <- vA
+    bgt a2, a3, 1f                  #  branch to 1 if comparison failed
+    FETCH_S(a1, 1)                         #  a1<- branch offset, in code units
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a2, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a2)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a2, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+3:
+    bnez      a0, common_updateProfile
+#else
+    bgez      a2, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rIBASE
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: mips/OP_IF_EQZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bne a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: mips/OP_IF_NEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    beq a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: mips/OP_IF_LTZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bge a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: mips/OP_IF_GEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    blt a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: mips/OP_IF_GTZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    ble a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: mips/OP_IF_LEZ.S */
+/* File: mips/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    GET_OPA(a0)                            #  a0 <- AA
+    GET_VREG(a2, a0)                       #  a2 <- vAA
+    FETCH_S(a1, 1)                         #  a1 <- branch offset, in code units
+    bgt a2, zero, 1f                #  branch to 1 if comparison failed
+    b 2f
+1:
+    li        a1, 2                        #  a1- BYTE branch dist for not-taken
+2:
+    addu      a1, a1, a1                   #  convert to bytes
+    FETCH_ADVANCE_INST_RB(a1)              #  update rPC, load rINST
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    bgez      a1, 3f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh table base
+3:
+    bnez      a0, common_updateProfile     #  test for JIT off at target
+#else
+    bgez      a1, 4f
+    lw        rIBASE, offThread_curHandlerTable(rSELF)  # refresh rtable base
+4:
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: mips/OP_UNUSED_3E.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: mips/OP_UNUSED_3F.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: mips/OP_UNUSED_40.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: mips/OP_UNUSED_41.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: mips/OP_UNUSED_42.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: mips/OP_UNUSED_43.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET: /* 0x44 */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lw a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: mips/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     * Arrays of long/double are 64-bit aligned.
+     */
+    /* aget-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+.LOP_AGET_WIDE_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64_off(a2, a3, a0, offArrayObject_contents)
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a2, a3, rOBJ)                  #  vAA/vAA+1 <- a2/a3
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: mips/OP_AGET_OBJECT.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lw a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: mips/OP_AGET_BOOLEAN.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lbu a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: mips/OP_AGET_BYTE.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lb a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: mips/OP_AGET_CHAR.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lhu a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: mips/OP_AGET_SHORT.S */
+/* File: mips/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    # a1 >= a3; compare unsigned index
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    lh a2, offArrayObject_contents(a0)  #  a2 <- vBB[vCC]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a2, rOBJ, t0)            #  vAA <- a2
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT: /* 0x4b */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 2
+    EASN(a0, a0, a1, 2)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sw a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: mips/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     *
+     * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+     */
+    /* aput-wide vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t0 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    EAS3(a0, a0, a1)                       #  a0 <- arrayObj + index*width
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a2, a3, rOBJ)                   #  a2/a3 <- vAA/vAA+1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64_off(a2, a3, a0, offArrayObject_contents) #  a2/a3 <- vBB[vCC]
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: mips/OP_APUT_OBJECT.S */
+    /*
+     * Store an object into an array.  vBB[vCC] <- vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t1)                            #  t1 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    GET_VREG(rINST, a2)                    #  rINST <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    GET_VREG(rBIX, t1)                     #  rBIX <- vAA
+    # null array object?
+    beqz      rINST, common_errNullObject  #  yes, bail
+
+    LOAD_base_offArrayObject_length(a3, rINST) #  a3 <- arrayObj->length
+    EAS2(rOBJ, rINST, a1)                  #  rOBJ <- arrayObj + index*width
+    # compare unsigned index, length
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    /*
+     * On entry:
+     *  rINST = vBB (arrayObj)
+     *  rBIX = vAA (obj)
+     *  rOBJ = offset into array (vBB + vCC * width)
+     */
+    bnez      rBIX, .LOP_APUT_OBJECT_checks     #  yes, skip type checks
+.LOP_APUT_OBJECT_finish:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    sw        rBIX, offArrayObject_contents(rOBJ) #  vBB[vCC] <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: mips/OP_APUT_BOOLEAN.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sb a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: mips/OP_APUT_BYTE.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 0
+    EASN(a0, a0, a1, 0)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sb a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: mips/OP_APUT_CHAR.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sh a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: mips/OP_APUT_SHORT.S */
+/* File: mips/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA.
+     * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    FETCH_B(a2, 1)                         #  a2 <- BB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    FETCH_C(a3, 1)                         #  a3 <- CC
+    GET_VREG(a0, a2)                       #  a0 <- vBB (array object)
+    GET_VREG(a1, a3)                       #  a1 <- vCC (requested index)
+    # null array object?
+    beqz      a0, common_errNullObject     #  yes, bail
+    LOAD_base_offArrayObject_length(a3, a0) #  a3 <- arrayObj->length
+    .if 1
+    EASN(a0, a0, a1, 1)               #  a0 <- arrayObj + index*width
+    .else
+    addu      a0, a0, a1
+    .endif
+    bgeu      a1, a3, common_errArrayIndex #  index >= length, bail
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a2, rOBJ)                     #  a2 <- vAA
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    sh a2, offArrayObject_contents(a0) #  vBB[vCC] <- a2
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET: /* 0x52 */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_finish
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: mips/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_WIDE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .LOP_IGET_WIDE_finish
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: mips/OP_IGET_OBJECT.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_OBJECT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_OBJECT_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: mips/OP_IGET_BOOLEAN.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_BOOLEAN_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_BOOLEAN_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: mips/OP_IGET_BYTE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_BYTE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_BYTE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: mips/OP_IGET_CHAR.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_CHAR_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_CHAR_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: mips/OP_IGET_SHORT.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_SHORT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_SHORT_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT: /* 0x59 */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: mips/OP_IPUT_WIDE.S */
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_WIDE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_WIDE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: mips/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_OBJECT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_OBJECT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: mips/OP_IPUT_BOOLEAN.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_BOOLEAN_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_BOOLEAN_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: mips/OP_IPUT_BYTE.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_BYTE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_BYTE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: mips/OP_IPUT_CHAR.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_CHAR_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_CHAR_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: mips/OP_IPUT_SHORT.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_SHORT_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_SHORT_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET: /* 0x60 */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: mips/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_SGET_WIDE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .LOP_SGET_WIDE_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: mips/OP_SGET_OBJECT.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_OBJECT_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_OBJECT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: mips/OP_SGET_BOOLEAN.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_BOOLEAN_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_BOOLEAN_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: mips/OP_SGET_BYTE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_BYTE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_BYTE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: mips/OP_SGET_CHAR.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_CHAR_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_CHAR_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: mips/OP_SGET_SHORT.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_SHORT_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_SHORT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT: /* 0x67 */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_finish            # resume
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: mips/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .LOP_SPUT_WIDE_resolve      #  yes, do resolve
+.LOP_SPUT_WIDE_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if 0
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: mips/OP_SPUT_OBJECT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_OBJECT_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .LOP_SPUT_OBJECT_finish             # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: mips/OP_SPUT_BOOLEAN.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_BOOLEAN_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_BOOLEAN_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: mips/OP_SPUT_BYTE.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_BYTE_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_BYTE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: mips/OP_SPUT_CHAR.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_CHAR_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_CHAR_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: mips/OP_SPUT_SHORT.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_SHORT_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_SHORT_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: mips/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!0)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .LOP_INVOKE_VIRTUAL_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_INVOKE_VIRTUAL_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: mips/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!0)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .LOP_INVOKE_SUPER_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_INVOKE_SUPER_continue
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: mips/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!0)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethodNoRange #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: mips/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethodNoRange #  yes, continue on
+    b         .LOP_INVOKE_STATIC_resolve
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: mips/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!0)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: mips/OP_UNUSED_73.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: mips/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: mips/OP_INVOKE_VIRTUAL.S */
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    .if (!1)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, .LOP_INVOKE_VIRTUAL_RANGE_continue     #  yes, continue on
+
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_INVOKE_VIRTUAL_RANGE_continue     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: mips/OP_INVOKE_SUPER_RANGE.S */
+/* File: mips/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    .if (!1)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this" ptr
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved baseMethod
+    # null "this"?
+    LOAD_rSELF_method(t1)                  #  t1 <- current method
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    # cmp a0, 0; already resolved?
+    LOAD_base_offMethod_clazz(rBIX, t1)    #  rBIX <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    bnez      a0, .LOP_INVOKE_SUPER_RANGE_continue     #  resolved, continue on
+
+    move      a0, rBIX                     #  a0 <- method->clazz
+    li        a2, METHOD_VIRTUAL           #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         .LOP_INVOKE_SUPER_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: mips/OP_INVOKE_DIRECT_RANGE.S */
+/* File: mips/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    FETCH(rBIX, 2)                         #  rBIX <- GFED or CCCC
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+    .if (!1)
+    and       rBIX, rBIX, 15               #  rBIX <- D (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    # already resolved?
+    bnez      a0, 1f                       #  resolved, call the function
+
+    lw        a3, offThread_method(rSELF)  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_DIRECT            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+
+1:
+    bnez      rOBJ, common_invokeMethodRange #  a0=method, rOBJ="this"
+    b         common_errNullObject         #  yes, throw exception
+
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: mips/OP_INVOKE_STATIC_RANGE.S */
+/* File: mips/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op {vCCCC..v(CCCC+AA-1)}, meth       /* BBBB */
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- pDvmDex
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offDvmDex_pResMethods(a3, a3) #  a3 <- pDvmDex->pResMethods
+    li      rOBJ, 0                        #  null "this" in delay slot
+    LOAD_eas2(a0, a3, a1)                  #  a0 <- resolved methodToCall
+#if defined(WITH_JIT)
+    EAS2(rBIX, a3, a1)                     #  rBIX<- &resolved_metherToCall
+#endif
+    EXPORT_PC()                            #  must export for invoke
+    # already resolved?
+    bnez      a0, common_invokeMethodRange #  yes, continue on
+    b         .LOP_INVOKE_STATIC_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: mips/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: mips/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    FETCH(a2, 2)                           #  a2 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!1)
+    and       a2, 15                       #  a2 <- C (or stays CCCC)
+    .endif
+    EXPORT_PC()                            #  must export for invoke
+    GET_VREG(rOBJ, a2)                     #  rOBJ <- first arg ("this")
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- methodClassDex
+    LOAD_rSELF_method(a2)                  #  a2 <- method
+    # null obj?
+    beqz      rOBJ, common_errNullObject   #  yes, fail
+    LOAD_base_offObject_clazz(a0, rOBJ)      #  a0 <- thisPtr->clazz
+    JAL(dvmFindInterfaceMethodInCache)     #  v0 <- call(class, ref, method, dex)
+    move      a0, v0
+    # failed?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: mips/OP_UNUSED_79.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: mips/OP_UNUSED_7A.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_INT: /* 0x7b */
+/* File: mips/OP_NEG_INT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    negu a0, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOT_INT: /* 0x7c */
+/* File: mips/OP_NOT_INT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    not a0, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: mips/OP_NEG_LONG.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    negu v0, a0                              #  optional op
+    negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: mips/OP_NOT_LONG.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    not a0, a0                              #  optional op
+    not a1, a1                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: mips/OP_NEG_FLOAT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a0, a0, 0x80000000                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: mips/OP_NEG_DOUBLE.S */
+/* File: mips/unopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be MIPS instruction or a function call.
+     *
+     * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vAA
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a1, a1, 0x80000000                                 #  a0/a1 <- op, a2-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: mips/OP_INT_TO_LONG.S */
+/* File: mips/unopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-long, int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(t1)                           #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    EAS2(rOBJ, rFP, t1)                    #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    sra a1, a0, 31                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vA/vA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: mips/OP_INT_TO_FLOAT.S */
+/* File: mips/unflop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+                                  #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    JAL(__floatsisf)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_INT_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    cvt.s.w fv0, fa0
+
+.LOP_INT_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: mips/OP_INT_TO_DOUBLE.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__floatsidf)                                 #  result <- op, a0-a3 changed
+
+.LOP_INT_TO_DOUBLE_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    cvt.d.w fv0, fa0
+
+.LOP_INT_TO_DOUBLE_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: mips/OP_LONG_TO_INT.S */
+    GET_OPB(a1)                            #  a1 <- B from 15:12
+    GET_OPA4(a0)                           #  a0 <- A from 11:8
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef HAVE_BIG_ENDIAN
+    addu      a1, a1, 1
+#endif
+    GET_VREG(a2, a1)                       #  a2 <- fp[B]
+    GET_INST_OPCODE(t0)                    #  t0 <- opcode from rINST
+    SET_VREG_GOTO(a2, a0, t0)              #  fp[A] <- a2
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: mips/OP_LONG_TO_FLOAT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64(rARG0, rARG1, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__floatdisf)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_LONG_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    JAL(__floatdisf)
+
+.LOP_LONG_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: mips/OP_LONG_TO_DOUBLE.S */
+/* File: mips/unflopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    LOAD64(rARG0, rARG1, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    JAL(__floatdidf)                                 #  a0/a1 <- op, a2-a3 changed
+
+.LOP_LONG_TO_DOUBLE_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: mips/OP_FLOAT_TO_INT.S */
+/* File: mips/unflop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: int-to-float, float-to-int
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t0 <- A+
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+                                  #  optional op
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+#ifdef SOFT_FLOAT
+    b f2i_doconv                                 #  a0 <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_INT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vAA <- result0
+#else
+    b f2i_doconv
+
+.LOP_FLOAT_TO_INT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)
+#endif
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: mips/OP_FLOAT_TO_LONG.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    b f2l_doconv                                 #  result <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_LONG_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    b f2l_doconv
+
+.LOP_FLOAT_TO_LONG_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: mips/OP_FLOAT_TO_DOUBLE.S */
+/* File: mips/unflopWider.S */
+    /*
+     * Generic 32bit-to-64bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0", where
+     * "result" is a 64-bit quantity in a0/a1.
+     *
+     * For: int-to-double, float-to-long, float-to-double
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, a3)                       #  a0 <- vB
+#else
+    GET_VREG_F(fa0, a3)
+#endif
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__extendsfdf2)                                 #  result <- op, a0-a3 changed
+
+.LOP_FLOAT_TO_DOUBLE_set_vreg:
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vA/vA+1 <- a0/a1
+#else
+    cvt.d.s fv0, fa0
+
+.LOP_FLOAT_TO_DOUBLE_set_vreg:
+    STORE64_F(fv0, fv0f, rOBJ)                             #  vA/vA+1 <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: mips/OP_DOUBLE_TO_INT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    b d2i_doconv                                 #  a0 <- op, a0-a3 changed
+
+.LOP_DOUBLE_TO_INT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    b d2i_doconv
+
+.LOP_DOUBLE_TO_INT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+/*
+ * Convert the double in a0/a1 to an int in a0.
+ *
+ * We have to clip values to int min/max per the specification.  The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer.  The EABI convert function isn't doing this for us.
+ * Use rBIX / rTEMP as global to hold arguments (they are not bound to a global var)
+ */
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: mips/OP_DOUBLE_TO_LONG.S */
+/* File: mips/unflopWide.S */
+    /*
+     * Generic 64-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0/a1".
+     * This could be a MIPS instruction or a function call.
+     *
+     * long-to-double, double-to-long
+     */
+    /* unop vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  t1 <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vAA
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    b d2l_doconv                                 #  a0/a1 <- op, a2-a3 changed
+
+.LOP_DOUBLE_TO_LONG_set_vreg:
+#ifdef SOFT_FLOAT
+    STORE64(rRESULT0, rRESULT1, rOBJ)      #  vAA <- a0/a1
+#else
+    STORE64(rRESULT0, rRESULT1, rOBJ)                             #  vAA <- a0/a1
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: mips/OP_DOUBLE_TO_FLOAT.S */
+/* File: mips/unopNarrower.S */
+    /*
+     * Generic 64bit-to-32bit unary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = op a0/a1", where
+     * "result" is a 32-bit quantity in a0.
+     *
+     * For: long-to-float, double-to-int, double-to-float
+     * If hard floating point support is available, use fa0 as the parameter, except for
+     * long-to-float opcode.
+     * (This would work for long-to-int, but that instruction is actually
+     * an exact match for OP_MOVE.)
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[B]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a3)               #  a0/a1 <- vB/vB+1
+#else
+    LOAD64_F(fa0, fa0f, a3)
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__truncdfsf2)                                 #  a0 <- op, a0-a3 changed
+
+.LOP_DOUBLE_TO_FLOAT_set_vreg:
+    SET_VREG(v0, rOBJ)                     #  vA <- result0
+#else
+    cvt.s.d fv0, fa0
+
+.LOP_DOUBLE_TO_FLOAT_set_vreg_f:
+    SET_VREG_F(fv0, rOBJ)                  #  vA <- result0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-11 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: mips/OP_INT_TO_BYTE.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sll a0, a0, 24                              #  optional op
+    sra a0, a0, 24                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: mips/OP_INT_TO_CHAR.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+    and a0, 0xffff                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: mips/OP_INT_TO_SHORT.S */
+/* File: mips/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op a0".
+     * This could be a MIPS instruction or a function call.
+     *
+     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+     *      int-to-byte, int-to-char, int-to-short
+     */
+    /* unop vA, vB */
+    GET_OPB(a3)                            #  a3 <- B
+    GET_OPA4(t0)                           #  t0 <- A+
+    GET_VREG(a0, a3)                       #  a0 <- vB
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    sll a0, 16                              #  optional op
+    sra a0, 16                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, t0, t1)        #  vAA <- result0
+    /* 9-10 instructions */
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT: /* 0x90 */
+/* File: mips/OP_ADD_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_INT: /* 0x91 */
+/* File: mips/OP_SUB_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    subu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT: /* 0x92 */
+/* File: mips/OP_MUL_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT: /* 0x93 */
+/* File: mips/OP_DIV_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT: /* 0x94 */
+/* File: mips/OP_REM_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT: /* 0x95 */
+/* File: mips/OP_AND_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT: /* 0x96 */
+/* File: mips/OP_OR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT: /* 0x97 */
+/* File: mips/OP_XOR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT: /* 0x98 */
+/* File: mips/OP_SHL_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT: /* 0x99 */
+/* File: mips/OP_SHR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT: /* 0x9a */
+/* File: mips/OP_USHR_INT.S */
+/* File: mips/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0 op a1".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.  Note that we
+     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+     * handles it correctly.
+     *
+     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: mips/OP_ADD_LONG.S */
+/*
+ *  The compiler generates the following sequence for
+ *  [v1 v0] =  [a1 a0] + [a3 a2];
+ *    addu v0,a2,a0
+ *    addu a1,a3,a1
+ *    sltu v1,v0,a2
+ *    addu v1,v1,a1
+ */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    addu v0, a2, a0                              #  optional op
+    addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: mips/OP_SUB_LONG.S */
+/*
+ * For little endian the code sequence looks as follows:
+ *    subu    v0,a0,a2
+ *    subu    v1,a1,a3
+ *    sltu    a0,a0,v0
+ *    subu    v1,v1,a0
+ */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    subu v0, a0, a2                              #  optional op
+    subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: mips/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *         a1   a0
+     *   x     a3   a2
+     *   -------------
+     *       a2a1 a2a0
+     *       a3a0
+     *  a3a1 (<= unused)
+     *  ---------------
+     *         v1   v0
+     */
+    /* mul-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    and       t0, a0, 255                  #  a2 <- BB
+    srl       t1, a0, 8                    #  a3 <- CC
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[BB]
+    LOAD64(a0, a1, t0)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t1, rFP, t1)                      #  t0 <- &fp[CC]
+    LOAD64(a2, a3, t1)                     #  a2/a3 <- vCC/vCC+1
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t0, a2, a1                   #  t0= a2a1
+    addu      v1, v1, t1                   #  v1+= hi(a2a0)
+    addu      v1, v1, t0                   #  v1= a3a0 + a2a1;
+
+    GET_OPA(a0)                            #  a0 <- AA
+    EAS2(a0, rFP, a0)                      #  a0 <- &fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    b         .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: mips/OP_DIV_LONG.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#else
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a1, a0, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a3, a2, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_LONG: /* 0x9f */
+/* File: mips/OP_REM_LONG.S */
+/* ldivmod returns quotient in a0/a1 and remainder in a2/a3 */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#else
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a1, a0, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a3, a2, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: mips/OP_AND_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a0, a0, a2                              #  optional op
+    and a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: mips/OP_OR_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    or a0, a0, a2                              #  optional op
+    or a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: mips/OP_XOR_LONG.S */
+/* File: mips/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+    LOAD64(a0, a1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(a2, a3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    xor a0, a0, a2                              #  optional op
+    xor a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: mips/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shl-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t2)                            #  t2 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: mips/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* shr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t3)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[AA]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    STORE64(v0, v1, t3)                    #  vAA/VAA+1 <- v0/v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: mips/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.
+     */
+    /* ushr-long vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(t0)                            #  t3 <- AA
+    and       a3, a0, 255                  #  a3 <- BB
+    srl       a0, a0, 8                    #  a0 <- CC
+    EAS2(a3, rFP, a3)                      #  a3 <- &fp[BB]
+    GET_VREG(a2, a0)                       #  a2 <- vCC
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- vBB/vBB+1
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ <- &fp[AA]
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- v0/v1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: mips/OP_ADD_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__addsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    add.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: mips/OP_SUB_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    sub.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: mips/OP_MUL_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__mulsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    mul.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: mips/OP_DIV_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divsf3)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    div.s fv0, fa0, fa1                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: mips/OP_REM_FLOAT.S */
+/* File: mips/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    srl       a3, a0, 8                    #  a3 <- CC
+    and       a2, a0, 255                  #  a2 <- BB
+#ifdef SOFT_FLOAT
+    GET_VREG(a1, a3)                       #  a1 <- vCC
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa1, a3)                    #  a1 <- vCC
+    GET_VREG_F(fa0, a2)                    #  a0 <- vBB
+
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1               #  condition bit and comparision with 0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmodf)                                 #  v0 = result
+    SET_VREG(v0, rOBJ)                     #  vAA <- v0
+#else
+    JAL(fmodf)                               #  f0 = result
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- fv0
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 11-14 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: mips/OP_ADD_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__adddf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: mips/OP_SUB_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: mips/OP_MUL_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__muldf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: mips/OP_DIV_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: mips/OP_REM_DOUBLE.S */
+/* File: mips/binflopWide.S */
+    /*
+     * Generic 64-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+     *      xor-long, add-double, sub-double, mul-double, div-double,
+     *      rem-double
+     *
+     * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+     */
+    /* binop vAA, vBB, vCC */
+    FETCH(a0, 1)                           #  a0 <- CCBB
+    GET_OPA(rOBJ)                          #  s5 <- AA
+    and       a2, a0, 255                  #  a2 <- BB
+    srl       a3, a0, 8                    #  a3 <- CC
+    EAS2(rOBJ, rFP, rOBJ)                  #  s5 <- &fp[AA]
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[BB]
+    EAS2(t1, rFP, a3)                      #  a3 <- &fp[CC]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG0, rARG1, a2)               #  a0/a1 <- vBB/vBB+1
+    LOAD64(rARG2, rARG3, t1)               #  a2/a3 <- vCC/vCC+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, a2)
+    LOAD64_F(fa1, fa1f, t1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmod)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    JAL(fmod)
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 14-17 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: mips/OP_ADD_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: mips/OP_SUB_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    subu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: mips/OP_MUL_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: mips/OP_DIV_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: mips/OP_REM_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: mips/OP_AND_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: mips/OP_OR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: mips/OP_XOR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: mips/OP_SHL_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: mips/OP_SHR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: mips/OP_USHR_INT_2ADDR.S */
+/* File: mips/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                  #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: mips/OP_ADD_LONG_2ADDR.S */
+/*
+ *See OP_ADD_LONG.S for details
+ */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    addu v0, a2, a0                              #  optional op
+    addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: mips/OP_SUB_LONG_2ADDR.S */
+/*
+ * See comments in OP_SUB_LONG.S
+ */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    subu v0, a0, a2                              #  optional op
+    subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: mips/OP_MUL_LONG_2ADDR.S */
+    /*
+     * See comments in OP_MUL_LONG.S
+     */
+    /* mul-long/2addr vA, vB */
+    GET_OPA4(t0)                           #  t0 <- A+
+
+    EAS2(t0, rFP, t0)                      #  t0 <- &fp[A]
+    LOAD64(a0, a1, t0)                     #  vAA.low / high
+
+    GET_OPB(t1)                            #  t1 <- B
+    EAS2(t1, rFP, t1)                      #  t1 <- &fp[B]
+    LOAD64(a2, a3, t1)                     #  vBB.low / high
+
+    mul       v1, a3, a0                   #  v1= a3a0
+    multu     a2, a0
+    mfhi      t1
+    mflo      v0                           #  v0= a2a0
+    mul       t2, a2, a1                   #  t2= a2a1
+    addu      v1, v1, t1                   #  v1= a3a0 + hi(a2a0)
+    addu      v1, v1, t2                   #  v1= v1 + a2a1;
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t1)                    #  extract opcode from rINST
+    # vAA <- v0 (low)
+    STORE64(v0, v1, t0)                    #  vAA+1 <- v1 (high)
+    GOTO_OPCODE(t1)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: mips/OP_DIV_LONG_2ADDR.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#else
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a3, a2, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a1, a0, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__divdi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: mips/OP_REM_LONG_2ADDR.S */
+#ifdef HAVE_LITTLE_ENDIAN
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)      #  vAA/vAA+1 <- v0/v1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#else
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a3, a2, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a1, a0, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 1
+    or        t0, a3, a2             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    JAL(__moddi3)                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v1, v0, rOBJ)      #  vAA/vAA+1 <- v1/v0
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+#endif
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: mips/OP_AND_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    and a0, a0, a2                              #  optional op
+    and a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: mips/OP_OR_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    or a0, a0, a2                              #  optional op
+    or a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: mips/OP_XOR_LONG_2ADDR.S */
+/* File: mips/binopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be a MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+     *      and-long/2addr, or-long/2addr, xor-long/2addr
+     *      rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+    LOAD64(a2, a3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(a0, a1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, a2, a3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    xor a0, a0, a2                              #  optional op
+    xor a1, a1, a3                                 #  result <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, rOBJ)      #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: mips/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(rOBJ, rFP, t2)                    #  rOBJ <- &fp[A]
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sll     v0, a0, a2                     #  rlo<- alo << (shift&31)
+    not     v1, a2                         #  rhi<- 31-shift  (shift is 5b)
+    srl     a0, 1
+    srl     a0, v1                         #  alo<- alo >> (32-(shift&31))
+    sll     v1, a1, a2                     #  rhi<- ahi << (shift&31)
+    or      v1, a0                         #  rhi<- rhi | alo
+    andi    a2, 0x20                       #  shift< shift & 0x20
+    movn    v1, v0, a2                     #  rhi<- rlo (if shift&0x20)
+    movn    v0, zero, a2                   #  rlo<- 0  (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, rOBJ)                  #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: mips/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shr-long/2addr vA, vB */
+    GET_OPA4(t2)                           #  t2 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t2, rFP, t2)                      #  t2 <- &fp[A]
+    LOAD64(a0, a1, t2)                     #  a0/a1 <- vAA/vAA+1
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+
+    sra     v1, a1, a2                     #  rhi<- ahi >> (shift&31)
+    srl     v0, a0, a2                     #  rlo<- alo >> (shift&31)
+    sra     a3, a1, 31                     #  a3<- sign(ah)
+    not     a0, a2                         #  alo<- 31-shift (shift is 5b)
+    sll     a1, 1
+    sll     a1, a0                         #  ahi<- ahi << (32-(shift&31))
+    or      v0, a1                         #  rlo<- rlo | ahi
+    andi    a2, 0x20                       #  shift & 0x20
+    movn    v0, v1, a2                     #  rlo<- rhi (if shift&0x20)
+    movn    v1, a3, a2                     #  rhi<- sign(ahi) (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t2)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: mips/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* ushr-long/2addr vA, vB */
+    GET_OPA4(t3)                           #  t3 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a2, a3)                       #  a2 <- vB
+    EAS2(t3, rFP, t3)                      #  t3 <- &fp[A]
+    LOAD64(a0, a1, t3)                     #  a0/a1 <- vAA/vAA+1
+
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+    srl       v1, a1, a2                   #  rhi<- ahi >> (shift&31)
+    srl       v0, a0, a2                   #  rlo<- alo >> (shift&31)
+    not       a0, a2                       #  alo<- 31-n  (shift is 5b)
+    sll       a1, 1
+    sll       a1, a0                       #  ahi<- ahi << (32-(shift&31))
+    or        v0, a1                       #  rlo<- rlo | ahi
+    andi      a2, 0x20                     #  shift & 0x20
+    movn      v0, v1, a2                   #  rlo<- rhi (if shift&0x20)
+    movn      v1, zero, a2                 #  rhi<- 0 (if shift&0x20)
+
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, t3)                    #  vAA/vAA+1 <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: mips/OP_ADD_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__addsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    add.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: mips/OP_SUB_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    sub.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: mips/OP_MUL_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__mulsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    mul.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: mips/OP_DIV_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divsf3)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    div.s fv0, fa0, fa1
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: mips/OP_REM_FLOAT_2ADDR.S */
+/* File: mips/binflop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" and
+     * "instr_f" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-float/2addr, sub-float/2addr, mul-float/2addr,
+     * div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  t1 <- A+
+    GET_OPB(a3)                            #  a3 <- B
+#ifdef SOFT_FLOAT
+    GET_VREG(a0, rOBJ)                     #  a0 <- vA
+    GET_VREG(a1, a3)                       #  a1 <- vB
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+#else
+    GET_VREG_F(fa0, rOBJ)
+    GET_VREG_F(fa1, a3)
+    .if 0
+    # is second operand zero?
+    li.s      ft0, 0
+    c.eq.s    fcc0, ft0, fa1
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmodf)                                 #  result <- op, a0-a3 changed
+    SET_VREG(v0, rOBJ)                     #  vAA <- result
+#else
+    JAL(fmodf)
+    SET_VREG_F(fv0, rOBJ)                  #  vAA <- result
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: mips/OP_ADD_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__adddf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    add.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: mips/OP_SUB_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__subdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    sub.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: mips/OP_MUL_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__muldf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    mul.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: mips/OP_DIV_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(__divdf3)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    div.d fv0, fa0, fa1
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: mips/OP_REM_DOUBLE_2ADDR.S */
+/* File: mips/binflopWide2addr.S */
+    /*
+     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0-a1 op a2-a3".
+     * This could be an MIPS instruction or a function call.
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+     *  div-double/2addr, rem-double/2addr
+     */
+    /* binop/2addr vA, vB */
+    GET_OPA4(rOBJ)                         #  rOBJ <- A+
+    GET_OPB(a1)                            #  a1 <- B
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[B]
+    EAS2(rOBJ, rFP, rOBJ)                  #  rOBJ <- &fp[A]
+#ifdef SOFT_FLOAT
+    LOAD64(rARG2, rARG3, a1)               #  a2/a3 <- vBB/vBB+1
+    LOAD64(rARG0, rARG1, rOBJ)             #  a0/a1 <- vAA/vAA+1
+    .if 0
+    or        t0, rARG2, rARG3             #  second arg (a2-a3) is zero?
+    beqz      t0, common_errDivideByZero
+    .endif
+#else
+    LOAD64_F(fa0, fa0f, rOBJ)
+    LOAD64_F(fa1, fa1f, a1)
+    .if 0
+    li.d      ft0, 0
+    c.eq.d    fcc0, fa1, ft0
+    bc1t      fcc0, common_errDivideByZero
+    .endif
+#endif
+1:
+    FETCH_ADVANCE_INST(1)                  #  advance rPC, load rINST
+                                  #  optional op
+#ifdef SOFT_FLOAT
+    JAL(fmod)                                 #  result <- op, a0-a3 changed
+    STORE64(rRESULT0, rRESULT1, rOBJ)
+#else
+    JAL(fmod)
+    STORE64_F(fv0, fv0f, rOBJ)
+#endif
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+    /* 12-15 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: mips/OP_ADD_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: mips/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    subu a0, a1, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: mips/OP_MUL_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: mips/OP_DIV_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 1
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: mips/OP_REM_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 1
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: mips/OP_AND_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: mips/OP_OR_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: mips/OP_XOR_INT_LIT16.S */
+/* File: mips/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    # binop/lit16 vA, vB,                  /* +CCCC */
+    FETCH_S(a1, 1)                         #  a1 <- ssssCCCC (sign-extended)
+    GET_OPB(a2)                            #  a2 <- B
+    GET_OPA(rOBJ)                          #  rOBJ <- A+
+    GET_VREG(a0, a2)                       #  a0 <- vB
+    and       rOBJ, rOBJ, 15
+    .if 0
+    # cmp a1, 0; is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-13 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: mips/OP_ADD_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    addu a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: mips/OP_RSUB_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    subu a0, a1, a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: mips/OP_MUL_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    mul a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: mips/OP_DIV_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mflo a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: mips/OP_REM_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 1
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    div zero, a0, a1; mfhi a0                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: mips/OP_AND_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    and a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: mips/OP_OR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    or a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: mips/OP_XOR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+                                  #  optional op
+    xor a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: mips/OP_SHL_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sll a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: mips/OP_SHR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    sra a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: mips/OP_USHR_INT_LIT8.S */
+/* File: mips/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = a0 op a1".
+     * This could be an MIPS instruction or a function call.  (If the result
+     * comes back in a register other than a0, you can override "result".)
+     *
+     * If "chkzero" is set to 1, we perform a divide-by-zero check on
+     * vCC (a1).  Useful for integer division and modulus.
+     *
+     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    # binop/lit8 vAA, vBB,                 /* +CC */
+    FETCH_S(a3, 1)                         #  a3 <- ssssCCBB (sign-extended for CC)
+    GET_OPA(rOBJ)                          #  rOBJ <- AA
+    and       a2, a3, 255                  #  a2 <- BB
+    GET_VREG(a0, a2)                       #  a0 <- vBB
+    sra       a1, a3, 8                    #  a1 <- ssssssCC (sign extended)
+    .if 0
+    # is second operand zero?
+    beqz      a1, common_errDivideByZero
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+
+    and a1, a1, 31                              #  optional op
+    srl a0, a0, a1                                 #  a0 <- op, a0-a3 changed
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a0, rOBJ, t0)       #  vAA <- a0
+    /* 10-12 instructions */
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: mips/OP_IGET_VOLATILE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: mips/OP_IPUT_VOLATILE.S */
+/* File: mips/OP_IPUT.S */
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: mips/OP_SGET_VOLATILE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: mips/OP_SPUT_VOLATILE.S */
+/* File: mips/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_VOLATILE_finish       #  is resolved entry null?
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SPUT_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: mips/OP_IGET_OBJECT_VOLATILE.S */
+/* File: mips/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_OBJECT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test results
+    move      a0, v0
+    bnez      v0, .LOP_IGET_OBJECT_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: mips/OP_IGET_WIDE_VOLATILE.S */
+/* File: mips/OP_IGET_WIDE.S */
+    /*
+     * Wide 32-bit instance field get.
+     */
+    # iget-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IGET_WIDE_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # test return code
+    move      a0, v0
+    bnez      v0, .LOP_IGET_WIDE_VOLATILE_finish
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: mips/OP_IPUT_WIDE_VOLATILE.S */
+/* File: mips/OP_IPUT_WIDE.S */
+    # iput-wide vA, vB, field              /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_WIDE_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_WIDE_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: mips/OP_SGET_WIDE_VOLATILE.S */
+/* File: mips/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    # sget-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_SGET_WIDE_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in v0.
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+
+    b        .LOP_SGET_WIDE_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: mips/OP_SPUT_WIDE_VOLATILE.S */
+/* File: mips/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    # sput-wide vAA, field                 /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    GET_OPA(t0)                            #  t0 <- AA
+    LOAD_eas2(a2, rBIX, a1)                #  a2 <- resolved StaticField ptr
+    EAS2(rOBJ, rFP, t0)                    #  rOBJ<- &fp[AA]
+    # is resolved entry null?
+    beqz      a2, .LOP_SPUT_WIDE_VOLATILE_resolve      #  yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish:                        #  field ptr in a2, AA in rOBJ
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- vAA/vAA+1
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    .if 1
+    addu    a2, offStaticField_value       #  a2<- pointer to data
+    JAL(dvmQuasiAtomicSwap64Sync)          #  stores a0/a1 into addr a2
+    .else
+    STORE64_off(a0, a1, a2, offStaticField_value) #  field <- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_BREAKPOINT: /* 0xec */
+    /* (stub) */
+    SAVE_PC_TO_SELF()            # only need to export PC and FP
+    SAVE_FP_TO_SELF()
+    move        a0, rSELF        # self is first arg to function
+    JAL(dvmMterp_OP_BREAKPOINT)      # call
+    LOAD_PC_FROM_SELF()          # retrieve updated values
+    LOAD_FP_FROM_SELF()
+    FETCH_INST()                 # load next instruction from rPC
+    GET_INST_OPCODE(t0)          # ...trim down to just the opcode
+    GOTO_OPCODE(t0)              # ...and jump to the handler
+/* ------------------------------ */
+    .balign 128
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: mips/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    FETCH(a2, 1)                           #  a2 <- BBBB
+    EXPORT_PC()                            #  export the PC
+    GET_OPA(a1)                            #  a1 <- AA
+    JAL(dvmThrowVerificationError)         #  always throws
+    b         common_exceptionThrown       #  handle exception
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: mips/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     *
+     * TUNING: could maintain two tables, pointer in Thread and
+     * swap if profiler/debuggger active.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                         #  rBIX <- BBBB
+    EXPORT_PC()                            #  can throw
+    and       a2, kSubModeDebugProfile     #  Any going on?
+    bnez      a2, .LOP_EXECUTE_INLINE_debugmode    #  yes - take slow path
+.LOP_EXECUTE_INLINE_resume:
+    addu      a1, rSELF, offThread_retval  #  a1 <- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    # test boolean result of inline
+    beqz      v0, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: mips/OP_EXECUTE_INLINE_RANGE.S */
+    /*
+     * Execute a "native inline" instruction, using "/range" semantics.
+     * Same idea as execute-inline, but we get the args differently.
+     *
+     * We need to call an InlineOp4Func:
+     *  bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+     *
+     * The first four args are in a0-a3, pointer to return value storage
+     * is on the stack.  The function's return value is a flag that tells
+     * us if an exception was thrown.
+     */
+    /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+    lhu       a2, offThread_subMode(rSELF)
+    FETCH(rBIX, 1)                       # rBIX<- BBBB
+    EXPORT_PC()                          # can throw
+    and       a2, kSubModeDebugProfile   # Any going on?
+    bnez      a2, .LOP_EXECUTE_INLINE_RANGE_debugmode  # yes - take slow path
+.LOP_EXECUTE_INLINE_RANGE_resume:
+    addu      a1, rSELF, offThread_retval # a1<- &self->retval
+    GET_OPA(a0)
+    sw        a1, STACK_OFFSET_ARG04(sp)  # push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_RANGE_continue)             # make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)     #  restore gp
+    beqz      v0, common_exceptionThrown  # returned false, handle exception
+    FETCH_ADVANCE_INST(3)                 # advance rPC, load rINST
+    GET_INST_OPCODE(t0)                   # extract opcode from rINST
+    GOTO_OPCODE(t0)                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: mips/OP_INVOKE_OBJECT_INIT_RANGE.S */
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    FETCH(a1, 2)                  # a1<- CCCC
+    GET_VREG(a0, a1)                    # a0<- "this" ptr
+    # check for NULL
+    beqz    a0, common_errNullObject    # export PC and throw NPE
+    LOAD_base_offObject_clazz(a1, a0)   # a1<- obj->clazz
+    LOAD_base_offClassObject_accessFlags(a2, a1) # a2<- clazz->accessFlags
+    and     a2, CLASS_ISFINALIZABLE     # is this class finalizable?
+    beqz    a2, .LOP_INVOKE_OBJECT_INIT_RANGE_finish      # no, go
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_setFinal:
+    EXPORT_PC()                         # can throw
+    JAL(dvmSetFinalizable)              # call dvmSetFinalizable(obj)
+    LOAD_offThread_exception(a0, rSELF)	# a0<- self->exception
+    # exception pending?
+    bnez    a0, common_exceptionThrown  # yes, handle it
+
+.LOP_INVOKE_OBJECT_INIT_RANGE_finish:
+    lhu     a1, offThread_subMode(rSELF)
+    and     a1, kSubModeDebuggerActive  # debugger active?
+    bnez    a1, .LOP_INVOKE_OBJECT_INIT_RANGE_debugger    # Yes - skip optimization
+    FETCH_ADVANCE_INST(2+1)       # advance to next instr, load rINST
+    GET_INST_OPCODE(t0)                 # t0<- opcode from rINST
+    GOTO_OPCODE(t0)                     # execute it
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: mips/OP_RETURN_VOID_BARRIER.S */
+    SMP_DMB
+    b         common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: mips/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: mips/OP_IGET_WIDE_QUICK.S */
+    # iget-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1                   #  t0 <- a3 + a1
+    LOAD64(a0, a1, t0)                     #  a0 <- obj.field (64 bits, aligned)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: mips/OP_IGET_OBJECT_QUICK.S */
+/* File: mips/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- object we're operating on
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    # check object for null
+    beqz      a3, common_errNullObject     #  object was null
+    addu      t0, a3, a1 #
+    lw        a0, 0(t0)                    #  a0 <- obj.field (always 32 bits)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: mips/OP_IPUT_QUICK.S */
+    /* For: iput-quick, iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: mips/OP_IPUT_WIDE_QUICK.S */
+    # iput-wide-quick vA, vB, offset       /* CCCC */
+    GET_OPA4(a0)                           #  a0 <- A(+)
+    GET_OPB(a1)                            #  a1 <- B
+    GET_VREG(a2, a1)                       #  a2 <- fp[B], the object pointer
+    EAS2(a3, rFP, a0)                      #  a3 <- &fp[A]
+    LOAD64(a0, a1, a3)                     #  a0/a1 <- fp[A]
+    # check object for null
+    beqz      a2, common_errNullObject     #  object was null
+    FETCH(a3, 1)                           #  a3 <- field byte offset
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      a2, a2, a3                   #  obj.field (64 bits, aligned) <- a0/a1
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0/a1
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: mips/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    # op vA, vB, offset                    /* CCCC */
+    GET_OPB(a2)                            #  a2 <- B
+    GET_VREG(a3, a2)                       #  a3 <- fp[B], the object pointer
+    FETCH(a1, 1)                           #  a1 <- field byte offset
+    GET_OPA4(a2)                           #  a2 <- A(+)
+    beqz      a3, common_errNullObject     #  object was null
+    GET_VREG(a0, a2)                       #  a0 <- fp[A]
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t0, a3, a1
+    sw        a0, 0(t0)                    #  obj.field (always 32 bits) <- a0
+    beqz      a0, 1f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t1, a3, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, 0(t2)
+1:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!0)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethodNoRange #  (a0=method, r9="this")
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: mips/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(a3, 2)                           #  a3 <- FEDC or CCCC
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    .if (!1)
+    and       a3, a3, 15                   #  a3 <- C (or stays CCCC)
+    .endif
+    GET_VREG(rOBJ, a3)                     #  rOBJ <- vC ("this" ptr)
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a2, rOBJ)    #  a2 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- thisPtr->clazz->vtable
+    EXPORT_PC()                            #  invoke must export
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- vtable[BBBB]
+    b         common_invokeMethodRange #  (a0=method, r9="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: mips/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!0)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: mips/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: mips/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    # op vB, {vD, vE, vF, vG, vA}, class   /* CCCC */
+    # op vAA, {vCCCC..v(CCCC+AA-1)}, meth  /* BBBB */
+    FETCH(t0, 2)                           #  t0 <- GFED or CCCC
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    .if (!1)
+    and       t0, t0, 15                   #  t0 <- D (or stays CCCC)
+    .endif
+    FETCH(a1, 1)                           #  a1 <- BBBB
+    LOAD_base_offMethod_clazz(a2, a2)      #  a2 <- method->clazz
+    EXPORT_PC()                            #  must export for invoke
+    LOAD_base_offClassObject_super(a2, a2) #  a2 <- method->clazz->super
+    GET_VREG(rOBJ, t0)                     #  rOBJ <- "this"
+    LOAD_base_offClassObject_vtable(a2, a2) #  a2 <- ...clazz->super->vtable
+    # is "this" null ?
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- super->vtable[BBBB]
+    beqz      rOBJ, common_errNullObject   #  "this" is null, throw exception
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: mips/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: mips/OP_IPUT_OBJECT.S */
+    /*
+     * 32-bit instance field put.
+     *
+     * for: iput-object, iput-object-volatile
+     */
+    # op vA, vB, field                     /* CCCC */
+    GET_OPB(a0)                            #  a0 <- B
+    LOAD_rSELF_methodClassDex(a3)          #  a3 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref CCCC
+    LOAD_base_offDvmDex_pResFields(a2, a3) #  a2 <- pDvmDex->pResFields
+    GET_VREG(rOBJ, a0)                     #  rOBJ <- fp[B], the object pointer
+    LOAD_eas2(a0, a2, a1)                  #  a0 <- resolved InstField ptr
+    # is resolved entry null?
+    bnez      a0, .LOP_IPUT_OBJECT_VOLATILE_finish       #  no, already resolved
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveInstField)               #  v0 <- resolved InstField ptr
+    # success?
+    move      a0, v0
+    bnez      v0, .LOP_IPUT_OBJECT_VOLATILE_finish       #  yes, finish up
+    b         common_exceptionThrown
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: mips/OP_SGET_OBJECT_VOLATILE.S */
+/* File: mips/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    # op vAA, field                        /* BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    # is resolved entry !null?
+    bnez      a0, .LOP_SGET_OBJECT_VOLATILE_finish
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    # success?
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b        .LOP_SGET_OBJECT_VOLATILE_finish            # resume
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: mips/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: mips/OP_SPUT_OBJECT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput-object, sput-object-volatile
+     */
+    /* op vAA, field@BBBB */
+    LOAD_rSELF_methodClassDex(a2)          #  a2 <- DvmDex
+    FETCH(a1, 1)                           #  a1 <- field ref BBBB
+    LOAD_base_offDvmDex_pResFields(rBIX, a2) #  rBIX <- dvmDex->pResFields
+    LOAD_eas2(a0, rBIX, a1)                #  a0 <- resolved StaticField ptr
+    bnez      a0, .LOP_SPUT_OBJECT_VOLATILE_finish       #  is resolved entry null?
+
+    /* Continuation if the field has not yet been resolved.
+     * a1:  BBBB field ref
+     * rBIX: dvmDex->pResFields
+     */
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() may throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  success? no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    b       .LOP_SPUT_OBJECT_VOLATILE_finish             # resume
+
+
+
+/* ------------------------------ */
+    .balign 128
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: mips/OP_UNUSED_FF.S */
+/* File: mips/unused.S */
+    BAL(common_abort)
+
+
+
+    .balign 128
+    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart
+    .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ *  Sister implementations
+ * ===========================================================================
+ */
+    .global dvmAsmSisterStart
+    .type   dvmAsmSisterStart, %function
+    .text
+    .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CHECK_CAST */
+
+.LOP_CHECK_CAST_castfailure:
+    # A cast has failed. We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()                            #  about to throw
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    move      a1,rBIX                      #  r1<- desired class
+    JAL(dvmThrowClassCastException)
+    b         common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a2   holds BBBB
+     *  rOBJ holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      a1, a2                       #  a1 <- BBBB
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    LOAD_base_offObject_clazz(a0, rOBJ)    #  a0 <- obj->clazz
+    b         .LOP_CHECK_CAST_resolved         #  pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  a0   holds obj->clazz
+     *  a1   holds class resolved from BBBB
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    JAL(dvmInstanceofNonTrivial)           #  v0 <- boolean result
+    move      a0, v0                       #  fall through to OP_INSTANCE_OF_store
+    b         .LOP_INSTANCE_OF_store
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a3   holds BBBB
+     *  rOBJ holds A
+     */
+.LOP_INSTANCE_OF_resolve:
+    EXPORT_PC()                            #  resolve() could throw
+    LOAD_rSELF_method(a0)                  #  a0 <- self->method
+    move      a1, a3                       #  a1 <- BBBB
+    li        a2, 1                        #  a2 <- true
+    LOAD_base_offMethod_clazz(a0, a0)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    # got null?
+    move      a1, v0                       #  a1 <- class resolved from BBB
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    GET_OPB(a3)                            #  a3 <- B
+    GET_VREG(a0, a3)                       #  a0 <- vB (object)
+    LOAD_base_offObject_clazz(a0, a0)      #  a0 <- obj->clazz
+    b         .LOP_INSTANCE_OF_resolved         #  pick up where we left off
+
+
+/* continuation for OP_NEW_INSTANCE */
+
+.LOP_NEW_INSTANCE_continue:
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(v0, a3)                       #  vAA <- v0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * v0: new object
+     * a3: vAA
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    lw        a1, 0(rBIX)                  #  reload resolved class
+    # okay?
+    bnez      a1, .LOP_NEW_INSTANCE_continue     #  yes, finish
+    move      rOBJ, v0                     #  preserve new object
+    move      rBIX, a3                     #  preserve vAA
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rOBJ, rBIX)                   #  vAA <- new object
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  a0 holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    JAL(dvmInitClass)                      #  initialize class
+    move      a0, rOBJ                     #  restore a0
+    # check boolean result
+    bnez      v0, .LOP_NEW_INSTANCE_initialized  #  success, continue
+    b         common_exceptionThrown       #  failed, deal with init exception
+
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  a1 holds BBBB
+     */
+.LOP_NEW_INSTANCE_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- resolved ClassObject ptr
+    move      a0, v0
+    # got null?
+    bnez      v0, .LOP_NEW_INSTANCE_resolved     #  no, continue
+    b         common_exceptionThrown       #  yes, handle exception
+
+/* continuation for OP_NEW_ARRAY */
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *
+     *  a1 holds array length
+     *  a2 holds class ref CCCC
+     */
+.LOP_NEW_ARRAY_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    move      rOBJ, a1                     #  rOBJ <- length (save)
+    move      a1, a2                       #  a1 <- CCCC
+    li        a2, 0                        #  a2 <- false
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    JAL(dvmResolveClass)                   #  v0 <- call(clazz, ref)
+    move      a1, rOBJ                     #  a1 <- length (restore)
+    # got null?
+    beqz      v0, common_exceptionThrown   #  yes, handle exception
+    move      a0, v0
+    b         .LOP_NEW_ARRAY_finish           #  continue with OP_NEW_ARRAY_finish
+
+
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if 0
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .LOP_FILLED_NEW_ARRAY_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if 0
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+    /*
+     * On entry:
+     *  a0 holds array class
+     *  rOBJ holds AA or BA
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    LOAD_base_offClassObject_descriptor(a3, a0) #  a3 <- arrayClass->descriptor
+    li        a2, ALLOC_DONT_TRACK         #  a2 <- alloc flags
+    lbu       rINST, 1(a3)                 #  rINST <- descriptor[1]
+    .if 1
+    move      a1, rOBJ                     #  a1 <- AA (length)
+    .else
+    srl       a1, rOBJ, 4                  #  rOBJ <- B (length)
+    .endif
+    seq       t0, rINST, 'I'               #  array of ints?
+    seq       t1, rINST, 'L'               #  array of objects?
+    or        t0, t1
+    seq       t1, rINST, '['               #  array of arrays?
+    or        t0, t1
+    move      rBIX, a1                     #  save length in rBIX
+    beqz      t0, .LOP_FILLED_NEW_ARRAY_RANGE_notimpl      #  no, not handled yet
+    JAL(dvmAllocArrayByClass)              #  v0 <- call(arClass, length, flags)
+    # null return?
+    beqz      v0, common_exceptionThrown   #  alloc failed, handle exception
+
+    FETCH(a1, 2)                           #  a1 <- FEDC or CCCC
+    sw        v0, offThread_retval(rSELF)  #  retval.l <- new array
+    sw        rINST, (offThread_retval+4)(rSELF) #  retval.h <- type
+    addu      a0, v0, offArrayObject_contents #  a0 <- newArray->contents
+    subu      rBIX, rBIX, 1                #  length--, check for neg
+    FETCH_ADVANCE_INST(3)                  #  advance to next instr, load rINST
+    bltz      rBIX, 2f                     #  was zero, bail
+
+    # copy values from registers into the array
+    # a0=array, a1=CCCC/FEDC, t0=length (from AA or B), rOBJ=AA/BA
+    move      t0, rBIX
+    .if 1
+    EAS2(a2, rFP, a1)                      #  a2 <- &fp[CCCC]
+1:
+    lw        a3, 0(a2)                    #  a3 <- *a2++
+    addu      a2, 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, (a0)                     #  *contents++ = vX
+    addu      a0, 4
+    bgez      t0, 1b
+
+    # continue at 2
+    .else
+    slt       t1, t0, 4                    #  length was initially 5?
+    and       a2, rOBJ, 15                 #  a2 <- A
+    bnez      t1, 1f                       #  <= 4 args, branch
+    GET_VREG(a3, a2)                       #  a3 <- vA
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 16(a0)                   #  contents[4] = vA
+1:
+    and       a2, a1, 15                   #  a2 <- F/E/D/C
+    GET_VREG(a3, a2)                       #  a3 <- vF/vE/vD/vC
+    srl       a1, a1, 4                    #  a1 <- next reg in low 4
+    subu      t0, t0, 1                    #  count--
+    sw        a3, 0(a0)                    #  *contents++ = vX
+    addu      a0, a0, 4
+    bgez      t0, 1b
+    # continue at 2
+    .endif
+
+2:
+    lw        a0, offThread_retval(rSELF)  #  a0 <- object
+    lw        a1, (offThread_retval+4)(rSELF) #  a1 <- type
+    seq       t1, a1, 'I'                  #  Is int array?
+    bnez      t1, 3f
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    srl       t3, a0, GC_CARD_SHIFT
+    addu      t2, a2, t3
+    sb        a2, (t2)
+3:
+    GET_INST_OPCODE(t0)                    #  ip <- opcode from rINST
+    GOTO_OPCODE(t0)                        #  execute it
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    la        a0, .LstrFilledNewArrayNotImpl
+    JAL(dvmThrowInternalError)
+    b         common_exceptionThrown
+
+    /*
+     * Ideally we'd only define this once, but depending on layout we can
+     * exceed the range of the load above.
+     */
+
+/* continuation for OP_CMPL_FLOAT */
+
+OP_CMPL_FLOAT_nan:
+    li rTEMP, -1
+    b         OP_CMPL_FLOAT_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPL_FLOAT_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, OP_CMPL_FLOAT_finish
+    b         OP_CMPL_FLOAT_nan
+#endif
+
+OP_CMPL_FLOAT_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
+
+/* continuation for OP_CMPG_FLOAT */
+
+OP_CMPG_FLOAT_nan:
+    li rTEMP, 1
+    b         OP_CMPG_FLOAT_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPG_FLOAT_continue:
+    JAL(__gtsf2)                           #  v0 <- (vBB > vCC)
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    bgtz      v0, OP_CMPG_FLOAT_finish
+    b         OP_CMPG_FLOAT_nan
+#endif
+
+OP_CMPG_FLOAT_finish:
+    GET_OPA(t0)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(rTEMP, t0)                    #  vAA <- rTEMP
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)
+
+/* continuation for OP_CMPL_DOUBLE */
+
+OP_CMPL_DOUBLE_nan:
+    li rTEMP, -1
+    b         OP_CMPL_DOUBLE_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPL_DOUBLE_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, OP_CMPL_DOUBLE_nan            #  fall thru for finish
+#endif
+
+OP_CMPL_DOUBLE_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
+
+/* continuation for OP_CMPG_DOUBLE */
+
+OP_CMPG_DOUBLE_nan:
+    li rTEMP, 1
+    b         OP_CMPG_DOUBLE_finish
+
+#ifdef SOFT_FLOAT
+OP_CMPG_DOUBLE_continue:
+    LOAD64(rARG2, rARG3, rBIX)             #  a2/a3 <- vCC/vCC+1
+    JAL(__gtdf2)                           #  fallthru
+    li        rTEMP, 1                     #  rTEMP = 1 if v0 != 0
+    blez      v0, OP_CMPG_DOUBLE_nan            #  fall thru for finish
+#endif
+
+OP_CMPG_DOUBLE_finish:
+    GET_OPA(rOBJ)
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(rTEMP, rOBJ, t0)         #  vAA <- rTEMP
+
+/* continuation for OP_APUT_OBJECT */
+.LOP_APUT_OBJECT_checks:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    JAL(dvmCanPutArrayElement)             #  test object type vs. array type
+    beqz      v0, .LOP_APUT_OBJECT_throw        #  okay ?
+    lw        a2, offThread_cardTable(rSELF)
+    srl       t1, rINST, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)
+    b         .LOP_APUT_OBJECT_finish           #  yes, skip type checks
+.LOP_APUT_OBJECT_throw:
+    LOAD_base_offObject_clazz(a0, rBIX)    #  a0 <- obj->clazz
+    LOAD_base_offObject_clazz(a1, rINST)   #  a1 <- arrayObj->clazz
+    EXPORT_PC()
+    JAL(dvmThrowArrayStoreExceptionIncompatibleElement)
+    b         common_exceptionThrown
+
+/* continuation for OP_IGET */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_WIDE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IGET_WIDE_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if 0
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_OBJECT */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_OBJECT_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_BOOLEAN */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_BOOLEAN_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_BYTE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_BYTE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_CHAR */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_CHAR_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_SHORT */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_SHORT_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+         # noop                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_WIDE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_WIDE_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if 0
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* continuation for OP_IPUT_OBJECT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_OBJECT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t2, rOBJ, a3                 #  form address
+        #  noop                            #  releasing store
+    sw a0, (t2)                        #  obj.field (32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_BOOLEAN_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_BYTE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_BYTE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_CHAR */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_CHAR_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_SHORT */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_SHORT_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+        #  noop                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+        #  noop
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET */
+
+.LOP_SGET_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_WIDE */
+
+.LOP_SGET_WIDE_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if 0
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* continuation for OP_SGET_OBJECT */
+
+.LOP_SGET_OBJECT_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_BOOLEAN */
+
+.LOP_SGET_BOOLEAN_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_BYTE */
+
+.LOP_SGET_BYTE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_CHAR */
+
+.LOP_SGET_CHAR_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SGET_SHORT */
+
+.LOP_SGET_SHORT_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+                      #  no-op                                #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT */
+
+.LOP_SPUT_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .LOP_SPUT_WIDE_finish           # resume
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+.LOP_SPUT_BOOLEAN_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_BYTE */
+
+.LOP_SPUT_BYTE_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_CHAR */
+
+.LOP_SPUT_CHAR_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_SPUT_SHORT */
+
+.LOP_SPUT_SHORT_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    #  no-op                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    #  no-op
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .LOP_INVOKE_SUPER_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodNoRange #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethodNoRange #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethodNoRange #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethodNoRange #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethodNoRange #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX= C or CCCC (index of first arg, which is the "this" ptr)
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG(rOBJ, rBIX)                   #  rOBJ <- "this" ptr
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    # is "this" null?
+    beqz      rOBJ, common_errNullObject   #  null "this", throw exception
+    LOAD_base_offObject_clazz(a3, rOBJ)    #  a3 <- thisPtr->clazz
+    LOAD_base_offClassObject_vtable(a3, a3) #  a3 <- thisPtr->clazz->vtable
+    LOAD_eas2(a0, a3, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodRange #  (a0=method, rOBJ="this")
+
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  a0 = resolved base method
+     *  rBIX = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    LOAD_base_offClassObject_super(a1, rBIX) #  a1 <- method->clazz->super
+    LOADu2_offMethod_methodIndex(a2, a0)   #  a2 <- baseMethod->methodIndex
+    LOAD_base_offClassObject_vtableCount(a3, a1) #  a3 <- super->vtableCount
+    EXPORT_PC()                            #  must export for invoke
+    # compare (methodIndex, vtableCount)
+    bgeu      a2, a3, .LOP_INVOKE_SUPER_RANGE_nsm      #  method not present in superclass
+    LOAD_base_offClassObject_vtable(a1, a1) #  a1 <- ...clazz->super->vtable
+    LOAD_eas2(a0, a1, a2)                  #  a0 <- vtable[methodIndex]
+    b         common_invokeMethodRange #  continue on
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  a0 = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    LOAD_base_offMethod_name(a1, a0)       #  a1 <- method name
+    b         common_errNoSuchMethod
+
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_resolve:
+    LOAD_rSELF_method(a3)                  #  a3 <- self->method
+    LOAD_base_offMethod_clazz(a0, a3)      #  a0 <- method->clazz
+    li        a2, METHOD_STATIC            #  resolver method type
+    JAL(dvmResolveMethod)                  #  v0 <- call(clazz, ref, flags)
+    move      a0, v0
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we're actively building a trace.  If so,
+     * we need to keep this instruction out of it.
+     * rBIX: &resolved_methodToCall
+     */
+    lhu       a2, offThread_subMode(rSELF)
+    beqz      v0, common_exceptionThrown   #  null, handle exception
+    and       a2, kSubModeJitTraceBuild    #  trace under construction?
+    beqz      a2, common_invokeMethodRange #  no, (a0=method, rOBJ="this")
+    lw        a1, 0(rBIX)                  #  reload resolved method
+    # finished resloving?
+    bnez      a1, common_invokeMethodRange #  yes, (a0=method, rOBJ="this")
+    move      rBIX, a0                     #  preserve method
+    move      a0, rSELF
+    move      a1, rPC
+    JAL(dvmJitEndTraceSelect)              #  (self, pc)
+    move      a0, rBIX
+    b         common_invokeMethodRange #  whew, finally!
+#else
+    # got null?
+    bnez      v0, common_invokeMethodRange #  (a0=method, rOBJ="this")
+    b         common_exceptionThrown       #  yes, handle exception
+#endif
+
+/* continuation for OP_FLOAT_TO_INT */
+
+/*
+ * Not an entry point as it is used only once !!
+ */
+f2i_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x4f000000               #  (float)maxint
+    move      rBIX, a0
+    JAL(__gesf2)                           #  is arg >= maxint?
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .LOP_FLOAT_TO_INT_set_vreg
+
+    move      a0, rBIX                     #  recover arg
+    li        a1, 0xcf000000               #  (float)minint
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .LOP_FLOAT_TO_INT_set_vreg
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        v0, 0                        #  return zero for NaN
+    bnez      t0, .LOP_FLOAT_TO_INT_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfsi)
+    b         .LOP_FLOAT_TO_INT_set_vreg
+#else
+    l.s       fa1, .LFLOAT_TO_INT_max
+    c.ole.s   fcc0, fa1, fa0
+    l.s       fv0, .LFLOAT_TO_INT_ret_max
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    l.s       fa1, .LFLOAT_TO_INT_min
+    c.ole.s   fcc0, fa0, fa1
+    l.s       fv0, .LFLOAT_TO_INT_ret_min
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .LOP_FLOAT_TO_INT_set_vreg_f
+
+    trunc.w.s  fv0, fa0
+    b         .LOP_FLOAT_TO_INT_set_vreg_f
+#endif
+
+.LFLOAT_TO_INT_max:
+    .word 0x4f000000
+.LFLOAT_TO_INT_min:
+    .word 0xcf000000
+.LFLOAT_TO_INT_ret_max:
+    .word 0x7fffffff
+.LFLOAT_TO_INT_ret_min:
+    .word 0x80000000
+
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+f2l_doconv:
+#ifdef SOFT_FLOAT
+    li        a1, 0x5f000000
+    move      rBIX, a0
+    JAL(__gesf2)
+
+    move      t0, v0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bgez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    li        a1, 0xdf000000
+    JAL(__lesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    blez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    move      a1, rBIX
+    JAL(__nesf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .LOP_FLOAT_TO_LONG_set_vreg
+
+    move      a0, rBIX
+    JAL(__fixsfdi)
+
+#else
+    l.s       fa1, .LLONG_TO_max
+    c.ole.s   fcc0, fa1, fa0
+    li        rRESULT0, ~0
+    li        rRESULT1, ~0x80000000
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    l.s       fa1, .LLONG_TO_min
+    c.ole.s   fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0x80000000
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    mov.s     fa1, fa0
+    c.un.s    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .LOP_FLOAT_TO_LONG_set_vreg
+
+    JAL(__fixsfdi)
+#endif
+
+    b         .LOP_FLOAT_TO_LONG_set_vreg
+
+.LLONG_TO_max:
+    .word 0x5f000000
+
+.LLONG_TO_min:
+    .word 0xdf000000
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+
+d2i_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)                           #  is arg >= maxint?
+
+    move      t0, v0
+    li        v0, ~0x80000000              #  return maxint (7fffffff)
+    bgez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)                           #  is arg <= minint?
+
+    move      t0, v0
+    li        v0, 0x80000000               #  return minint (80000000)
+    blez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  nonzero == yes
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    move      rARG2, rBIX                  #  compare against self
+    move      rARG3, rTEMP
+    JAL(__nedf2)                           #  is arg == self?
+
+    move      t0, v0                       #  zero == no
+    li        v0, 0
+    bnez      t0, .LOP_DOUBLE_TO_INT_set_vreg     #  return zero for NaN
+
+    move      rARG0, rBIX                  #  recover arg
+    move      rARG1, rTEMP
+    JAL(__fixdfsi)                         #  convert double to int
+    b         .LOP_DOUBLE_TO_INT_set_vreg
+#else
+    la        t0, .LDOUBLE_TO_INT_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    l.s       fv0, .LDOUBLE_TO_INT_maxret
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    la        t0, .LDOUBLE_TO_INT_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    l.s       fv0, .LDOUBLE_TO_INT_minret
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li.s      fv0, 0
+    bc1t      .LOP_DOUBLE_TO_INT_set_vreg_f
+
+    trunc.w.d  fv0, fa0
+    b         .LOP_DOUBLE_TO_INT_set_vreg_f
+#endif
+
+
+.LDOUBLE_TO_INT_max:
+    .dword 0x41dfffffffc00000
+.LDOUBLE_TO_INT_min:
+    .dword 0xc1e0000000000000              #  minint, as a double (high word)
+.LDOUBLE_TO_INT_maxret:
+    .word 0x7fffffff
+.LDOUBLE_TO_INT_minret:
+    .word 0x80000000
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+d2l_doconv:
+#ifdef SOFT_FLOAT
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64(rARG2, rARG3, t0)
+    move      rBIX, rARG0                  #  save a0
+    move      rTEMP, rARG1                 #  and a1
+    JAL(__gedf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bgez      t1, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64(rARG2, rARG3, t0)
+    JAL(__ledf2)
+
+    move      t1, v0
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    blez      t1, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    move      rARG2, rBIX
+    move      rARG3, rTEMP
+    JAL(__nedf2)
+
+    move      t0, v0
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bnez      t0, .LOP_DOUBLE_TO_LONG_set_vreg
+
+    move      rARG0, rBIX
+    move      rARG1, rTEMP
+    JAL(__fixdfdi)
+
+#else
+    la        t0, .LDOUBLE_TO_LONG_max
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa1, fa0
+    la        t0, .LDOUBLE_TO_LONG_ret_max
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+
+    la        t0, .LDOUBLE_TO_LONG_min
+    LOAD64_F(fa1, fa1f, t0)
+    c.ole.d   fcc0, fa0, fa1
+    la        t0, .LDOUBLE_TO_LONG_ret_min
+    LOAD64(rRESULT0, rRESULT1, t0)
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+
+    mov.d     fa1, fa0
+    c.un.d    fcc0, fa0, fa1
+    li        rRESULT0, 0
+    li        rRESULT1, 0
+    bc1t      .LOP_DOUBLE_TO_LONG_set_vreg
+    JAL(__fixdfdi)
+#endif
+    b         .LOP_DOUBLE_TO_LONG_set_vreg
+
+
+.LDOUBLE_TO_LONG_max:
+    .dword 0x43e0000000000000              #  maxlong, as a double (high word)
+.LDOUBLE_TO_LONG_min:
+    .dword 0xc3e0000000000000              #  minlong, as a double (high word)
+.LDOUBLE_TO_LONG_ret_max:
+    .dword 0x7fffffffffffffff
+.LDOUBLE_TO_LONG_ret_min:
+    .dword 0x8000000000000000
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(v0, v1, a0)                    #  vAA::vAA+1 <- v0(low) :: v1(high)
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_VOLATILE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_VOLATILE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    SMP_DMB                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      rOBJ, rOBJ, a3               #  form address
+    SMP_DMB_ST                            #  releasing store
+    sw a0, (rOBJ)                      #  obj.field (8/16/32 bits) <- a0
+    SMP_DMB
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET_VOLATILE */
+
+.LOP_SGET_VOLATILE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    SMP_DMB                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT_VOLATILE */
+
+.LOP_SPUT_VOLATILE_finish:
+    # field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SMP_DMB_ST                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    SMP_DMB
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  v0 holds resolved field
+     *  rOBJ holds object (caller saved)
+     */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    addu      a3, a3, rOBJ                 #  form address
+    lw a0, (a3)                         #  a0 <- obj.field (8/16/32 bits)
+    SMP_DMB                               #  acquiring load
+    GET_OPA4(a2)                           #  a2 <- A+
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    SET_VREG(a0, a2)                       #  fp[A] <- a0
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IGET_WIDE_VOLATILE_finish:
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    beqz      rOBJ, common_errNullObject   #  object was null
+    GET_OPA4(a2)                           #  a2 <- A+
+    addu      rOBJ, rOBJ, a3               #  form address
+    .if 1
+    vLOAD64(a0, a1, rOBJ)                  #  a0/a1 <- obj.field (64-bit align ok)
+    .else
+    LOAD64(a0, a1, rOBJ)                   #  a0/a1 <- obj.field (64-bit align ok)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a3, rFP, a2)                      #  a3 <- &fp[A]
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    STORE64(a0, a1, a3)                    #  fp[A] <- a0/a1
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0   holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+    GET_OPA4(a2)                           #  a2 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    EAS2(a2, rFP, a2)                      #  a2 <- &fp[A]
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    LOAD64(a0, a1, a2)                     #  a0/a1 <- fp[A]
+    GET_INST_OPCODE(rBIX)                  #  extract opcode from rINST
+    addu      a2, rOBJ, a3                 #  form address
+    .if 1
+    JAL(dvmQuasiAtomicSwap64Sync)          # stores r0/r1 into addr r2
+#    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .else
+    STORE64(a0, a1, a2)                    #  obj.field (64 bits, aligned) <- a0 a1
+    .endif
+    GOTO_OPCODE(rBIX)                      #  jump to next instruction
+
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+.LOP_SGET_WIDE_VOLATILE_finish:
+    GET_OPA(a1)                            #  a1 <- AA
+    .if 1
+    vLOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .else
+    LOAD64_off(a2, a3, a0, offStaticField_value) #  a2/a3 <- field value (aligned)
+    .endif
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    EAS2(a1, rFP, a1)                      #  a1 <- &fp[AA]
+    STORE64(a2, a3, a1)                    #  vAA/vAA+1 <- a2/a3
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  a1:  BBBB field ref
+     *  rOBJ:  &fp[AA]
+     *  rBIX: dvmDex->pResFields
+     *
+     * Returns StaticField pointer in a2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    LOAD_rSELF_method(a2)                  #  a2 <- current method
+#if defined(WITH_JIT)
+    EAS2(rBIX, rBIX, a1)                   #  rBIX<- &dvmDex->pResFields[field]
+#endif
+    EXPORT_PC()                            #  resolve() could throw, so export now
+    LOAD_base_offMethod_clazz(a0, a2)      #  a0 <- method->clazz
+    JAL(dvmResolveStaticField)             #  v0 <- resolved StaticField ptr
+    # success ?
+    move      a0, v0
+    beqz      v0, common_exceptionThrown   #  no, handle exception
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including this instruction.
+     */
+    JAL(common_verifyField)
+#endif
+    move      a2, v0
+    b         .LOP_SPUT_WIDE_VOLATILE_finish           # resume
+
+/* continuation for OP_EXECUTE_INLINE */
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *
+     * Other ideas:
+     * - Use a jump table from the main piece to jump directly into the
+     *   AND/LW pairs.  Costs a data load, saves a branch.
+     * - Have five separate pieces that do the loading, so we can work the
+     *   interleave a little better.  Increases code size.
+     */
+.LOP_EXECUTE_INLINE_continue:
+    FETCH(rINST, 2)                        #  rINST <- FEDC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    and       t0, rINST, 0xf000            #  isolate F
+    ESRN(t1, rFP, t0, 10)
+    lw        a3, 0(t1)                    #  a3 <- vF (shift right 12, left 2)
+3:
+    and       t0, rINST, 0x0f00            #  isolate E
+    ESRN(t1, rFP, t0, 6)
+    lw        a2, 0(t1)                    #  a2 <- vE
+2:
+    and       t0, rINST, 0x00f0            #  isolate D
+    ESRN(t1, rFP, t0, 2)
+    lw        a1, 0(t1)                    #  a1 <- vD
+1:
+    and       t0, rINST, 0x000f            #  isolate C
+    EASN(t1, rFP, t0, 2)
+    lw        a0, 0(t1)                    #  a0 <- vC
+0:
+    la        rINST, gDvmInlineOpsTable    #  table of InlineOperation
+    EAS4(t1, rINST, rBIX)                  #  t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                           #  sizeof=16, "func" is first entry
+    # (not reached)
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .LOP_EXECUTE_INLINE_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPB(a0)                            #  a0 <- B
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    BAL(.LOP_EXECUTE_INLINE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rINST, v0                    #  save result of inline
+    move      a0, rOBJ                     #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rINST, common_exceptionThrown   #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+    /*
+     * Extract args, call function.
+     *  a0 = #of args (0-4)
+     *  rBIX = call index
+     *  ra = return addr, above  [DO NOT JAL out of here w/o preserving ra]
+     */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+    FETCH(rOBJ, 2)                       # rOBJ <- CCCC
+    beq       a0, 0, 0f
+    beq       a0, 1, 1f
+    beq       a0, 2, 2f
+    beq       a0, 3, 3f
+    beq       a0, 4, 4f
+    JAL(common_abort)                      #  too many arguments
+
+4:
+    add       t0, rOBJ, 3
+    GET_VREG(a3, t0)
+3:
+    add       t0, rOBJ, 2
+    GET_VREG(a2, t0)
+2:
+    add       t0, rOBJ, 1
+    GET_VREG(a1, t0)
+1:
+    GET_VREG(a0, rOBJ)
+0:
+    la        rOBJ, gDvmInlineOpsTable      # table of InlineOperation
+    EAS4(t1, rOBJ, rBIX)                    # t1 <- rINST + rBIX<<4
+    lw        t9, 0(t1)
+    jr        t9                            # sizeof=16, "func" is first entry
+    # not reached
+
+    /*
+     * We're debugging or profiling.
+     * rBIX: opIndex
+     */
+.LOP_EXECUTE_INLINE_RANGE_debugmode:
+    move      a0, rBIX
+    JAL(dvmResolveInlineNative)
+    beqz      v0, .LOP_EXECUTE_INLINE_RANGE_resume       #  did it resolve? no, just move on
+    move      rOBJ, v0                     #  remember method
+    move      a0, v0
+    move      a1, rSELF
+    JAL(dvmFastMethodTraceEnter)           #  (method, self)
+    addu      a1, rSELF, offThread_retval  #  a1<- &self->retval
+    GET_OPA(a0)                            #  a0 <- A
+    # Stack should have 16/20 available
+    sw        a1, STACK_OFFSET_ARG04(sp)   #  push &self->retval
+    move      rINST, rOBJ                  #  rINST<- method
+    BAL(.LOP_EXECUTE_INLINE_RANGE_continue)              #  make call; will return after
+    lw        gp, STACK_OFFSET_GP(sp)      #  restore gp
+    move      rOBJ, v0                     #  save result of inline
+    move      a0, rINST                    #  a0<- method
+    move      a1, rSELF                    #  a1<- self
+    JAL(dvmFastNativeMethodTraceExit)      #  (method, self)
+    beqz      rOBJ, common_exceptionThrown #  returned false, handle exception
+    FETCH_ADVANCE_INST(3)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/* continuation for OP_INVOKE_OBJECT_INIT_RANGE */
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.LOP_INVOKE_OBJECT_INIT_RANGE_debugger:
+    lw      a1, offThread_mainHandlerTable(rSELF)
+    li      t0, OP_INVOKE_DIRECT_RANGE
+    GOTO_OPCODE_BASE(a1, t0)            # execute it
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+    /*
+     * Currently:
+     *  a0 holds resolved field
+     *  rOBJ holds object
+     */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    #BAL(common_squeak0)
+    GET_OPA4(a1)                           #  a1 <- A+
+    LOAD_base_offInstField_byteOffset(a3, a0) #  a3 <- byte offset of field
+    GET_VREG(a0, a1)                       #  a0 <- fp[A]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    # check object for null
+    beqz      rOBJ, common_errNullObject   #  object was null
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    addu      t2, rOBJ, a3                 #  form address
+    SMP_DMB_ST                            #  releasing store
+    sw a0, (t2)                        #  obj.field (32 bits) <- a0
+    SMP_DMB
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    beqz      a0, 1f                       #  stored a null reference?
+    srl       t1, rOBJ, GC_CARD_SHIFT
+    addu      t2, a2, t1
+    sb        a2, (t2)                     #  mark card if not
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+.LOP_SGET_OBJECT_VOLATILE_finish:
+    LOAD_base_offStaticField_value(a1, a0) #  a1 <- field value
+    SMP_DMB                               #  acquiring load
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SET_VREG_GOTO(a1, a2, t0)              #  fp[AA] <- a1
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish:                        #  field ptr in a0
+    GET_OPA(a2)                            #  a2 <- AA
+    FETCH_ADVANCE_INST(2)                  #  advance rPC, load rINST
+    GET_VREG(a1, a2)                       #  a1 <- fp[AA]
+    lw        a2, offThread_cardTable(rSELF) #  a2 <- card table base
+    lw        t1, offField_clazz(a0)       #  t1 <- field->clazz
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    SMP_DMB_ST                            #  releasing store
+    sw        a1, offStaticField_value(a0) #  field <- vAA
+    SMP_DMB
+    beqz      a1, 1f
+    srl       t2, t1, GC_CARD_SHIFT
+    addu      t3, a2, t2
+    sb        a2, (t3)
+1:
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: mips/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * "longjmp" to a translation after single-stepping.  Before returning
+ * to translation, must save state for self-verification.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      rBIX, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    b       jitSVShadowRunStart                 # resume as if cache hit
+                                                # expects resume addr in rBIX
+
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    li        a2, kSVSPunt                 #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    move      rPC, a0                      # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a2, kSVSSingleStep           #  a2 <- interpreter entry point
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoProfile            #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSTraceSelect          #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSBackwardBranch       #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        a0, 0(ra)                   #  pass our target PC
+    li        a2, kSVSNormal               #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    move      a0, rPC                      #  pass our target PC
+    li        a2, kSVSNoChain              #  a2 <- interpreter entry point
+    sw        zero, offThread_inJitCodeCache(rSELF) #  Back to the interp land
+    b         jitSVShadowRunEnd            #  doesn't return
+#else                                   /*  WITH_SELF_VERIFICATION */
+
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+    .global dvmJitResumeTranslation             # (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+    move    rSELF, a0                           # restore self
+    move    rPC, a1                             # restore Dalvik pc
+    move    rFP, a2                             # restore Dalvik fp
+    lw      a0, offThread_jitResumeNPC(rSELF)
+    sw      zero, offThread_jitResumeNPC(rSELF) # reset resume address
+    lw      sp, offThread_jitResumeNSP(rSELF)   # cut back native stack
+    jr      a0                                  # resume translation
+
+
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0
+#if defined(WITH_JIT_TUNING)
+    move      a0, ra
+    JAL(dvmBumpPunt)
+#endif
+    EXPORT_PC()
+    sw        zero, offThread_inJitCodeCache(rSELF) # Back to the interp land
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    rPC <= Dalvik PC of instrucion to interpret
+ *    a1 <= Dalvik PC of resume instruction
+ *    ra <= resume point in translation
+ */
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    lw        gp, STACK_OFFSET_GP(sp)
+    move      rPC, a0                       # set up dalvik pc
+    EXPORT_PC()
+    sw        ra, offThread_jitResumeNPC(rSELF)
+    sw        sp, offThread_jitResumeNSP(rSELF)
+    sw        a1, offThread_jitResumeDPC(rSELF)
+    li        a1, 1
+    sw        a1, offThread_singleStepCount(rSELF) # just step once
+    move      a0, rSELF
+    li        a1, kSubModeCountedStep
+    JAL(dvmEnableSubMode)                   # (self, subMode)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    move      a1, rPC                      # arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 2f                       # 0 means translation does not exist
+    jr        a0
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used following
+ * invokes.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # @ (pc, self)
+    sw        v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag
+    beqz      v0, 2f
+    move      a0, v0
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/* No translation, so request one if profiling isn't disabled */
+2:
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    FETCH_INST()
+    li        t0, kJitTSelectRequestHot
+    movn      a2, t0, a0                   #  ask for trace selection
+    bnez      a0, common_selectTrace
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target.  If so, we do a translation chain and
+ * go back to native execution.  Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    lw        gp, STACK_OFFSET_GP(sp)
+    lw        rPC, (ra)                    #  get our target PC
+    subu      rINST, ra, 8                 #  save start of chain branch
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNormal)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)           # @ (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    beqz      a0, toInterpreter            #  go if not, otherwise do chain
+    move      a1, rINST
+    JAL(dvmJitChain)                       #  v0 <- dvmJitChain(codeAddr, chainAddr)
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    move      a0, v0
+    beqz      a0, toInterpreter            #  didn't chain - resume with interpreter
+
+    jr        a0                           #  continue native execution
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, footer235
+
+    jr        a0                           #  continue native execution if so
+footer235:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)                    #  extract opcode from rINST
+    GOTO_OPCODE(t0)                        #  jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    lw        gp, STACK_OFFSET_GP(sp)
+#if defined(WITH_JIT_TUNING)
+    JAL(dvmBumpNoChain)
+#endif
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        a0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+    beqz      a0, 1f
+    jr        a0                           #  continue native execution if so
+1:
+#endif                                  /*  WITH_SELF_VERIFICATION */
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rSELF & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here.  We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+
+toInterpreter:
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    lw        a0, offThread_pJitProfTable(rSELF)
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    # NOTE: intended fallthrough
+
+/*
+ * Similar to common_updateProfile, but tests for null pJitProfTable
+ * r0 holds pJifProfTAble, rINST is loaded, rPC is current and
+ * rIBASE has been recently refreshed.
+ */
+
+common_testUpdateProfile:
+
+    beqz      a0, 4f
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.
+ * On entry here:
+ *    r0    <= pJitProfTable (verified non-NULL)
+ *    rPC   <= Dalvik PC
+ *    rINST <= next instruction
+ */
+common_updateProfile:
+    srl       a3, rPC, 12                  #  cheap, but fast hash function
+    xor       a3, a3, rPC
+    andi      a3, a3, JIT_PROF_SIZE-1      #  eliminate excess bits
+    addu      t1, a0, a3
+    lbu       a1, (t1)                     #  get counter
+    GET_INST_OPCODE(t0)
+    subu      a1, a1, 1                    #  decrement counter
+    sb        a1, (t1)                     #  and store it
+    beqz      a1, 1f
+    GOTO_OPCODE(t0)                        #  if not threshold, fallthrough otherwise
+1:
+    /* Looks good, reset the counter */
+    lw        a1, offThread_jitThreshold(rSELF)
+    sb        a1, (t1)
+    EXPORT_PC()
+    move      a0, rPC
+    move      a1, rSELF
+    JAL(dvmJitGetTraceAddrThread)          # (pc, self)
+    move      a0, v0
+    sw        v0, offThread_inJitCodeCache(rSELF) #  set the inJitCodeCache flag
+    move      a1, rPC                      #  arg1 of translation may need this
+    move      ra, zero                     #  in case target is HANDLER_INTERPRET
+
+#if !defined(WITH_SELF_VERIFICATION)
+    li        t0, kJitTSelectRequest       #  ask for trace selection
+    movz      a2, t0, a0
+    beqz      a0, common_selectTrace
+    jr        a0                           #  jump to the translation
+#else
+
+    bne       a0, zero, skip_ask_for_trace_selection
+    li        a2, kJitTSelectRequest       #  ask for trace selection
+    j         common_selectTrace
+
+skip_ask_for_trace_selection:
+    /*
+     * At this point, we have a target translation.  However, if
+     * that translation is actually the interpret-only pseudo-translation
+     * we want to treat it the same as no translation.
+     */
+    move      rBIX, a0                     #  save target
+    jal       dvmCompilerGetInterpretTemplate
+    # special case?
+    bne       v0, rBIX, jitSVShadowRunStart  #  set up self verification shadow space
+    # Need to clear the inJitCodeCache flag
+    sw        zero, offThread_inJitCodeCache(rSELF) #  back to the interp land
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state.
+ */
+
+common_selectTrace:
+    lhu        a0, offThread_subMode(rSELF)
+    andi       a0, (kSubModeJitTraceBuild | kSubModeJitSV)
+    bnez       a0, 3f                      # already doing JIT work, continue
+    sw         a2, offThread_jitState(rSELF)
+    move       a0, rSELF
+
+/*
+ * Call out to validate trace-building request.  If successful,
+ * rIBASE will be swapped to to send us into single-stepping trace
+ * building mode, so we need to refresh before we continue.
+ */
+
+    EXPORT_PC()
+    SAVE_PC_TO_SELF()
+    SAVE_FP_TO_SELF()
+    JAL(dvmJitCheckTraceRequest)
+3:
+    FETCH_INST()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+4:
+    GET_INST_OPCODE(t0)                    # extract opcode from rINST
+    GOTO_OPCODE(t0)
+    /* no return */
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ *    rPC, rFP, rSELF: the values that they should contain
+ *    r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+    move      a0, rPC                      #  r0 <- program counter
+    move      a1, rFP                      #  r1 <- frame pointer
+    move      a2, rSELF                    #  r2 <- InterpState pointer
+    move      a3, rBIX                     #  r3 <- target translation
+    jal       dvmSelfVerificationSaveState #  save registers to shadow space
+    lw        rFP, offShadowSpace_shadowFP(v0) #  rFP <- fp in shadow space
+    jr        rBIX                         #  jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    move      a1, rFP                      #  pass ending fp
+    move      a3, rSELF                    #  pass self ptr for convenience
+    jal       dvmSelfVerificationRestoreState #  restore pc and fp values
+    LOAD_PC_FP_FROM_SELF()                 #  restore pc, fp
+    lw        a1, offShadowSpace_svState(a0) #  get self verification state
+    beq       a1, zero, 1f                 #  check for punt condition
+
+    # Setup SV single-stepping
+    move      a0, rSELF
+    li        a1, kSubModeJitSV
+    JAL(dvmEnableSubMode)                  # (self, subMode)
+    li        a2, kJitSelfVerification     #  ask for self verification
+    sw        a2, offThread_jitState(rSELF)
+    # Intentional fallthrough
+
+1:
+    # exit to interpreter without check
+    EXPORT_PC()
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+#endif
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * It will end this interpreter activation, and return to the caller
+ * of dvmMterpStdRun.
+ *
+ * State registers will be saved to the "thread" area before bailing
+ * debugging purposes
+ */
+    .ent common_gotoBail
+common_gotoBail:
+    SAVE_PC_FP_TO_SELF()                   # export state to "thread"
+    move      a0, rSELF                    # a0 <- self ptr
+    b         dvmMterpStdBail              # call(self, changeInterp)
+    .end common_gotoBail
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    beqz    rOBJ, 1f
+    lw      rOBJ, offObject_clazz(rOBJ)
+1:
+    sw      a0, offThread_methodToCall(rSELF)
+    sw      rOBJ, offThread_callsiteClass(rSELF)
+    jr      ra
+#endif
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPA(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)              #  rBIX <- stack save area
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)                           #  a1 <- CCCC
+.LinvokeRangeArgs:
+    # a0=methodToCall, a1=CCCC, a2=count, rBIX=outs
+    # (very few methods have > 10 args; could unroll for common cases)
+    EAS2(a3, rFP, a1)
+    sll       t0, a2, 2
+    subu      rBIX, rBIX, t0
+
+1:
+    lw        a1, 0(a3)
+    addu      a3, a3, 4
+    subu      a2, a2, 1
+    sw        a1, 0(rBIX)
+    addu      rBIX, 4
+    bnez      a2, 1b
+    b         .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  a0 is "Method* methodToCall", "rOBJ is this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+    lhu      a1, offThread_subMode(rSELF)
+    andi     a1, kSubModeJitTraceBuild
+    beqz     a1, 1f
+    JAL(save_callsiteinfo)
+#endif
+
+    # prepare to copy args to "outs" area of current frame
+1:
+    GET_OPB(a2)
+    SAVEAREA_FROM_FP(rBIX, rFP)
+    beqz      a2, .LinvokeArgsDone
+    FETCH(a1, 2)
+
+    # a0=methodToCall, a1=GFED, a2=count,
+.LinvokeNonRange:
+    beq       a2, 0, 0f
+    beq       a2, 1, 1f
+    beq       a2, 2, 2f
+    beq       a2, 3, 3f
+    beq       a2, 4, 4f
+    beq       a2, 5, 5f
+
+5:
+    and       t0, rINST, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+4:
+    and       t0, a1, 0xf000
+    ESRN(t2, rFP, t0, 10)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+3:
+    and       t0, a1, 0x0f00
+    ESRN(t2, rFP, t0, 6)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+2:
+    and       t0, a1, 0x00f0
+    ESRN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+1:
+    and       t0, a1, 0x000f
+    EASN(t2, rFP, t0, 2)
+    lw        a3, (t2)
+    subu      rBIX, 4
+    sw        a3, 0(rBIX)
+
+0:
+    #fall through .LinvokeArgsDone
+
+
+.LinvokeArgsDone:                          #  a0=methodToCall
+    lhu       rOBJ, offMethod_registersSize(a0)
+    lhu       a3, offMethod_outsSize(a0)
+    lw        a2, offMethod_insns(a0)
+    lw        rINST, offMethod_clazz(a0)
+    # find space for the new stack frame, check for overflow
+    SAVEAREA_FROM_FP(a1, rFP)              # a1 <- stack save area
+    sll       t0, rOBJ, 2                    #  a1 <- newFp (old savearea - regsSize)
+    subu      a1, a1, t0
+    SAVEAREA_FROM_FP(rBIX, a1)
+    lw        rOBJ, offThread_interpStackEnd(rSELF) #  t3 <- interpStackEnd
+    sll       t2, a3, 2
+    subu      t0, rBIX, t2
+    lhu       ra, offThread_subMode(rSELF)
+    lw        a3, offMethod_accessFlags(a0) #  a3 <- methodToCall->accessFlags
+    bltu      t0, rOBJ, .LstackOverflow      #  yes, this frame will overflow stack
+
+
+    # set up newSaveArea
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP(t0, rFP)
+    sw        t0, offStackSaveArea_prevSave(rBIX)
+#endif
+    sw        rFP, (offStackSaveArea_prevFrame)(rBIX)
+    sw        rPC, (offStackSaveArea_savedPc)(rBIX)
+#if defined(WITH_JIT)
+    sw        zero, (offStackSaveArea_returnAddr)(rBIX)
+#endif
+    sw        a0, (offStackSaveArea_method)(rBIX)
+    # Profiling?
+    bnez       ra, 2f
+1:
+    and       t2, a3, ACC_NATIVE
+    bnez      t2, .LinvokeNative
+    lhu       rOBJ, (a2)           # rOBJ -< load Inst from New PC
+    lw        a3, offClassObject_pDvmDex(rINST)
+    move      rPC, a2              # Publish new rPC
+    # Update state values for the new method
+    # a0=methodToCall, a1=newFp, a3=newMethodClass, rOBJ=newINST
+    sw        a0, offThread_method(rSELF)
+    sw        a3, offThread_methodClassDex(rSELF)
+    li        a2, 1
+    sw        a2, offThread_debugIsMethodEntry(rSELF)
+
+#if defined(WITH_JIT)
+    lw        a0, offThread_pJitProfTable(rSELF)
+    move      rFP, a1                    # fp = newFp
+    GET_PREFETCHED_OPCODE(t0, rOBJ)      # extract prefetched opcode from rOBJ
+    move      rINST, rOBJ                # publish new rINST
+    sw        a1, offThread_curFrame(rSELF)
+    bnez      a0, common_updateProfile
+    GOTO_OPCODE(t0)
+#else
+    move      rFP, a1
+    GET_PREFETCHED_OPCODE(t0, rOBJ)
+    move      rINST, rOBJ
+    sw        a1, offThread_curFrame(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+2:
+    # Profiling - record method entry.  a0: methodToCall
+    STACK_STORE(a0, 0)
+    STACK_STORE(a1, 4)
+    STACK_STORE(a2, 8)
+    STACK_STORE(a3, 12)
+    sw       rPC, offThread_pc(rSELF)          # update interpSave.pc
+    move     a1, a0
+    move     a0, rSELF
+    JAL(dvmReportInvoke)
+    STACK_LOAD(a3, 12)                         # restore a0-a3
+    STACK_LOAD(a2, 8)
+    STACK_LOAD(a1, 4)
+    STACK_LOAD(a0, 0)
+    b        1b
+.LinvokeNative:
+    # Prep for the native call
+    # a0=methodToCall, a1=newFp, rBIX=newSaveArea
+    lhu       ra, offThread_subMode(rSELF)
+    lw        t3, offThread_jniLocal_topCookie(rSELF)
+    sw        a1, offThread_curFrame(rSELF)
+    sw        t3, offStackSaveArea_localRefCookie(rBIX) # newFp->localRefCookie=top
+    move      a2, a0
+    move      a0, a1
+    addu      a1, rSELF, offThread_retval
+    move      a3, rSELF
+#ifdef ASSIST_DEBUGGER
+    /* insert fake function header to help gdb find the stack frame */
+    b         .Lskip
+    .ent dalvik_mterp
+dalvik_mterp:
+    STACK_STORE_FULL()
+.Lskip:
+#endif
+    bnez      ra, 11f                          # Any special SubModes active?
+    lw        t9, offMethod_nativeFunc(a2)
+    jalr      t9
+    lw        gp, STACK_OFFSET_GP(sp)
+7:
+    # native return; rBIX=newSaveArea
+    # equivalent to dvmPopJniLocals
+    lw        a0, offStackSaveArea_localRefCookie(rBIX)
+    lw        a1, offThread_exception(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    sw        a0, offThread_jniLocal_topCookie(rSELF)    # new top <- old top
+    bnez      a1, common_exceptionThrown
+
+    FETCH_ADVANCE_INST(3)
+    GET_INST_OPCODE(t0)
+    GOTO_OPCODE(t0)
+11:
+    # a0=newFp, a1=&retval, a2=methodToCall, a3=self, ra=subModes
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    move      a0, a2                    # a0 <- methodToCall
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPreNativeInvoke)       # (methodToCall, self, fp)
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Call the native method
+    lw       t9, offMethod_nativeFunc(a2)      # t9<-methodToCall->nativeFunc
+    jalr     t9
+    lw       gp, STACK_OFFSET_GP(sp)
+
+    # Restore the pre-call arguments
+    SCRATCH_LOAD(a3, 12)                         # restore a0-a3
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a0, 0)
+
+    # Finish up any post-invoke subMode requirements
+    move      a0, a2
+    move      a1, rSELF
+    move      a2, rFP
+    JAL(dvmReportPostNativeInvoke)      # (methodToCall, self, fp)
+    b         7b
+
+
+.LstackOverflow:       # a0=methodToCall
+    move      a1, a0                    #  a1 <- methodToCall
+    move      a0, rSELF                 # a0 <- self
+    JAL(dvmHandleStackOverflow)         #  dvmHandleStackOverflow(self, methodToCall)
+    b         common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .end dalvik_mterp
+#endif
+
+    /*
+     * Common code for method invocation, calling through "glue code".
+     *
+     * TODO: now that we have range and non-range invoke handlers, this
+     *       needs to be split into two.  Maybe just create entry points
+     *       that set r9 and jump here?
+     *
+     * On entry:
+     *  r0 is "Method* methodToCall", the method we're trying to call
+     *  r9 is "bool methodCallRange", indicating if this is a /range variant
+     */
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+    lhu       t0, offThread_subMode(rSELF)
+    SAVEAREA_FROM_FP(a0, rFP)
+    lw        rOBJ, offStackSaveArea_savedPc(a0) # rOBJ = saveArea->savedPc
+    bnez      t0, 19f
+14:
+    lw        rFP, offStackSaveArea_prevFrame(a0) # fp = saveArea->prevFrame
+    lw        a2, (offStackSaveArea_method - sizeofStackSaveArea)(rFP)
+                                               # a2<- method we're returning to
+    # is this a break frame?
+    beqz      a2, common_gotoBail              # break frame, bail out completely
+
+    lw        rBIX, offMethod_clazz(a2)        # rBIX<- method->clazz
+    lw        rIBASE, offThread_curHandlerTable(rSELF) # refresh rIBASE
+    PREFETCH_ADVANCE_INST(rINST, rOBJ, 3)      # advance rOBJ, update new rINST
+    sw        a2, offThread_method(rSELF)      # self->method = newSave->method
+    lw        a1, offClassObject_pDvmDex(rBIX) # r1<- method->clazz->pDvmDex
+    sw        rFP, offThread_curFrame(rSELF)   # curFrame = fp
+#if defined(WITH_JIT)
+    lw         rBIX, offStackSaveArea_returnAddr(a0)
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    sw         rBIX, offThread_inJitCodeCache(rSELF) # may return to JIT'ed land
+    beqz       rBIX, 15f                       # caller is compiled code
+    move       t9, rBIX
+    jalr       t9
+    lw         gp, STACK_OFFSET_GP(sp)
+15:
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    GOTO_OPCODE(t0)                            # jump to next instruction
+#else
+    GET_INST_OPCODE(t0)                        # extract opcode from rINST
+    move       rPC, rOBJ                       # publish new rPC
+    sw         a1, offThread_methodClassDex(rSELF)
+    GOTO_OPCODE(t0)
+#endif
+
+19:
+    # Handle special actions
+    # On entry, a0: StackSaveArea
+    lw         a1, offStackSaveArea_prevFrame(a0) # a1<- prevFP
+    sw         rPC, offThread_pc(rSELF)        # update interpSave.pc
+    sw         a1, offThread_curFrame(rSELF)   # update interpSave.curFrame
+    move       a0, rSELF
+    JAL(dvmReportReturn)
+    SAVEAREA_FROM_FP(a0, rFP)                  # restore StackSaveArea
+    b          14b
+
+    .if 0
+    /*
+     * Return handling, calls through "glue code".
+     */
+.LreturnOld:
+    SAVE_PC_FP_TO_SELF()                       # export state
+    move       a0, rSELF                       # arg to function
+    JAL(dvmMterp_returnFromMethod)
+    b          common_resumeAfterGlueCall
+    .endif
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+    .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC()
+    move     a0, rSELF
+    JAL(dvmCheckSuspendPending)
+    lw       rOBJ, offThread_exception(rSELF)
+    move     a1, rSELF
+    move     a0, rOBJ
+    JAL(dvmAddTrackedAlloc)
+    lhu      a2, offThread_subMode(rSELF)
+    sw       zero, offThread_exception(rSELF)
+
+    # Special subMode?
+    bnez     a2, 7f                     # any special subMode handling needed?
+8:
+    /* set up args and a local for "&fp" */
+    sw       rFP, 20(sp)                 #  store rFP => tmp
+    addu     t0, sp, 20                  #  compute &tmp
+    sw       t0, STACK_OFFSET_ARG04(sp)  #  save it in arg4 as per ABI
+    li       a3, 0                       #  a3 <- false
+    lw       a1, offThread_method(rSELF)
+    move     a0, rSELF
+    lw       a1, offMethod_insns(a1)
+    move     a2, rOBJ
+    subu     a1, rPC, a1
+    sra      a1, a1, 1
+
+    /* call, r0 gets catchRelPc (a code-unit offset) */
+    JAL(dvmFindCatchBlock)           # call(self, relPc, exc, scan?, &fp)
+    lw        rFP, 20(sp)            # retrieve the updated rFP
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    move      a0, v0
+    bltz      v0, .LnotCaughtLocally
+
+    /* fix earlier stack overflow if necessary; Preserve a0 */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 1f
+    move      rBIX, a0
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)
+    move      a0, rBIX
+
+1:
+
+/* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP(a1, rFP)           # a1<- new save area
+    lw        a1, offStackSaveArea_method(a1)
+    sw        a1, offThread_method(rSELF)
+    lw        a2, offMethod_clazz(a1)
+    lw        a3, offMethod_insns(a1)
+    lw        a2, offClassObject_pDvmDex(a2)
+    EAS1(rPC, a3, a0)
+    sw        a2, offThread_methodClassDex(rSELF)
+
+    /* release the tracked alloc on the exception */
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+
+    /* restore the exception if the handler wants it */
+    lw        rIBASE, offThread_curHandlerTable(rSELF)
+    FETCH_INST()
+    GET_INST_OPCODE(t0)
+    bne       t0, OP_MOVE_EXCEPTION, 2f
+    sw        rOBJ, offThread_exception(rSELF)
+2:
+    GOTO_OPCODE(t0)
+
+    # Manage debugger bookkeeping
+7:
+    sw        rPC, offThread_pc(rSELF)
+    sw        rFP, offThread_curFrame(rSELF)
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmReportExceptionThrow)
+    b         8b
+
+.LnotCaughtLocally:                     #  rOBJ = exception
+    /* fix stack overflow if necessary */
+    lbu       a1, offThread_stackOverflowed(rSELF)
+    beqz      a1, 3f
+    move      a0, rSELF
+    move      a1, rOBJ
+    JAL(dvmCleanupStackOverflow)           #  dvmCleanupStackOverflow(self, exception)
+
+3:
+    # may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+    /* call __android_log_print(prio, tag, format, ...) */
+    /* "Exception %s from %s:%d not caught locally" */
+    lw        a0, offThread_method(rSELF)
+    lw        a1, offMethod_insns(a0)
+    subu      a1, rPC, a1
+    sra       a1, a1, 1
+    JAL(dvmLineNumFromPC)
+    sw        v0, 20(sp)
+    # dvmGetMethodSourceFile(method)
+    lw        a0, offThread_method(rSELF)
+    JAL(dvmGetMethodSourceFile)
+    sw        v0, 16(sp)
+    # exception->clazz->descriptor
+    lw        a3, offObject_clazz(rOBJ)
+    lw        a3, offClassObject_descriptor(a3)
+    la        a2, .LstrExceptionNotCaughtLocally
+    la        a1, .LstrLogTag
+    li        a0, 3
+    JAL(__android_log_print)
+#endif
+    sw        rOBJ, offThread_exception(rSELF)
+    move      a0, rOBJ
+    move      a1, rSELF
+    JAL(dvmReleaseTrackedAlloc)
+    b         common_gotoBail
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_TO_SELF()                # export state
+    SAVE_FP_TO_SELF()
+    move     a0, rSELF               # arg to function
+    JAL(dvmMterp_exceptionThrown)
+    b       common_resumeAfterGlueCall
+    .endif
+
+#if defined(WITH_JIT)
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     rBIX: &dvmDex->pResFields[field]
+     *     a0:  field pointer (must preserve)
+     */
+common_verifyField:
+     lhu     a3, offThread_subMode(rSELF)
+     andi    a3, kSubModeJitTraceBuild
+     bnez    a3, 1f                 # Not building trace, continue
+     jr      ra
+1:
+     lw      a1, (rBIX)
+     beqz    a1, 2f                 # resolution complete ?
+     jr      ra
+2:
+    SCRATCH_STORE(a0, 0)
+    SCRATCH_STORE(a1, 4)
+    SCRATCH_STORE(a2, 8)
+    SCRATCH_STORE(a3, 12)
+    SCRATCH_STORE(ra, 16)
+    move    a0, rSELF
+    move    a1, rPC
+    JAL(dvmJitEndTraceSelect)        #(self,pc) end trace before this inst)
+    SCRATCH_LOAD(a0, 0)
+    SCRATCH_LOAD(a1, 4)
+    SCRATCH_LOAD(a2, 8)
+    SCRATCH_LOAD(a3, 12)
+    SCRATCH_LOAD(ra, 16)
+    jr      ra                       # return
+#endif
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_SELF()           #  pull rPC and rFP out of thread
+    lw      rIBASE, offThread_curHandlerTable(rSELF) # refresh
+    FETCH_INST()                     #  load rINST from rPC
+    GET_INST_OPCODE(t0)              #  extract opcode from rINST
+    GOTO_OPCODE(t0)                  #  jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use a1
+ * and a3 because those just happen to be the registers all our callers are
+ * using. We move a3 before calling the C function, but a1 happens to match.
+ * a1: index
+ * a3: size
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    move      a0, a3
+    JAL(dvmThrowArrayIndexOutOfBoundsException)
+    b         common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    la     a0, .LstrDivideByZero
+    JAL(dvmThrowArithmeticException)
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in a1
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    move    a0, a1                                # arg0 <- len
+    JAL(dvmThrowNegativeArraySizeException)    # (len)
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in a1
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    move     a0, a1
+    JAL(dvmThrowNoSuchMethodError)
+    b       common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one.  We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+    EXPORT_PC()
+    li      a0, 0
+    JAL(dvmThrowNullPointerException)
+    b       common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will be in ra. Use a jal to jump here.
+ */
+common_abort:
+    lw      zero,-4(zero)            #  generate SIGSEGV
+
+/*
+ * Spit out a "we were here", preserving all registers.
+ */
+    .macro SQUEAK num
+common_squeak\num:
+    STACK_STORE_RA();
+    la        a0, .LstrSqueak
+    LOAD_IMM(a1, \num);
+    JAL(printf);
+    STACK_LOAD_RA();
+    RETURN;
+    .endm
+
+    SQUEAK 0
+    SQUEAK 1
+    SQUEAK 2
+    SQUEAK 3
+    SQUEAK 4
+    SQUEAK 5
+
+/*
+ * Spit out the number in a0, preserving registers.
+ */
+common_printNum:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrSqueak
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    STACK_STORE_RA()
+    la        a0, .LstrNewline
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN
+
+    /*
+     * Print the 32-bit quantity in a0 as a hex value, preserving registers.
+     */
+common_printHex:
+    STACK_STORE_RA()
+    MOVE_REG(a1, a0)
+    la        a0, .LstrPrintHex
+    JAL(printf)
+    STACK_LOAD_RA()
+RETURN;
+
+/*
+ * Print the 64-bit quantity in a0-a1, preserving registers.
+ */
+common_printLong:
+    STACK_STORE_RA()
+    MOVE_REG(a3, a1)
+    MOVE_REG(a2, a0)
+    la        a0, .LstrPrintLong
+    JAL(printf)
+    STACK_LOAD_RA()
+    RETURN;
+
+/*
+ * Print full method info.  Pass the Method* in a0.  Preserves regs.
+ */
+common_printMethod:
+    STACK_STORE_RA()
+    JAL(dvmMterpPrintMethod)
+    STACK_LOAD_RA()
+    RETURN
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info.  Requires the C function to be compiled in.
+ */
+    .if 0
+common_dumpRegs:
+    STACK_STORE_RA()
+    JAL(dvmMterpDumpMipsRegs)
+    STACK_LOAD_RA()
+    RETURN
+    .endif
+
+/*
+ * Zero-terminated ASCII string data.
+ */
+    .data
+
+.LstrBadEntryPoint:
+    .asciiz "Bad entry point %d\n"
+.LstrDivideByZero:
+    .asciiz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciiz "filled-new-array only implemented for 'int'"
+.LstrLogTag:
+    .asciiz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciiz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciiz "\n"
+.LstrSqueak:
+    .asciiz "<%d>"
+.LstrPrintHex:
+    .asciiz "<0x%x>"
+.LstrPrintLong:
+    .asciiz "<%lld>"
+
+
+    .global dvmAsmAltInstructionStart
+    .type   dvmAsmAltInstructionStart, %function
+    .text
+
+dvmAsmAltInstructionStart = .L_ALT_OP_NOP
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (0 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (1 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (2 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (3 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (4 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (5 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (6 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (7 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (8 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (9 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (10 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (11 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (12 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (13 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (14 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (15 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (16 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (17 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (18 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (19 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (20 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (21 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (22 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (23 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (24 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (25 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (26 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (27 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (28 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (29 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (30 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (31 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (32 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (33 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (34 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (35 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (36 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (37 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (38 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (39 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (40 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (41 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (42 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (43 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (44 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (45 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (46 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (47 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (48 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (49 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (50 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (51 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (52 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (53 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (54 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (55 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (56 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (57 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (58 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (59 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (60 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (61 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (62 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (63 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (64 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (65 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (66 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (67 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (68 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (69 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (70 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (71 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (72 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (73 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (74 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (75 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (76 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (77 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (78 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (79 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (80 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (81 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (82 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (83 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (84 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (85 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (86 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (87 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (88 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (89 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (90 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (91 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (92 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (93 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (94 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (95 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (96 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (97 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (98 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (99 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (100 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (101 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (102 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (103 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (104 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (105 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (106 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (107 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (108 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (109 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (110 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (111 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (112 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (113 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (114 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (115 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (116 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (117 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (118 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (119 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (120 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (121 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (122 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (123 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (124 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (125 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (126 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (127 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (128 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (129 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (130 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (131 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (132 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (133 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (134 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (135 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (136 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (137 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (138 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (139 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (140 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (141 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (142 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (143 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (144 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (145 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (146 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (147 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (148 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (149 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (150 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (151 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (152 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (153 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (154 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (155 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (156 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (157 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (158 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (159 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (160 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (161 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (162 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (163 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (164 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (165 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (166 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (167 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (168 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (169 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (170 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (171 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (172 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (173 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (174 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (175 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (176 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (177 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (178 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (179 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (180 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (181 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (182 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (183 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (184 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (185 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (186 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (187 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (188 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (189 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (190 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (191 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (192 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (193 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (194 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (195 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (196 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (197 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (198 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (199 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (200 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (201 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (202 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (203 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (204 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (205 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (206 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (207 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (208 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (209 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (210 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (211 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (212 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (213 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (214 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (215 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (216 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (217 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (218 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (219 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (220 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (221 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (222 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (223 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (224 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (225 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (226 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (227 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (228 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (229 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (230 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (231 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (232 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (233 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (234 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (235 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (236 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (237 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (238 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (239 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (240 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (241 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (242 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (243 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (244 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (245 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (246 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (247 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (248 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (249 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (250 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (251 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (252 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (253 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (254 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+/* ------------------------------ */
+    .balign 128
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: mips/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.    Note that the call to dvmCheckBefore is done as a tail call.
+ * rIBASE updates won't be seen until a refresh, and we can tell we have a
+ * stale rIBASE if breakFlags==0.  Always refresh rIBASE here, and then
+ * bail to the real handler if breakFlags==0.
+ */
+    lbu    a3, offThread_breakFlags(rSELF)
+    la     rBIX, dvmAsmInstructionStart + (255 * 128)
+    lw     rIBASE, offThread_curHandlerTable(rSELF)
+    bnez   a3, 1f
+    jr     rBIX            # nothing to do - jump to real handler
+1:
+    EXPORT_PC()
+    move   a0, rPC         # arg0
+    move   a1, rFP         # arg1
+    move   a2, rSELF       # arg2
+    JAL(dvmCheckBefore)
+    jr     rBIX
+
+    .balign 128
+    .size   dvmAsmAltInstructionStart, .-dvmAsmAltInstructionStart
+    .global dvmAsmAltInstructionEnd
+dvmAsmAltInstructionEnd:
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
new file mode 100644
index 0000000..c87f306
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -0,0 +1,16697 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: x86/header.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+   eax, edx, ecx, st(0)-st(7)
+Callee save set:
+   ebx, esi, edi, ebp
+Return regs:
+   32-bit in eax
+   64-bit in edx:eax (low-order 32 in eax)
+   fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left.  On entry to target, first
+parm is at 4(%esp).  Traditional entry code is:
+
+functEntry:
+    push    %ebp             # save old frame pointer
+    mov     %ebp,%esp        # establish new frame pointer
+    sub     FrameSize,%esp   # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Stack must be 16-byte aligned to support SSE in native code.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers.  Note that each
+will also have an associated spill location (mostly useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      esi   interpreted program counter, used for fetching instructions
+  rFP      edi   interpreted frame pointer, used for accessing locals and args
+  rINSTw   bx    first 16-bit code of current instruction
+  rINSTbl  bl    opcode portion of instruction word
+  rINSTbh  bh    high byte of inst word, usually contains src/tgt reg names
+  rIBASE   edx   base of instruction handler table
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
+   o eax and ecx are scratch, rINSTw/ebx sometimes scratch
+
+*/
+
+#define rSELF    8(%ebp)
+#define rPC      %esi
+#define rFP      %edi
+#define rINST    %ebx
+#define rINSTw   %bx
+#define rINSTbh  %bh
+#define rINSTbl  %bl
+#define rIBASE   %edx
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0)
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EBX_SPILL      (-12)
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rINST_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define TMP_SPILL1     (-32)
+#define TMP_SPILL2     (-36)
+#define TMP_SPILL3     (-20)
+#define LOCAL0_OFFSET  (-44)
+#define LOCAL1_OFFSET  (-48)
+#define LOCAL2_OFFSET  (-52)
+/* Out Arg offsets, relative to %esp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+#if defined(WITH_JIT)
+/* for spill region: increase size by 48 (to keep 16-byte alignment) */
+/* 76 + 48 = 124 */
+#define JIT_SPILL      (-56)
+#define FRAME_SIZE     124
+#else
+#define FRAME_SIZE     76
+#endif
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP1(reg) movl reg,TMP_SPILL1(%ebp)
+#define UNSPILL_TMP1(reg) movl TMP_SPILL1(%ebp),reg
+#define SPILL_TMP2(reg) movl reg,TMP_SPILL2(%ebp)
+#define UNSPILL_TMP2(reg) movl TMP_SPILL2(%ebp),reg
+#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
+#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _self _reg
+    movl    offThread_pJitProfTable(\_self),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _self _reg
+    movl    offThread_jitThreshold(\_self),\_reg
+.endm
+#endif
+
+/* save/restore the PC and/or FP from the self struct */
+.macro SAVE_PC_FP_TO_SELF _reg
+    movl     rSELF,\_reg
+    movl     rPC,offThread_pc(\_reg)
+    movl     rFP,offThread_curFrame(\_reg)
+.endm
+
+.macro LOAD_PC_FP_FROM_SELF
+    movl    rSELF,rFP
+    movl    offThread_pc(rFP),rPC
+    movl    offThread_curFrame(rFP),rFP
+.endm
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects.  Must be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+.macro EXPORT_PC
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+.endm
+
+.macro GET_PC
+    movl     (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP), rPC
+.endm
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+.macro SAVEAREA_FROM_FP _reg
+    leal    -sizeofStackSaveArea(rFP), \_reg
+.endm
+
+/*
+ * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
+ */
+.macro FETCH_INST
+    movzwl    (rPC),rINST
+.endm
+
+/*
+ * Fetch the opcode byte and zero-extend it into _reg.  Must be used
+ * in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_R _reg
+    movzbl    (rPC),\_reg
+.endm
+
+/*
+ * Fetch the opcode byte at _count words offset from rPC and zero-extend
+ * it into _reg.  Must be used in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_OPCODE _count _reg
+    movzbl  \_count*2(rPC),\_reg
+.endm
+
+/*
+ * Fetch the nth instruction word from rPC into rINSTw.  Does not advance
+ * rPC, and _count is in words
+ */
+.macro FETCH_INST_WORD _count
+    movzwl  \_count*2(rPC),rINST
+.endm
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+.macro FETCH_INST_INDEXED _reg
+    movzwl  (rPC,\_reg,2),rINST
+.endm
+
+/*
+ * Advance rPC by instruction count
+ */
+.macro ADVANCE_PC _count
+    leal  2*\_count(rPC),rPC
+.endm
+
+/*
+ * Advance rPC by branch offset in register
+ */
+.macro ADVANCE_PC_INDEXED _reg
+    leal (rPC,\_reg,2),rPC
+.endm
+
+.macro GOTO_NEXT
+     movzx   rINSTbl,%eax
+     movzbl  rINSTbh,rINST
+     jmp     *(rIBASE,%eax,4)
+.endm
+
+   /*
+    * Version of GOTO_NEXT that assumes _reg preloaded with opcode.
+    * Should be paired with FETCH_INST_R
+    */
+.macro GOTO_NEXT_R _reg
+     movzbl  1(rPC),rINST
+     jmp     *(rIBASE,\_reg,4)
+.endm
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+.macro GET_VREG_R _reg _vreg
+    movl     (rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG _reg _vreg
+    movl     \_reg,(rFP,\_vreg,4)
+.endm
+
+.macro GET_VREG_WORD _reg _vreg _offset
+    movl     4*(\_offset)(rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG_WORD _reg _vreg _offset
+    movl     \_reg,4*(\_offset)(rFP,\_vreg,4)
+.endm
+
+#define sReg0 LOCAL0_OFFSET(%ebp)
+#define sReg1 LOCAL1_OFFSET(%ebp)
+#define sReg2 LOCAL2_OFFSET(%ebp)
+
+   /*
+    * x86 JIT Helpers
+    */
+
+    .macro dumpSwitch _regData _regScratch1 _regScratch2
+    .endm
+
+   /*
+    * Hard coded helper values.
+    */
+
+.balign 16
+
+.LdoubNeg:
+    .quad       0x8000000000000000
+
+.L64bits:
+    .quad       0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+    .quad       0x0000000000000000
+.LshiftMask:
+    .quad       0x000000000000003F
+
+.Lvalue64:
+    .quad       0x0000000000000040
+
+.LvaluePosInfLong:
+    .quad       0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+    .quad       0x8000000000000000
+
+.LvalueNanLong:
+    .quad       0x0000000000000000
+
+.LintMin:
+.long   0x80000000
+
+.LintMax:
+.long   0x7FFFFFFF
+
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+
+    .global dvmAsmInstructionStartCode
+    .type   dvmAsmInstructionStartCode, %function
+dvmAsmInstructionStartCode = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+.L_OP_NOP: /* 0x00 */
+/* File: x86/OP_NOP.S */
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE: /* 0x01 */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_16: /* 0x03 */
+/* File: x86/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86/OP_MOVE_WIDE.S */
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzbl    rINSTbl,%ecx                # ecx <- BA
+    sarl      $4,rINST                   # rINST<- B
+    GET_VREG_WORD %eax rINST 0            # eax<- v[B+0]
+    GET_VREG_WORD rINST rINST 1           # rINST<- v[B+1]
+    andb      $0xf,%cl                   # ecx <- A
+    SET_VREG_WORD rINST %ecx 1            # v[A+1]<- rINST
+    SET_VREG_WORD %eax %ecx 0             # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86/OP_MOVE_WIDE_FROM16.S */
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    2(rPC),%ecx              # ecx<- BBBB
+    movzbl    rINSTbl,%eax             # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0         # rINST<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1          # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0         # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1          # v[AAAA+1]<- eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86/OP_MOVE_WIDE_16.S */
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    4(rPC),%ecx            # ecx<- BBBB
+    movzwl    2(rPC),%eax            # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0       # rINSTw_WORD<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1        # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0       # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1        # v[AAAA+1]<- ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86/OP_MOVE_OBJECT.S */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86/OP_MOVE_OBJECT_FROM16.S */
+/* File: x86/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86/OP_MOVE_OBJECT_16.S */
+/* File: x86/OP_MOVE_16.S */
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    movl    rSELF,%ecx
+    movl    offThread_retval(%ecx),%eax
+    movl    4+offThread_retval(%ecx),%ecx
+    SET_VREG_WORD %eax rINST 0     # v[AA+0] <- eax
+    SET_VREG_WORD %ecx rINST 1     # v[AA+1] <- ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86/OP_MOVE_RESULT_OBJECT.S */
+/* File: x86/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    movl    rSELF,%ecx
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG %eax rINST                # fp[AA]<- exception object
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    movl    $0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/OP_RETURN_VOID.S */
+    jmp       common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN: /* 0x0f */
+/* File: x86/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    movl    rSELF,%ecx
+    GET_VREG_WORD %eax rINST 0       # eax<- v[AA+0]
+    GET_VREG_WORD rINST rINST 1      # rINST<- v[AA+1]
+    movl    %eax,offThread_retval(%ecx)
+    movl    rINST,4+offThread_retval(%ecx)
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86/OP_RETURN_OBJECT.S */
+/* File: x86/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+
+/* ------------------------------ */
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    movsx   rINSTbl,%eax              # eax<-ssssssBx
+    movl    $0xf,rINST
+    andl    %eax,rINST                # rINST<- A
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    sarl    $4,%eax
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    SET_VREG %ecx rINST                # vAA<- ssssBBBB
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_CONST: /* 0x14 */
+/* File: x86/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    movl      2(rPC),%eax             # grab all 32 bits at once
+    movl      rINST,rINST             # rINST<- AA
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG %eax rINST               # vAA<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG %eax rINST                   # vAA<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86/OP_CONST_WIDE_16.S */
+    /* const-wide/16 vAA, #+BBBB */
+    movswl    2(rPC),%eax               # eax<- ssssBBBB
+    SPILL(rIBASE)                       # preserve rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST 1        # store msw
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86/OP_CONST_WIDE_32.S */
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    movl     2(rPC),%eax                # eax<- BBBBbbbb
+    SPILL(rIBASE)                       # save rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST,1        # store msw
+    FETCH_INST_OPCODE 3 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    movl      2(rPC),%eax         # eax<- lsw
+    movzbl    rINSTbl,%ecx        # ecx<- AA
+    movl      6(rPC),rINST        # rINST<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST,4(%ecx)
+    movl      %eax,(%ecx)
+    FETCH_INST_OPCODE 5 %ecx
+    ADVANCE_PC 5
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD %eax rINST 1            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD %eax rINST 0            # v[AA+0]<- eax
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86/OP_CONST_STRING.S */
+
+    /* const/string vAA, String@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_STRING_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/OP_CONST_STRING_JUMBO.S */
+
+    /* const/string vAA, String@BBBBBBBB */
+    movl      rSELF,%ecx
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 3 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_JUMBO_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_STRING_JUMBO_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 3 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/OP_CONST_CLASS.S */
+
+    /* const/class vAA, Class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_CLASS_resolve
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST               # vAA<- rResClasses[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.LOP_CONST_CLASS_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     $1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    FETCH_INST_WORD 1
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC                           # need for precise GC
+    je     common_errNullObject
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86/OP_MONITOR_EXIT.S */
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_VREG_R %eax rINST
+    movl    rSELF,%ecx
+    EXPORT_PC
+    testl   %eax,%eax                   # null object?
+    je      .LOP_MONITOR_EXIT_errNullObject   # go if so
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC 1
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT_R %ecx
+.LOP_MONITOR_EXIT_errNullObject:
+    ADVANCE_PC 1                        # advance before throw
+    jmp     common_errNullObject
+
+/* ------------------------------ */
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: x86/OP_CHECK_CAST.S */
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    movl      rSELF,%ecx
+    GET_VREG_R  rINST,rINST             # rINST<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST,rINST               # is oject null?
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    je        .LOP_CHECK_CAST_okay          # null obj, cast always succeeds
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved class
+    movl      offObject_clazz(rINST),%ecx # ecx<- obj->clazz
+    testl     %eax,%eax                 # have we resolved this before?
+    je        .LOP_CHECK_CAST_resolve       # no, go do it now
+.LOP_CHECK_CAST_resolved:
+    cmpl      %eax,%ecx                 # same class (trivial success)?
+    jne       .LOP_CHECK_CAST_fullcheck     # no, do full check
+.LOP_CHECK_CAST_okay:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    movl    %eax,sReg0                 # we'll need the desired class on failure
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmInstanceofNonTrivial    # eax<- boolean result
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # failed?
+    jne     .LOP_CHECK_CAST_okay           # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC
+    movl    offObject_clazz(rINST),%eax
+    movl    %eax,OUT_ARG0(%esp)                 # arg0<- obj->clazz
+    movl    sReg0,%ecx
+    movl    %ecx,OUT_ARG1(%esp)                 # arg1<- desired class
+    call    dvmThrowClassCastException
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)        # arg1<- BBBB
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+    movl    $0,OUT_ARG2(%esp)         # arg2<- false
+    movl    %ecx,OUT_ARG0(%esp)        # arg0<- method->clazz
+    SPILL(rIBASE)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST),%ecx  # ecx<- obj->clazz
+    jmp     .LOP_CHECK_CAST_resolved       # pick up where we left off
+
+/* ------------------------------ */
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86/OP_INSTANCE_OF.S */
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                   # object null?
+    movl    offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)                       # preserve rIBASE
+    je      .LOP_INSTANCE_OF_store           # null obj, not instance, store it
+    movzwl  2(rPC),rIBASE               # rIBASE<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rIBASE,4),%ecx        # ecx<- resolved class
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    testl   %ecx,%ecx                   # have we resolved this before?
+    je      .LOP_INSTANCE_OF_resolve         # not resolved, do it now
+.LOP_INSTANCE_OF_resolved:  # eax<- obj->clazz, ecx<- resolved class
+    cmpl    %eax,%ecx                   # same class (trivial success)?
+    je      .LOP_INSTANCE_OF_trivial         # yes, trivial finish
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST has BA
+     */
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    # fall through to OP_INSTANCE_OF_store
+
+    /*
+     * eax holds boolean result
+     * rINST holds BA
+     */
+.LOP_INSTANCE_OF_store:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    SET_VREG %eax rINST                 # vA<- eax
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    movl    $1,%eax
+    SET_VREG %eax rINST                 # vA<- true
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rIBASE holds BBBB
+     *  rINST holds BA
+     */
+.LOP_INSTANCE_OF_resolve:
+    movl    rIBASE,OUT_ARG1(%esp)         # arg1<- BBBB
+    movl    rSELF,%ecx
+    movl    offThread_method(%ecx),%ecx
+    movl    $1,OUT_ARG2(%esp)          # arg2<- true
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    EXPORT_PC
+    movl    %ecx,OUT_ARG0(%esp)         # arg0<- method->clazz
+    call    dvmResolveClass             # eax<- resolved ClassObject ptr
+    testl   %eax,%eax                   # success?
+    je      common_exceptionThrown      # no, handle exception
+/* Now, we need to sync up with fast path.  We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+    movl    %eax,%ecx                   # ecx<- resolved class
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    jmp     .LOP_INSTANCE_OF_resolved
+
+/* ------------------------------ */
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+   mov      rINST,%eax                # eax<- BA
+   sarl     $4,rINST                 # rINST<- B
+   GET_VREG_R %ecx rINST              # ecx<- vB (object ref)
+   andb     $0xf,%al                 # eax<- A
+   testl    %ecx,%ecx                 # is null?
+   je       common_errNullObject
+   movl     offArrayObject_length(%ecx),rINST
+   FETCH_INST_OPCODE 1 %ecx
+   ADVANCE_PC 1
+   SET_VREG rINST %eax
+   GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)
+    SPILL_TMP2(%ebx)
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC
+#if defined(WITH_JIT)
+    lea       (%ecx,%eax,4),%ebx        # ebx <- &resolved class
+#endif
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    testl     %ecx,%ecx                 # resolved?
+    je        .LOP_NEW_INSTANCE_resolve       # no, go do it
+.LOP_NEW_INSTANCE_resolved:  # on entry, ecx<- class
+    cmpb      $CLASS_INITIALIZED,offClassObject_status(%ecx)
+    jne       .LOP_NEW_INSTANCE_needinit
+.LOP_NEW_INSTANCE_initialized:  # on entry, ecx<- class
+    movl      $ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+#if defined(WITH_JIT)
+        /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    movl    rSELF, %ecx
+    movl    offThread_subMode(%ecx), %ecx
+    andl    $kSubModeJitTraceBuild, %ecx # under construction?
+    jne     .LOP_NEW_INSTANCE_jitCheck
+#endif
+.LOP_NEW_INSTANCE_end:
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * eax: new object
+     */
+.LOP_NEW_INSTANCE_jitCheck:
+    cmp     $0, (%ebx)                   # okay?
+    jne     .LOP_NEW_INSTANCE_end        # yes, finish
+    SPILL_TMP1(%eax)                     # preserve new object
+    movl    rSELF, %ecx
+    movl    %ecx, OUT_ARG0(%esp)
+    movl    rPC, OUT_ARG1(%esp)
+    call    dvmJitEndTraceSelect         # (self, pc)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST                  # vAA <- new object
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    SPILL_TMP1(%ecx)                    # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP1(%ecx)                  # restore object
+    testl   %eax,%eax                   # success?
+    jne     .LOP_NEW_INSTANCE_initialized     # success, continue
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.LOP_NEW_INSTANCE_resolve:
+    movl    rSELF,%ecx
+    movzwl  2(rPC),%eax
+    movl    offThread_method(%ecx),%ecx   # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    movl    $0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass             # call(clazz,off,flags)
+    movl    %eax,%ecx                   # ecx<- resolved ClassObject ptr
+    testl   %ecx,%ecx                   # success?
+    jne     .LOP_NEW_INSTANCE_resolved        # good to go
+    jmp     common_exceptionThrown      # no, handle exception
+
+/* ------------------------------ */
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86/OP_NEW_ARRAY.S */
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movl    offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    SPILL(rIBASE)
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINSTbl,%eax
+    sarl    $4,%eax                          # eax<- B
+    GET_VREG_R %eax %eax                      # eax<- vB (array length)
+    andb    $0xf,rINSTbl                     # rINST<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail, passing len in eax
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .LOP_NEW_ARRAY_finish                # yes, fast path
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+    movl    rSELF,%ecx
+    SPILL_TMP1(%eax)                   # save array length
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movzwl  2(rPC),%eax                # eax<- CCCC
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    movl    %eax,%ecx
+    UNSPILL_TMP1(%eax)
+    testl   %ecx,%ecx                  # successful resolution?
+    je      common_exceptionThrown     # no, bail.
+# fall through to OP_NEW_ARRAY_finish
+
+    /*
+     * Finish allocation
+     *
+     * ecx holds class
+     * eax holds array length (vB)
+     */
+.LOP_NEW_ARRAY_finish:
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .LOP_FILLED_NEW_ARRAY_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .LOP_FILLED_NEW_ARRAY_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.LOP_FILLED_NEW_ARRAY_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $'I',%cl                             # supported?
+    je      1f
+    cmpb    $'L',%cl
+    je      1f
+    cmpb    $'[',%cl
+    jne      .LOP_FILLED_NEW_ARRAY_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!0)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 0
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    movl    $.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .LOP_FILLED_NEW_ARRAY_RANGE_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $'I',%cl                             # supported?
+    je      1f
+    cmpb    $'L',%cl
+    je      1f
+    cmpb    $'[',%cl
+    jne      .LOP_FILLED_NEW_ARRAY_RANGE_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!1)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 1
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    movl    $.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
+
+
+/* ------------------------------ */
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG_R %eax rINST
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_THROW: /* 0x27 */
+/* File: x86/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    EXPORT_PC
+    GET_VREG_R %eax rINST              # eax<- exception object
+    movl     rSELF,%ecx                # ecx<- self
+    testl    %eax,%eax                 # null object?
+    je       common_errNullObject
+    movl     %eax,offThread_exception(%ecx) # thread->exception<- obj
+    jmp      common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_GOTO: /* 0x28 */
+/* File: x86/OP_GOTO.S */
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    movl    rSELF,%ecx
+    movsbl  rINSTbl,%eax          # eax<- ssssssAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_GOTO_16: /* 0x29 */
+/* File: x86/OP_GOTO_16.S */
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset
+     */
+    /* goto/16 +AAAA */
+    movl    rSELF,%ecx
+    movswl  2(rPC),%eax            # eax<- ssssAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_GOTO_32: /* 0x2a */
+/* File: x86/OP_GOTO_32.S */
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset.
+     */
+    /* goto/32 AAAAAAAA */
+    movl    rSELF,%ecx
+    movl    2(rPC),%eax            # eax<- AAAAAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    dvmInterpHandlePackedSwitch
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+/* ------------------------------ */
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86/OP_SPARSE_SWITCH.S */
+/* File: x86/OP_PACKED_SWITCH.S */
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    dvmInterpHandleSparseSwitch
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86/OP_CMPL_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 0
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPL_FLOAT_isNaN
+    je       .LOP_CMPL_FLOAT_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPL_FLOAT_finish
+    incl     %ecx
+.LOP_CMPL_FLOAT_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPL_FLOAT_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_FLOAT_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86/OP_CMPG_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 0
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPG_FLOAT_isNaN
+    je       .LOP_CMPG_FLOAT_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPG_FLOAT_finish
+    incl     %ecx
+.LOP_CMPG_FLOAT_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPG_FLOAT_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_FLOAT_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86/OP_CMPL_DOUBLE.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 1
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPL_DOUBLE_isNaN
+    je       .LOP_CMPL_DOUBLE_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPL_DOUBLE_finish
+    incl     %ecx
+.LOP_CMPL_DOUBLE_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPL_DOUBLE_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_DOUBLE_finish
+
+
+/* ------------------------------ */
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86/OP_CMPG_DOUBLE.S */
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if 1
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .LOP_CMPG_DOUBLE_isNaN
+    je       .LOP_CMPG_DOUBLE_finish
+    sbbl     %ecx,%ecx
+    jb       .LOP_CMPG_DOUBLE_finish
+    incl     %ecx
+.LOP_CMPG_DOUBLE_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.LOP_CMPG_DOUBLE_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_DOUBLE_finish
+
+/* ------------------------------ */
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: x86/OP_CMP_LONG.S */
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     */
+    // TUNING: rework to avoid rIBASE spill
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)
+    movzbl    3(rPC),rIBASE            # rIBASE- CC
+    GET_VREG_WORD %eax %ecx,1          # eax<- v[BB+1]
+    GET_VREG_WORD %ecx %ecx 0          # ecx<- v[BB+0]
+    cmpl      4(rFP,rIBASE,4),%eax
+    jl        .LOP_CMP_LONG_smaller
+    jg        .LOP_CMP_LONG_bigger
+    sub       (rFP,rIBASE,4),%ecx
+    ja        .LOP_CMP_LONG_bigger
+    jb        .LOP_CMP_LONG_smaller
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_CMP_LONG_bigger:
+    movl      $1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_CMP_LONG_smaller:
+    movl      $-1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IF_EQ: /* 0x32 */
+/* File: x86/OP_IF_EQ.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jne   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_NE: /* 0x33 */
+/* File: x86/OP_IF_NE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    je   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LT: /* 0x34 */
+/* File: x86/OP_IF_LT.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jge   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GE: /* 0x35 */
+/* File: x86/OP_IF_GE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jl   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GT: /* 0x36 */
+/* File: x86/OP_IF_GT.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jle   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LE: /* 0x37 */
+/* File: x86/OP_IF_LE.S */
+/* File: x86/bincmp.S */
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $2,%eax              # assume not taken
+    jg   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: x86/OP_IF_EQZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jne   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: x86/OP_IF_NEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    je   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: x86/OP_IF_LTZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jge   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: x86/OP_IF_GEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jl   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: x86/OP_IF_GTZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jle   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: x86/OP_IF_LEZ.S */
+/* File: x86/zcmp.S */
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $2,%eax              # assume branch not taken
+    jg   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/OP_UNUSED_3E.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/OP_UNUSED_3F.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86/OP_UNUSED_40.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86/OP_UNUSED_41.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86/OP_UNUSED_42.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86/OP_UNUSED_43.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_AGET: /* 0x44 */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_AGET_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: x86/OP_AGET_WIDE.S */
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD %ecx rINST 0
+    SET_VREG_WORD %eax rINST 1
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86/OP_AGET_OBJECT.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_AGET_OBJECT_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86/OP_AGET_BOOLEAN.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movzbl     offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_AGET_BOOLEAN_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: x86/OP_AGET_BYTE.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movsbl     offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_AGET_BYTE_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: x86/OP_AGET_CHAR.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movzwl     offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_AGET_CHAR_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: x86/OP_AGET_SHORT.S */
+/* File: x86/OP_AGET.S */
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    movswl     offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_AGET_SHORT_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT: /* 0x4b */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,4),%eax
+.LOP_APUT_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movl     rINST,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: x86/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC]<-vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD %ecx rINST 0
+    GET_VREG_WORD rINST rINST 1
+    movl      %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86/OP_APUT_OBJECT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    GET_VREG_R  rINST rINST             # rINST<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST<- vAA
+     */
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST,rINST                    # storing null reference?
+    je        .LOP_APUT_OBJECT_skip_check
+    SPILL_TMP1(%ecx)                         # save target address
+    SPILL_TMP2(%eax)                         # save object head
+    movl      offObject_clazz(%eax),%eax     # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST),%ecx    # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    movl      %ecx,sReg0                     # store the two classes for later
+    movl      %eax,sReg1
+    SPILL(rIBASE)
+    call      dvmCanPutArrayElement          # test object type vs. array type
+    UNSPILL(rIBASE)
+    UNSPILL_TMP1(%ecx)                       # recover target address
+    testl     %eax,%eax
+    movl      rSELF,%eax
+    jne       .LOP_APUT_OBJECT_types_okay
+
+    # The types don't match.  We need to throw an ArrayStoreException.
+    EXPORT_PC
+    movl      sReg0,%eax                     # restore the two classes...
+    movl      %eax,OUT_ARG0(%esp)
+    movl      sReg1,%ecx
+    movl      %ecx,OUT_ARG1(%esp)
+    call      dvmThrowArrayStoreExceptionIncompatibleElement # ...and throw
+    jmp       common_exceptionThrown
+
+.LOP_APUT_OBJECT_types_okay:
+    movl      offThread_cardTable(%eax),%eax   # get card table base
+    movl      rINST,(%ecx)                   # store into array
+    UNSPILL_TMP2(rINST)                      # recover object head
+    FETCH_INST_OPCODE 2 %ecx
+    shrl      $GC_CARD_SHIFT,rINST          # object head to card number
+    movb      %al,(%eax,rINST)               # mark card using object head
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_APUT_OBJECT_skip_check:
+    movl      rINST,(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86/OP_APUT_BOOLEAN.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_APUT_BOOLEAN_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movb     rINSTbl,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: x86/OP_APUT_BYTE.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+.LOP_APUT_BYTE_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movb     rINSTbl,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: x86/OP_APUT_CHAR.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_APUT_CHAR_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movw     rINSTw,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: x86/OP_APUT_SHORT.S */
+/* File: x86/OP_APUT.S */
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+.LOP_APUT_SHORT_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    movw     rINSTw,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IGET: /* 0x52 */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/OP_IGET_WIDE.S */
+    /*
+     * 64-bit instance field get.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_WIDE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # for dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save objpointer across call
+    movl    rPC,OUT_ARG0(%esp)                  # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IGET_WIDE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_WIDE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    movl    (%eax),%ecx                         # ecx<- lsw
+    movl    4(%eax),%eax                        # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                             # restore rIBASE
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86/OP_IGET_OBJECT.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_OBJECT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_OBJECT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_OBJECT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86/OP_IGET_BOOLEAN.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_BOOLEAN_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movzbl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: x86/OP_IGET_BYTE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_BYTE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movsbl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: x86/OP_IGET_CHAR.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_CHAR_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movzwl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: x86/OP_IGET_SHORT.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_SHORT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movswl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IPUT: /* 0x59 */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/OP_IPUT_WIDE.S */
+    /*
+     * 64-bit instance field put.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_WIDE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_WIDE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_WIDE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    GET_VREG_WORD %ecx rINST 0                  # ecx<- lsw
+    GET_VREG_WORD rINST rINST 1                 # rINST<- msw
+    movl    rINST,4(%eax)
+    movl    %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/OP_IPUT_OBJECT.S */
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_OBJECT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_OBJECT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_OBJECT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86/OP_IPUT_BOOLEAN.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_BOOLEAN_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINSTbl,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86/OP_IPUT_BYTE.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_BYTE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINSTbl,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86/OP_IPUT_CHAR.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_CHAR_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINSTw,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86/OP_IPUT_SHORT.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_SHORT_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINSTw,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET: /* 0x60 */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_resolve                # if not, make it so
+.LOP_SGET_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     *
+     */
+    /* sget-wide vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_WIDE_resolve                # if not, make it so
+.LOP_SGET_WIDE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%ecx    # ecx<- lsw
+    movl      4+offStaticField_value(%eax),%eax  # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_WIDE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_WIDE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86/OP_SGET_OBJECT.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_OBJECT_resolve                # if not, make it so
+.LOP_SGET_OBJECT_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_OBJECT_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86/OP_SGET_BOOLEAN.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_BOOLEAN_resolve                # if not, make it so
+.LOP_SGET_BOOLEAN_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_BOOLEAN_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: x86/OP_SGET_BYTE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_BYTE_resolve                # if not, make it so
+.LOP_SGET_BYTE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BYTE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_BYTE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: x86/OP_SGET_CHAR.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_CHAR_resolve                # if not, make it so
+.LOP_SGET_CHAR_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_CHAR_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_CHAR_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: x86/OP_SGET_SHORT.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_SHORT_resolve                # if not, make it so
+.LOP_SGET_SHORT_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_SHORT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_SHORT_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT: /* 0x67 */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_resolve                # if not, make it so
+.LOP_SPUT_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_finish                 # success, continue
+/* ------------------------------ */
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86/OP_SPUT_WIDE.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_WIDE_resolve                # if not, make it so
+.LOP_SPUT_WIDE_finish:     # field ptr in eax
+    GET_VREG_WORD %ecx rINST 0                  # rINST<- lsw
+    GET_VREG_WORD rINST rINST 1                 # ecx<- msw
+    movl      %ecx,offStaticField_value(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4+offStaticField_value(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_WIDE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_WIDE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/OP_SPUT_OBJECT.S */
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_OBJECT_resolve                # if not, make it so
+.LOP_SPUT_OBJECT_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_SPUT_OBJECT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_OBJECT_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86/OP_SPUT_BOOLEAN.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_BOOLEAN_resolve                # if not, make it so
+.LOP_SPUT_BOOLEAN_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_BOOLEAN_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86/OP_SPUT_BYTE.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_BYTE_resolve                # if not, make it so
+.LOP_SPUT_BYTE_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BYTE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_BYTE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86/OP_SPUT_CHAR.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_CHAR_resolve                # if not, make it so
+.LOP_SPUT_CHAR_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_CHAR_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_CHAR_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86/OP_SPUT_SHORT.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_SHORT_resolve                # if not, make it so
+.LOP_SPUT_SHORT_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_SHORT_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_SHORT_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .LOP_INVOKE_VIRTUAL_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .LOP_INVOKE_VIRTUAL_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.LOP_INVOKE_VIRTUAL_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!0)
+    andl      $0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!0)
+    andl      $0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .LOP_INVOKE_SUPER_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.LOP_INVOKE_SUPER_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .LOP_INVOKE_SUPER_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethodNoRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .LOP_INVOKE_SUPER_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.LOP_INVOKE_SUPER_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
+
+/* ------------------------------ */
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!0)
+    andl      $0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodNoRange  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.LOP_INVOKE_DIRECT_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_finish
+     jmp      common_exceptionThrown
+
+/* ------------------------------ */
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethodNoRange
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $0, %ecx                 # make "this" null
+    je        common_invokeMethodNoRange # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethodNoRange # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethodNoRange
+#else
+    movl      $0, %ecx                 # make "this" null
+    jne       common_invokeMethodNoRange
+    jmp       common_exceptionThrown
+#endif
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!0)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86/OP_UNUSED_73.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .LOP_INVOKE_VIRTUAL_RANGE_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .LOP_INVOKE_VIRTUAL_RANGE_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!1)
+    andl      $0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86/OP_INVOKE_SUPER_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER.S */
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!1)
+    andl      $0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .LOP_INVOKE_SUPER_RANGE_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .LOP_INVOKE_SUPER_RANGE_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethodRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .LOP_INVOKE_SUPER_RANGE_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86/OP_INVOKE_DIRECT_RANGE.S */
+/* File: x86/OP_INVOKE_DIRECT.S */
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!1)
+    andl      $0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_RANGE_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodRange  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_RANGE_finish
+     jmp      common_exceptionThrown
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86/OP_INVOKE_STATIC_RANGE.S */
+/* File: x86/OP_INVOKE_STATIC.S */
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethodRange
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $0, %ecx                 # make "this" null
+    je        common_invokeMethodRange # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethodRange # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethodRange
+#else
+    movl      $0, %ecx                 # make "this" null
+    jne       common_invokeMethodRange
+    jmp       common_exceptionThrown
+#endif
+
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!1)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86/OP_UNUSED_79.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/OP_UNUSED_7A.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+.L_OP_NEG_INT: /* 0x7b */
+/* File: x86/OP_NEG_INT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    negl %eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NOT_INT: /* 0x7c */
+/* File: x86/OP_NOT_INT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    notl %eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86/OP_NEG_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx        # ecx<- BA
+    sarl      $4,%ecx            # ecx<- B
+    andb      $0xf,rINSTbl       # rINST<- A
+    GET_VREG_WORD %eax %ecx 0     # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1     # ecx<- v[B+1]
+    negl      %eax
+    adcl      $0,%ecx
+    negl      %ecx
+    SET_VREG_WORD %eax rINST 0    # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1    # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86/OP_NOT_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- BA
+    sarl      $4,%ecx           # ecx<- B
+    andb      $0xf,rINSTbl      # rINST<- A
+    GET_VREG_WORD %eax %ecx 0    # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1    # ecx<- v[B+1]
+    notl      %eax
+    notl      %ecx
+    SET_VREG_WORD %eax rINST 0   # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1   # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86/OP_NEG_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    flds    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    fchs
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86/OP_NEG_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fldl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    fchs
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/OP_INT_TO_LONG.S */
+    /* int to long vA, vB */
+    movzbl  rINSTbl,%eax                # eax<- +A
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    andb    $0xf,rINSTbl               # rINST<- A
+    SPILL(rIBASE)                       # cltd trashes rIBASE/edx
+    cltd                                # rINST:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD rIBASE rINST 1        # v[A+1]<- rIBASE/rPC
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[A+0]<- %eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86/OP_INT_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86/OP_INT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86/OP_LONG_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildll    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86/OP_LONG_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fildll    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86/OP_FLOAT_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 0
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 0
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 0
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_FLOAT_TO_INT_special_case # fix up result
+
+.LOP_FLOAT_TO_INT_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_FLOAT_TO_INT_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_FLOAT_TO_INT_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 0
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_FLOAT_TO_INT_finish
+.LOP_FLOAT_TO_INT_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 0
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_FLOAT_TO_INT_finish
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86/OP_FLOAT_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 0
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 1
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 1
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_FLOAT_TO_LONG_special_case # fix up result
+
+.LOP_FLOAT_TO_LONG_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_FLOAT_TO_LONG_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_FLOAT_TO_LONG_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 1
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_FLOAT_TO_LONG_finish
+.LOP_FLOAT_TO_LONG_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 1
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_FLOAT_TO_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86/OP_FLOAT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    flds    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstpl  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86/OP_DOUBLE_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 1
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 0
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 0
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_DOUBLE_TO_INT_special_case # fix up result
+
+.LOP_DOUBLE_TO_INT_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DOUBLE_TO_INT_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_DOUBLE_TO_INT_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 0
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_DOUBLE_TO_INT_finish
+.LOP_DOUBLE_TO_INT_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 0
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_DOUBLE_TO_INT_finish
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86/OP_DOUBLE_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $4,rINST         # rINST<- B
+    .if 1
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $0xf,%cl                # ecx<- A
+    .if 1
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if 1
+    movl     $0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .LOP_DOUBLE_TO_LONG_special_case # fix up result
+
+.LOP_DOUBLE_TO_LONG_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DOUBLE_TO_LONG_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .LOP_DOUBLE_TO_LONG_isNaN
+    adcl     $-1,(rFP,%ecx,4)
+    .if 1
+    adcl     $-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .LOP_DOUBLE_TO_LONG_finish
+.LOP_DOUBLE_TO_LONG_isNaN:
+    movl      $0,(rFP,%ecx,4)
+    .if 1
+    movl      $0,4(rFP,%ecx,4)
+    .endif
+    jmp       .LOP_DOUBLE_TO_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86/OP_DOUBLE_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $4,rINST         # rINST<- B
+    fldl    (rFP,rINST,4)      # %st0<- vB
+    andb     $0xf,%cl          # ecx<- A
+    
+    fstps  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86/OP_INT_TO_BYTE.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movsbl %al,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86/OP_INT_TO_CHAR.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movzwl %ax,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86/OP_INT_TO_SHORT.S */
+/* File: x86/unop.S */
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $0xf,%cl              # ecx<- A
+    
+    
+    movswl %ax,%eax
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_INT: /* 0x90 */
+/* File: x86/OP_ADD_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    addl (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_INT: /* 0x91 */
+/* File: x86/OP_SUB_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    subl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT: /* 0x92 */
+/* File: x86/OP_MUL_INT.S */
+    /*
+     * 32-bit binary multiplication.
+     */
+    /* mul vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    SPILL(rIBASE)
+    imull    (rFP,%ecx,4),%eax      # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT: /* 0x93 */
+/* File: x86/OP_DIV_INT.S */
+/* File: x86/bindiv.S */
+
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT: /* 0x94 */
+/* File: x86/OP_REM_INT.S */
+/* File: x86/bindiv.S */
+
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT: /* 0x95 */
+/* File: x86/OP_AND_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    andl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT: /* 0x96 */
+/* File: x86/OP_OR_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    orl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT: /* 0x97 */
+/* File: x86/OP_XOR_INT.S */
+/* File: x86/binop.S */
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    xorl   (rFP,%ecx,4),%eax                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT: /* 0x98 */
+/* File: x86/OP_SHL_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    sall    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT: /* 0x99 */
+/* File: x86/OP_SHR_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    sarl    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT: /* 0x9a */
+/* File: x86/OP_USHR_INT.S */
+/* File: x86/binop1.S */
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    shrl    %cl,%eax                          # ex: addl    %ecx,%eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: x86/OP_ADD_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    addl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    adcl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: x86/OP_SUB_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    subl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    sbbl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: x86/OP_MUL_LONG.S */
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * We could definately use more free registers for
+     * this code.   We spill rINSTw (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill edi (rFP)
+     * for use as the vB pointer and esi (rPC) for use
+     * as the vC pointer.  Yuck.
+     */
+    /* mul-long vAA, vBB, vCC */
+    movzbl    2(rPC),%eax              # eax<- B
+    movzbl    3(rPC),%ecx              # ecx<- C
+    SPILL_TMP2(%esi)                   # save Dalvik PC
+    SPILL(rFP)
+    SPILL(rINST)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # esi<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(%esi),%ecx             # ecx<- Bmsw
+    imull     (rFP),%ecx               # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax              # eax<- Cmsw
+    imull     (%esi),%eax              # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx                # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax               # eax<- Clsw
+    mull      (%esi)                   # eax<- (Clsw*Alsw)
+    UNSPILL(rINST)
+    UNSPILL(rFP)
+    leal      (%ecx,rIBASE),rIBASE # full result now in rIBASE:%eax
+    UNSPILL_TMP2(%esi)             # Restore Dalvik PC
+    FETCH_INST_OPCODE 2 %ecx       # Fetch next instruction
+    movl      rIBASE,4(rFP,rINST,4)# v[B+1]<- rIBASE
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,rINST,4)   # v[B]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: x86/OP_DIV_LONG.S */
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_DIV_LONG_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_DIV_LONG_check_neg1
+.LOP_DIV_LONG_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.LOP_DIV_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __divdi3
+.LOP_DIV_LONG_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_LONG_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_DIV_LONG_notSpecial
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_DIV_LONG_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_DIV_LONG_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_DIV_LONG_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0x80000000,rIBASE
+    jmp      .LOP_DIV_LONG_finish
+
+/* ------------------------------ */
+.L_OP_REM_LONG: /* 0x9f */
+/* File: x86/OP_REM_LONG.S */
+/* File: x86/OP_DIV_LONG.S */
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_REM_LONG_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_REM_LONG_check_neg1
+.LOP_REM_LONG_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.LOP_REM_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __moddi3
+.LOP_REM_LONG_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_LONG_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_REM_LONG_notSpecial
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_REM_LONG_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_REM_LONG_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_REM_LONG_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0,rIBASE
+    jmp      .LOP_REM_LONG_finish
+
+
+/* ------------------------------ */
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: x86/OP_AND_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    andl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    andl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: x86/OP_OR_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    orl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    orl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: x86/OP_XOR_LONG.S */
+/* File: x86/binopWide.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    xorl (rFP,%ecx,4),rIBASE         # ex: addl   (rFP,%ecx,4),rIBASE
+    xorl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: x86/OP_SHL_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shl-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rINST */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # ecx<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: x86/OP_SHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: x86/OP_USHR_LONG.S */
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R  %ecx %ecx               # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1          # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[BB+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86/OP_ADD_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fadds   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86/OP_SUB_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fsubs   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86/OP_MUL_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fmuls   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86/OP_DIV_FLOAT.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    flds    (rFP,%eax,4)         # vCC to fp stack
+    fdivs   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstps   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: x86/OP_REM_FLOAT.S */
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    flds     (rFP,%ecx,4)           # vCC to fp stack
+    flds     (rFP,%eax,4)           # vCC to fp stack
+    movzbl   rINSTbl,%ecx           # ecx<- AA
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/OP_ADD_DOUBLE.S */
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    movq     (rFP, %eax, 4), %xmm0      # %xmm0<- vBB
+    movq     (rFP, %ecx, 4), %xmm1      # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    addsd    %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq     %xmm0, (rFP, rINST, 4)     # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/OP_SUB_DOUBLE.S */
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    subsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB - vCC
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/OP_MUL_DOUBLE.S */
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    mulsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86/OP_DIV_DOUBLE.S */
+/* File: x86/binflop.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    fldl    (rFP,%eax,4)         # vCC to fp stack
+    fdivl   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    fstpl   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86/OP_REM_DOUBLE.S */
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    fldl     (rFP,%ecx,4)           # vCC to fp stack
+    fldl     (rFP,%eax,4)           # vCC to fp stack
+    FETCH_INST_OPCODE 2 %ecx
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC 2
+    fstpl    (rFP,rINST,4)           # %st to vAA
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86/OP_ADD_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    addl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86/OP_SUB_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    subl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/OP_MUL_INT_2ADDR.S */
+    /* mul vA, vB */
+    movzx   rINSTbl,%ecx              # ecx<- A+
+    sarl    $4,rINST                 # rINST<- B
+    GET_VREG_R %eax rINST             # eax<- vB
+    andb    $0xf,%cl                 # ecx<- A
+    SPILL(rIBASE)
+    imull   (rFP,%ecx,4),%eax         # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86/OP_DIV_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_2ADDR_continue_div2addr
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_2ADDR_continue_div2addr
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86/OP_REM_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_2ADDR_continue_div2addr
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_2ADDR_continue_div2addr
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86/OP_AND_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    andl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86/OP_OR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    orl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86/OP_XOR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $0xf,%cl                  # ecx<- A
+    xorl     %eax,(rFP,%ecx,4)                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86/OP_SHL_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    sall    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86/OP_SHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    sarl    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86/OP_USHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    shrl    %cl,%eax                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86/OP_ADD_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    addl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    adcl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86/OP_SUB_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    subl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    sbbl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86/OP_MUL_LONG_2ADDR.S */
+    /*
+     * Signed 64-bit integer multiply, 2-addr version
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill %edx (rIBASE) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and rIBASE as computational
+     * temps.  On top of that, we'll spill %esi (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINSTbl,%eax             # eax<- BA
+    andb      $0xf,%al                # eax<- A
+    sarl      $4,rINST                # rINST<- B
+    SPILL_TMP2(%esi)
+    SPILL(rFP)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # %esi<- &v[A]
+    leal      (rFP,rINST,4),rFP        # rFP<- &v[B]
+    movl      4(%esi),%ecx             # ecx<- Amsw
+    imull     (rFP),%ecx               # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax              # eax<- Bmsw
+    imull     (%esi),%eax              # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx                # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax               # eax<- Blsw
+    mull      (%esi)                   # eax<- (Blsw*Alsw)
+    leal      (%ecx,rIBASE),rIBASE     # full result now in %edx:%eax
+    movl      rIBASE,4(%esi)           # v[A+1]<- rIBASE
+    movl      %eax,(%esi)              # v[A]<- %eax
+    UNSPILL_TMP2(%esi)
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    UNSPILL(rFP)
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $4,%eax                  # eax<- B
+    andb      $0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_DIV_LONG_2ADDR_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_DIV_LONG_2ADDR_check_neg1
+.LOP_DIV_LONG_2ADDR_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.LOP_DIV_LONG_2ADDR_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __divdi3
+.LOP_DIV_LONG_2ADDR_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_LONG_2ADDR_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_2ADDR_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_DIV_LONG_2ADDR_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_DIV_LONG_2ADDR_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0x80000000,rIBASE
+    jmp      .LOP_DIV_LONG_2ADDR_finish
+
+/* ------------------------------ */
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86/OP_REM_LONG_2ADDR.S */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $4,%eax                  # eax<- B
+    andb      $0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .LOP_REM_LONG_2ADDR_check_zero
+    cmpl     $-1,%eax
+    je       .LOP_REM_LONG_2ADDR_check_neg1
+.LOP_REM_LONG_2ADDR_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.LOP_REM_LONG_2ADDR_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __moddi3
+.LOP_REM_LONG_2ADDR_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_LONG_2ADDR_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_2ADDR_check_neg1:
+    testl   rIBASE,%eax
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .LOP_REM_LONG_2ADDR_notSpecial1
+    cmpl     $0x80000000,%ecx
+    jne      .LOP_REM_LONG_2ADDR_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $0,rIBASE
+    jmp      .LOP_REM_LONG_2ADDR_finish
+
+
+/* ------------------------------ */
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86/OP_AND_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    andl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    andl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86/OP_OR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    orl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    orl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86/OP_XOR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $0xF,rINSTbl             # rINST<- A
+    xorl %eax,(rFP,rINST,4)         # example: addl   %eax,(rFP,rINST,4)
+    xorl %ecx,4(rFP,rINST,4)         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86/OP_SHL_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R  %ecx %ecx              # ecx<- vBB
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86/OP_SHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx         # ecx<- BA
+    andb      $0xf,rINSTbl        # rINST<- A
+    GET_VREG_WORD %eax rINST 0     # eax<- v[AA+0]
+    sarl      $4,%ecx             # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1   # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx           # ecx<- vBB
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1   # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0    # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86/OP_USHR_LONG_2ADDR.S */
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx               # ecx<- vBB
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86/OP_ADD_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fadds   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86/OP_SUB_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fsubs   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86/OP_MUL_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fmuls   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86/OP_DIV_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    flds    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fdivs   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/OP_REM_FLOAT_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    flds     (rFP,rINST,4)              # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/OP_ADD_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    addsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/OP_SUB_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    subsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/OP_MUL_DOUBLE_2ADDR.S */
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $0xf,%cl               # ecx<- A
+    sarl        $4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    mulsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86/OP_DIV_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $0xf,%cl              # ecx<- A
+    fldl    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $4,rINST             # rINST<- B
+    fdivl   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/OP_REM_DOUBLE_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $4,rINST                  # rINST<- B
+    fldl     (rFP,rINST,4)              # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
+
+/* ------------------------------ */
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86/OP_ADD_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    addl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: x86/OP_RSUB_INT.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    subl %eax,%ecx                              # for example: addl %ecx, %eax
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/OP_MUL_INT_LIT16.S */
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    SPILL(rIBASE)
+    imull     %ecx,%eax                 # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86/OP_DIV_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl        # rINST<- A
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_LIT16_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_LIT16_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86/OP_REM_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl        # rINST<- A
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_LIT16_continue_div
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_LIT16_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86/OP_AND_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    andl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86/OP_OR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    orl     %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86/OP_XOR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $0xf,rINSTbl              # rINST<- A
+    xor    %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86/OP_ADD_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    addl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86/OP_RSUB_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    subl  %eax,%ecx                             # ex: addl %ecx,%eax
+    SET_VREG   %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86/OP_MUL_INT_LIT8.S */
+    /* mul/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    SPILL(rIBASE)
+    imull     %ecx,%eax                # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG  %eax rINST
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86/OP_DIV_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $0x80000000,%eax
+    jne      .LOP_DIV_INT_LIT8_continue_div
+    cmpl     $-1,%ecx
+    jne      .LOP_DIV_INT_LIT8_continue_div
+    movl     $0x80000000,%eax
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_DIV_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86/OP_REM_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $0,%ecx
+    je       common_errDivideByZero
+    cmpl     $0x80000000,%eax
+    jne      .LOP_REM_INT_LIT8_continue_div
+    cmpl     $-1,%ecx
+    jne      .LOP_REM_INT_LIT8_continue_div
+    movl     $0,rIBASE
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_REM_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG rIBASE rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86/OP_AND_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    andl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86/OP_OR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    orl     %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86/OP_XOR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    xor    %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86/OP_SHL_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    sall  %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86/OP_SHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    sarl    %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86/OP_USHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    shrl     %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG   %eax rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: x86/OP_IGET_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: x86/OP_IPUT_VOLATILE.S */
+/* File: x86/OP_IPUT.S */
+
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: x86/OP_SGET_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_VOLATILE_resolve                # if not, make it so
+.LOP_SGET_VOLATILE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: x86/OP_SPUT_VOLATILE.S */
+/* File: x86/OP_SPUT.S */
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_VOLATILE_resolve                # if not, make it so
+.LOP_SPUT_VOLATILE_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_VOLATILE_finish                 # success, continue
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: x86/OP_IGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IGET_OBJECT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .LOP_IGET_OBJECT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_OBJECT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_IGET_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_IPUT_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_SGET_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_SPUT_WIDE_VOLATILE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: x86/OP_BREAKPOINT.S */
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.  We also assume that all other special "checkBefore"
+     * actions have been handled, so we'll transition directly
+     * to the real handler
+     */
+    SPILL(rIBASE)
+    movl    rPC,OUT_ARG0(%esp)
+    call    dvmGetOriginalOpcode
+    UNSPILL(rIBASE)
+    movl    rSELF,%ecx
+    movzbl  1(rPC),rINST
+    movl    offThread_mainHandlerTable(%ecx),%ecx
+    jmp     *(%ecx,%eax,4)
+
+
+/* ------------------------------ */
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86/OP_THROW_VERIFICATION_ERROR.S */
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offThread_method(%ecx),%ecx       # ecx<- self->method
+    EXPORT_PC
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST,OUT_ARG1(%esp)            # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    jmp      common_exceptionThrown          # handle exception
+
+/* ------------------------------ */
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86/OP_EXECUTE_INLINE.S */
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We will be calling through a function table:
+     *
+     * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+     *
+     * Ignores argument count - always loads 4.
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    movl      rSELF,%ecx
+    EXPORT_PC
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    SPILL(rIBASE)                       # preserve rIBASE
+    movl      offThread_subMode(%ecx), %edx # edx<- submode flags
+    andl      $kSubModeDebugProfile, %edx # debug or profile mode active?
+    jnz       .LOP_EXECUTE_INLINE_debugprofile   # yes, take slow path
+.LOP_EXECUTE_INLINE_resume:
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    call      .LOP_EXECUTE_INLINE_continue      # make call; will return after
+    UNSPILL(rIBASE)                     # restore rIBASE
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+.LOP_EXECUTE_INLINE_continue:
+    /*
+     * Extract args, call function.
+     *  ecx = #of args (0-4)
+     *  eax = call index
+     *  @esp = return addr
+     *  esp is -4 from normal
+     *
+     *  Go ahead and load all 4 args, even if not used.
+     */
+    movzwl    4(rPC),rIBASE
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $4,rIBASE
+    movl      %ecx,4+OUT_ARG3(%esp)
+
+    sall      $4,%eax      # index *= sizeof(table entry)
+    jmp       *gDvmInlineOpsTable(%eax)
+    # will return to caller of .LOP_EXECUTE_INLINE_continue
+
+    /*
+     * We're debugging or profiling.
+     * eax: opIndex
+     */
+.LOP_EXECUTE_INLINE_debugprofile:
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- BBBB
+    SPILL_TMP1(%eax)                    # save opIndex
+    call      dvmResolveInlineNative    # dvmResolveInlineNative(opIndex)
+    movl      rSELF,%ecx                # restore self
+    testl     %eax,%eax                 # method resolved?
+    movl      %eax,%edx                 # save possibly resolved method in edx
+    UNSPILL_TMP1(%eax)                  # in case not resolved, restore opIndex
+    jz        .LOP_EXECUTE_INLINE_resume        # not resolved, just move on
+    SPILL_TMP2(%edx)                    # save method
+    movl      %edx,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastMethodTraceEnter   # dvmFastMethodTraceEnter(method,self)
+    movl      rSELF,%ecx                # restore self
+    UNSPILL_TMP1(%eax)                  # restore opIndex
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)       # needed for pResult of inline operation handler
+    call      .LOP_EXECUTE_INLINE_continue      # make call; will return after
+    SPILL_TMP1(%eax)                    # save result of inline
+    UNSPILL_TMP2(%eax)                  # restore method
+    movl      rSELF,%ecx                # restore self
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastNativeMethodTraceExit # dvmFastNativeMethodTraceExit(method,self)
+    UNSPILL(rIBASE)                     # restore rIBASE
+    UNSPILL_TMP1(%eax)                  # restore result of inline
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_EXECUTE_INLINE_RANGE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_INVOKE_OBJECT_INIT_RANGE     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_OP_RETURN_VOID_BARRIER     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
+/* ------------------------------ */
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/OP_IGET_WIDE_QUICK.S */
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%eax        # eax<- address of 64-bit source
+    movl      (%eax),%ecx               # ecx<- lsw
+    movl      4(%eax),%eax              # eax<- msw
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG_WORD %ecx rINST 0          # v[A+0]<- lsw
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1          # v[A+1]<- msw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86/OP_IGET_OBJECT_QUICK.S */
+/* File: x86/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST,rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/OP_IPUT_WIDE_QUICK.S */
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl      %ecx,%ecx                # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%ecx        # ecx<- Address of 64-bit target
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_WORD %eax rINST 0          # eax<- lsw
+    GET_VREG_WORD rINST rINST 1         # rINST<- msw
+    movl      %eax,(%ecx)
+    movl      rINST,4(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    movl      rSELF,%eax
+    testl     rINST,rINST               # did we store null?
+    movl      offThread_cardTable(%eax),%eax  # get card table base
+    je        1f                            # skip card mark if null store
+    shrl      $GC_CARD_SHIFT,%ecx          # object head to card number
+    movb      %al,(%eax,%ecx)               # mark card based on object head
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!0)
+    andl      $0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!1)
+    andl      $0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!0)
+    andl      $0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!1)
+    andl      $0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: x86/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_IPUT_OBJECT.S */
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .LOP_IPUT_OBJECT_VOLATILE_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .LOP_IPUT_OBJECT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+
+/* ------------------------------ */
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: x86/OP_SGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SGET_OBJECT_VOLATILE_resolve                # if not, make it so
+.LOP_SGET_OBJECT_VOLATILE_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SGET_OBJECT_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: x86/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_SPUT_OBJECT.S */
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .LOP_SPUT_OBJECT_VOLATILE_resolve                # if not, make it so
+.LOP_SPUT_OBJECT_VOLATILE_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .LOP_SPUT_OBJECT_VOLATILE_finish                 # success, continue
+
+
+/* ------------------------------ */
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86/OP_UNUSED_FF.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+    .size   dvmAsmInstructionStartCode, .-dvmAsmInstructionStartCode
+    .global dvmAsmInstructionEndCode
+dvmAsmInstructionEndCode:
+
+    .global dvmAsmAltInstructionStartCode
+    .type   dvmAsmAltInstructionStartCode, %function
+    .text
+
+dvmAsmAltInstructionStartCode = .L_ALT_OP_NOP
+/* ------------------------------ */
+.L_ALT_OP_NOP: /* 0x00 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(0*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE: /* 0x01 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(1*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(2*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_16: /* 0x03 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(3*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(4*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(5*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(6*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(7*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(8*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(9*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(10*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(11*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(12*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(13*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(14*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN: /* 0x0f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(15*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(16*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(17*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_4: /* 0x12 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(18*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_16: /* 0x13 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(19*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST: /* 0x14 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(20*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(21*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(22*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(23*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(24*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(25*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_STRING: /* 0x1a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(26*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(27*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(28*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(29*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(30*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CHECK_CAST: /* 0x1f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(31*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(32*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(33*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(34*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(35*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(36*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(37*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(38*4)
+
+/* ------------------------------ */
+.L_ALT_OP_THROW: /* 0x27 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(39*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO: /* 0x28 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(40*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO_16: /* 0x29 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(41*4)
+
+/* ------------------------------ */
+.L_ALT_OP_GOTO_32: /* 0x2a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(42*4)
+
+/* ------------------------------ */
+.L_ALT_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(43*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(44*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(45*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(46*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(47*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(48*4)
+
+/* ------------------------------ */
+.L_ALT_OP_CMP_LONG: /* 0x31 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(49*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_EQ: /* 0x32 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(50*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_NE: /* 0x33 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(51*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LT: /* 0x34 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(52*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GE: /* 0x35 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(53*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GT: /* 0x36 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(54*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LE: /* 0x37 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(55*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_EQZ: /* 0x38 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(56*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_NEZ: /* 0x39 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(57*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LTZ: /* 0x3a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(58*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GEZ: /* 0x3b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(59*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_GTZ: /* 0x3c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(60*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IF_LEZ: /* 0x3d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(61*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(62*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(63*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_40: /* 0x40 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(64*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_41: /* 0x41 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(65*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_42: /* 0x42 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(66*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_43: /* 0x43 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(67*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET: /* 0x44 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(68*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_WIDE: /* 0x45 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(69*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(70*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(71*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_BYTE: /* 0x48 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(72*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_CHAR: /* 0x49 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(73*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AGET_SHORT: /* 0x4a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(74*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT: /* 0x4b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(75*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_WIDE: /* 0x4c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(76*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(77*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(78*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_BYTE: /* 0x4f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(79*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_CHAR: /* 0x50 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(80*4)
+
+/* ------------------------------ */
+.L_ALT_OP_APUT_SHORT: /* 0x51 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(81*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET: /* 0x52 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(82*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(83*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(84*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(85*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_BYTE: /* 0x56 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(86*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_CHAR: /* 0x57 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(87*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_SHORT: /* 0x58 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(88*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT: /* 0x59 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(89*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(90*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(91*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(92*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(93*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(94*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(95*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET: /* 0x60 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(96*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(97*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(98*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(99*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_BYTE: /* 0x64 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(100*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_CHAR: /* 0x65 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(101*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_SHORT: /* 0x66 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(102*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT: /* 0x67 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(103*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(104*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(105*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(106*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(107*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(108*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(109*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(110*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(111*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(112*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(113*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(114*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_73: /* 0x73 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(115*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(116*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(117*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(118*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(119*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(120*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_79: /* 0x79 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(121*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(122*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_INT: /* 0x7b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(123*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NOT_INT: /* 0x7c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(124*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_LONG: /* 0x7d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(125*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NOT_LONG: /* 0x7e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(126*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(127*4)
+
+/* ------------------------------ */
+.L_ALT_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(128*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(129*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(130*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(131*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(132*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(133*4)
+
+/* ------------------------------ */
+.L_ALT_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(134*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(135*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(136*4)
+
+/* ------------------------------ */
+.L_ALT_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(137*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(138*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(139*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(140*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(141*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(142*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(143*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT: /* 0x90 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(144*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_INT: /* 0x91 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(145*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT: /* 0x92 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(146*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT: /* 0x93 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(147*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT: /* 0x94 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(148*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT: /* 0x95 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(149*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT: /* 0x96 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(150*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT: /* 0x97 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(151*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT: /* 0x98 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(152*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT: /* 0x99 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(153*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT: /* 0x9a */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(154*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_LONG: /* 0x9b */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(155*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_LONG: /* 0x9c */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(156*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_LONG: /* 0x9d */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(157*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_LONG: /* 0x9e */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(158*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_LONG: /* 0x9f */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(159*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_LONG: /* 0xa0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(160*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_LONG: /* 0xa1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(161*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_LONG: /* 0xa2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(162*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_LONG: /* 0xa3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(163*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_LONG: /* 0xa4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(164*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_LONG: /* 0xa5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(165*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(166*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(167*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(168*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(169*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_FLOAT: /* 0xaa */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(170*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(171*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(172*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(173*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(174*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(175*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(176*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(177*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(178*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(179*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(180*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(181*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(182*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(183*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(184*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(185*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(186*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(187*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(188*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(189*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(190*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(191*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(192*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(193*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(194*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(195*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(196*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(197*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(198*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(199*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(200*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(201*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(202*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(203*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(204*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(205*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(206*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(207*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(208*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RSUB_INT: /* 0xd1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(209*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(210*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(211*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(212*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(213*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(214*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(215*4)
+
+/* ------------------------------ */
+.L_ALT_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(216*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(217*4)
+
+/* ------------------------------ */
+.L_ALT_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(218*4)
+
+/* ------------------------------ */
+.L_ALT_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(219*4)
+
+/* ------------------------------ */
+.L_ALT_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(220*4)
+
+/* ------------------------------ */
+.L_ALT_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(221*4)
+
+/* ------------------------------ */
+.L_ALT_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(222*4)
+
+/* ------------------------------ */
+.L_ALT_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(223*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(224*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(225*4)
+
+/* ------------------------------ */
+.L_ALT_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(226*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(227*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(228*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(229*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(230*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(231*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(232*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(233*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(234*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(235*4)
+
+/* ------------------------------ */
+.L_ALT_OP_BREAKPOINT: /* 0xec */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(236*4)
+
+/* ------------------------------ */
+.L_ALT_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(237*4)
+
+/* ------------------------------ */
+.L_ALT_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(238*4)
+
+/* ------------------------------ */
+.L_ALT_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(239*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_OBJECT_INIT_RANGE: /* 0xf0 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(240*4)
+
+/* ------------------------------ */
+.L_ALT_OP_RETURN_VOID_BARRIER: /* 0xf1 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(241*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(242*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(243*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(244*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(245*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(246*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(247*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(248*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(249*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(250*4)
+
+/* ------------------------------ */
+.L_ALT_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(251*4)
+
+/* ------------------------------ */
+.L_ALT_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(252*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(253*4)
+
+/* ------------------------------ */
+.L_ALT_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(254*4)
+
+/* ------------------------------ */
+.L_ALT_OP_UNUSED_FF: /* 0xff */
+/* File: x86/alt_stub.S */
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(255*4)
+
+    .size   dvmAsmAltInstructionStartCode, .-dvmAsmAltInstructionStartCode
+    .global dvmAsmAltInstructionEndCode
+dvmAsmAltInstructionEndCode:
+
+    .global dvmAsmInstructionStart
+    .text
+dvmAsmInstructionStart:
+    .long .L_OP_NOP /* 0x00 */
+    .long .L_OP_MOVE /* 0x01 */
+    .long .L_OP_MOVE_FROM16 /* 0x02 */
+    .long .L_OP_MOVE_16 /* 0x03 */
+    .long .L_OP_MOVE_WIDE /* 0x04 */
+    .long .L_OP_MOVE_WIDE_FROM16 /* 0x05 */
+    .long .L_OP_MOVE_WIDE_16 /* 0x06 */
+    .long .L_OP_MOVE_OBJECT /* 0x07 */
+    .long .L_OP_MOVE_OBJECT_FROM16 /* 0x08 */
+    .long .L_OP_MOVE_OBJECT_16 /* 0x09 */
+    .long .L_OP_MOVE_RESULT /* 0x0a */
+    .long .L_OP_MOVE_RESULT_WIDE /* 0x0b */
+    .long .L_OP_MOVE_RESULT_OBJECT /* 0x0c */
+    .long .L_OP_MOVE_EXCEPTION /* 0x0d */
+    .long .L_OP_RETURN_VOID /* 0x0e */
+    .long .L_OP_RETURN /* 0x0f */
+    .long .L_OP_RETURN_WIDE /* 0x10 */
+    .long .L_OP_RETURN_OBJECT /* 0x11 */
+    .long .L_OP_CONST_4 /* 0x12 */
+    .long .L_OP_CONST_16 /* 0x13 */
+    .long .L_OP_CONST /* 0x14 */
+    .long .L_OP_CONST_HIGH16 /* 0x15 */
+    .long .L_OP_CONST_WIDE_16 /* 0x16 */
+    .long .L_OP_CONST_WIDE_32 /* 0x17 */
+    .long .L_OP_CONST_WIDE /* 0x18 */
+    .long .L_OP_CONST_WIDE_HIGH16 /* 0x19 */
+    .long .L_OP_CONST_STRING /* 0x1a */
+    .long .L_OP_CONST_STRING_JUMBO /* 0x1b */
+    .long .L_OP_CONST_CLASS /* 0x1c */
+    .long .L_OP_MONITOR_ENTER /* 0x1d */
+    .long .L_OP_MONITOR_EXIT /* 0x1e */
+    .long .L_OP_CHECK_CAST /* 0x1f */
+    .long .L_OP_INSTANCE_OF /* 0x20 */
+    .long .L_OP_ARRAY_LENGTH /* 0x21 */
+    .long .L_OP_NEW_INSTANCE /* 0x22 */
+    .long .L_OP_NEW_ARRAY /* 0x23 */
+    .long .L_OP_FILLED_NEW_ARRAY /* 0x24 */
+    .long .L_OP_FILLED_NEW_ARRAY_RANGE /* 0x25 */
+    .long .L_OP_FILL_ARRAY_DATA /* 0x26 */
+    .long .L_OP_THROW /* 0x27 */
+    .long .L_OP_GOTO /* 0x28 */
+    .long .L_OP_GOTO_16 /* 0x29 */
+    .long .L_OP_GOTO_32 /* 0x2a */
+    .long .L_OP_PACKED_SWITCH /* 0x2b */
+    .long .L_OP_SPARSE_SWITCH /* 0x2c */
+    .long .L_OP_CMPL_FLOAT /* 0x2d */
+    .long .L_OP_CMPG_FLOAT /* 0x2e */
+    .long .L_OP_CMPL_DOUBLE /* 0x2f */
+    .long .L_OP_CMPG_DOUBLE /* 0x30 */
+    .long .L_OP_CMP_LONG /* 0x31 */
+    .long .L_OP_IF_EQ /* 0x32 */
+    .long .L_OP_IF_NE /* 0x33 */
+    .long .L_OP_IF_LT /* 0x34 */
+    .long .L_OP_IF_GE /* 0x35 */
+    .long .L_OP_IF_GT /* 0x36 */
+    .long .L_OP_IF_LE /* 0x37 */
+    .long .L_OP_IF_EQZ /* 0x38 */
+    .long .L_OP_IF_NEZ /* 0x39 */
+    .long .L_OP_IF_LTZ /* 0x3a */
+    .long .L_OP_IF_GEZ /* 0x3b */
+    .long .L_OP_IF_GTZ /* 0x3c */
+    .long .L_OP_IF_LEZ /* 0x3d */
+    .long .L_OP_UNUSED_3E /* 0x3e */
+    .long .L_OP_UNUSED_3F /* 0x3f */
+    .long .L_OP_UNUSED_40 /* 0x40 */
+    .long .L_OP_UNUSED_41 /* 0x41 */
+    .long .L_OP_UNUSED_42 /* 0x42 */
+    .long .L_OP_UNUSED_43 /* 0x43 */
+    .long .L_OP_AGET /* 0x44 */
+    .long .L_OP_AGET_WIDE /* 0x45 */
+    .long .L_OP_AGET_OBJECT /* 0x46 */
+    .long .L_OP_AGET_BOOLEAN /* 0x47 */
+    .long .L_OP_AGET_BYTE /* 0x48 */
+    .long .L_OP_AGET_CHAR /* 0x49 */
+    .long .L_OP_AGET_SHORT /* 0x4a */
+    .long .L_OP_APUT /* 0x4b */
+    .long .L_OP_APUT_WIDE /* 0x4c */
+    .long .L_OP_APUT_OBJECT /* 0x4d */
+    .long .L_OP_APUT_BOOLEAN /* 0x4e */
+    .long .L_OP_APUT_BYTE /* 0x4f */
+    .long .L_OP_APUT_CHAR /* 0x50 */
+    .long .L_OP_APUT_SHORT /* 0x51 */
+    .long .L_OP_IGET /* 0x52 */
+    .long .L_OP_IGET_WIDE /* 0x53 */
+    .long .L_OP_IGET_OBJECT /* 0x54 */
+    .long .L_OP_IGET_BOOLEAN /* 0x55 */
+    .long .L_OP_IGET_BYTE /* 0x56 */
+    .long .L_OP_IGET_CHAR /* 0x57 */
+    .long .L_OP_IGET_SHORT /* 0x58 */
+    .long .L_OP_IPUT /* 0x59 */
+    .long .L_OP_IPUT_WIDE /* 0x5a */
+    .long .L_OP_IPUT_OBJECT /* 0x5b */
+    .long .L_OP_IPUT_BOOLEAN /* 0x5c */
+    .long .L_OP_IPUT_BYTE /* 0x5d */
+    .long .L_OP_IPUT_CHAR /* 0x5e */
+    .long .L_OP_IPUT_SHORT /* 0x5f */
+    .long .L_OP_SGET /* 0x60 */
+    .long .L_OP_SGET_WIDE /* 0x61 */
+    .long .L_OP_SGET_OBJECT /* 0x62 */
+    .long .L_OP_SGET_BOOLEAN /* 0x63 */
+    .long .L_OP_SGET_BYTE /* 0x64 */
+    .long .L_OP_SGET_CHAR /* 0x65 */
+    .long .L_OP_SGET_SHORT /* 0x66 */
+    .long .L_OP_SPUT /* 0x67 */
+    .long .L_OP_SPUT_WIDE /* 0x68 */
+    .long .L_OP_SPUT_OBJECT /* 0x69 */
+    .long .L_OP_SPUT_BOOLEAN /* 0x6a */
+    .long .L_OP_SPUT_BYTE /* 0x6b */
+    .long .L_OP_SPUT_CHAR /* 0x6c */
+    .long .L_OP_SPUT_SHORT /* 0x6d */
+    .long .L_OP_INVOKE_VIRTUAL /* 0x6e */
+    .long .L_OP_INVOKE_SUPER /* 0x6f */
+    .long .L_OP_INVOKE_DIRECT /* 0x70 */
+    .long .L_OP_INVOKE_STATIC /* 0x71 */
+    .long .L_OP_INVOKE_INTERFACE /* 0x72 */
+    .long .L_OP_UNUSED_73 /* 0x73 */
+    .long .L_OP_INVOKE_VIRTUAL_RANGE /* 0x74 */
+    .long .L_OP_INVOKE_SUPER_RANGE /* 0x75 */
+    .long .L_OP_INVOKE_DIRECT_RANGE /* 0x76 */
+    .long .L_OP_INVOKE_STATIC_RANGE /* 0x77 */
+    .long .L_OP_INVOKE_INTERFACE_RANGE /* 0x78 */
+    .long .L_OP_UNUSED_79 /* 0x79 */
+    .long .L_OP_UNUSED_7A /* 0x7a */
+    .long .L_OP_NEG_INT /* 0x7b */
+    .long .L_OP_NOT_INT /* 0x7c */
+    .long .L_OP_NEG_LONG /* 0x7d */
+    .long .L_OP_NOT_LONG /* 0x7e */
+    .long .L_OP_NEG_FLOAT /* 0x7f */
+    .long .L_OP_NEG_DOUBLE /* 0x80 */
+    .long .L_OP_INT_TO_LONG /* 0x81 */
+    .long .L_OP_INT_TO_FLOAT /* 0x82 */
+    .long .L_OP_INT_TO_DOUBLE /* 0x83 */
+    .long .L_OP_LONG_TO_INT /* 0x84 */
+    .long .L_OP_LONG_TO_FLOAT /* 0x85 */
+    .long .L_OP_LONG_TO_DOUBLE /* 0x86 */
+    .long .L_OP_FLOAT_TO_INT /* 0x87 */
+    .long .L_OP_FLOAT_TO_LONG /* 0x88 */
+    .long .L_OP_FLOAT_TO_DOUBLE /* 0x89 */
+    .long .L_OP_DOUBLE_TO_INT /* 0x8a */
+    .long .L_OP_DOUBLE_TO_LONG /* 0x8b */
+    .long .L_OP_DOUBLE_TO_FLOAT /* 0x8c */
+    .long .L_OP_INT_TO_BYTE /* 0x8d */
+    .long .L_OP_INT_TO_CHAR /* 0x8e */
+    .long .L_OP_INT_TO_SHORT /* 0x8f */
+    .long .L_OP_ADD_INT /* 0x90 */
+    .long .L_OP_SUB_INT /* 0x91 */
+    .long .L_OP_MUL_INT /* 0x92 */
+    .long .L_OP_DIV_INT /* 0x93 */
+    .long .L_OP_REM_INT /* 0x94 */
+    .long .L_OP_AND_INT /* 0x95 */
+    .long .L_OP_OR_INT /* 0x96 */
+    .long .L_OP_XOR_INT /* 0x97 */
+    .long .L_OP_SHL_INT /* 0x98 */
+    .long .L_OP_SHR_INT /* 0x99 */
+    .long .L_OP_USHR_INT /* 0x9a */
+    .long .L_OP_ADD_LONG /* 0x9b */
+    .long .L_OP_SUB_LONG /* 0x9c */
+    .long .L_OP_MUL_LONG /* 0x9d */
+    .long .L_OP_DIV_LONG /* 0x9e */
+    .long .L_OP_REM_LONG /* 0x9f */
+    .long .L_OP_AND_LONG /* 0xa0 */
+    .long .L_OP_OR_LONG /* 0xa1 */
+    .long .L_OP_XOR_LONG /* 0xa2 */
+    .long .L_OP_SHL_LONG /* 0xa3 */
+    .long .L_OP_SHR_LONG /* 0xa4 */
+    .long .L_OP_USHR_LONG /* 0xa5 */
+    .long .L_OP_ADD_FLOAT /* 0xa6 */
+    .long .L_OP_SUB_FLOAT /* 0xa7 */
+    .long .L_OP_MUL_FLOAT /* 0xa8 */
+    .long .L_OP_DIV_FLOAT /* 0xa9 */
+    .long .L_OP_REM_FLOAT /* 0xaa */
+    .long .L_OP_ADD_DOUBLE /* 0xab */
+    .long .L_OP_SUB_DOUBLE /* 0xac */
+    .long .L_OP_MUL_DOUBLE /* 0xad */
+    .long .L_OP_DIV_DOUBLE /* 0xae */
+    .long .L_OP_REM_DOUBLE /* 0xaf */
+    .long .L_OP_ADD_INT_2ADDR /* 0xb0 */
+    .long .L_OP_SUB_INT_2ADDR /* 0xb1 */
+    .long .L_OP_MUL_INT_2ADDR /* 0xb2 */
+    .long .L_OP_DIV_INT_2ADDR /* 0xb3 */
+    .long .L_OP_REM_INT_2ADDR /* 0xb4 */
+    .long .L_OP_AND_INT_2ADDR /* 0xb5 */
+    .long .L_OP_OR_INT_2ADDR /* 0xb6 */
+    .long .L_OP_XOR_INT_2ADDR /* 0xb7 */
+    .long .L_OP_SHL_INT_2ADDR /* 0xb8 */
+    .long .L_OP_SHR_INT_2ADDR /* 0xb9 */
+    .long .L_OP_USHR_INT_2ADDR /* 0xba */
+    .long .L_OP_ADD_LONG_2ADDR /* 0xbb */
+    .long .L_OP_SUB_LONG_2ADDR /* 0xbc */
+    .long .L_OP_MUL_LONG_2ADDR /* 0xbd */
+    .long .L_OP_DIV_LONG_2ADDR /* 0xbe */
+    .long .L_OP_REM_LONG_2ADDR /* 0xbf */
+    .long .L_OP_AND_LONG_2ADDR /* 0xc0 */
+    .long .L_OP_OR_LONG_2ADDR /* 0xc1 */
+    .long .L_OP_XOR_LONG_2ADDR /* 0xc2 */
+    .long .L_OP_SHL_LONG_2ADDR /* 0xc3 */
+    .long .L_OP_SHR_LONG_2ADDR /* 0xc4 */
+    .long .L_OP_USHR_LONG_2ADDR /* 0xc5 */
+    .long .L_OP_ADD_FLOAT_2ADDR /* 0xc6 */
+    .long .L_OP_SUB_FLOAT_2ADDR /* 0xc7 */
+    .long .L_OP_MUL_FLOAT_2ADDR /* 0xc8 */
+    .long .L_OP_DIV_FLOAT_2ADDR /* 0xc9 */
+    .long .L_OP_REM_FLOAT_2ADDR /* 0xca */
+    .long .L_OP_ADD_DOUBLE_2ADDR /* 0xcb */
+    .long .L_OP_SUB_DOUBLE_2ADDR /* 0xcc */
+    .long .L_OP_MUL_DOUBLE_2ADDR /* 0xcd */
+    .long .L_OP_DIV_DOUBLE_2ADDR /* 0xce */
+    .long .L_OP_REM_DOUBLE_2ADDR /* 0xcf */
+    .long .L_OP_ADD_INT_LIT16 /* 0xd0 */
+    .long .L_OP_RSUB_INT /* 0xd1 */
+    .long .L_OP_MUL_INT_LIT16 /* 0xd2 */
+    .long .L_OP_DIV_INT_LIT16 /* 0xd3 */
+    .long .L_OP_REM_INT_LIT16 /* 0xd4 */
+    .long .L_OP_AND_INT_LIT16 /* 0xd5 */
+    .long .L_OP_OR_INT_LIT16 /* 0xd6 */
+    .long .L_OP_XOR_INT_LIT16 /* 0xd7 */
+    .long .L_OP_ADD_INT_LIT8 /* 0xd8 */
+    .long .L_OP_RSUB_INT_LIT8 /* 0xd9 */
+    .long .L_OP_MUL_INT_LIT8 /* 0xda */
+    .long .L_OP_DIV_INT_LIT8 /* 0xdb */
+    .long .L_OP_REM_INT_LIT8 /* 0xdc */
+    .long .L_OP_AND_INT_LIT8 /* 0xdd */
+    .long .L_OP_OR_INT_LIT8 /* 0xde */
+    .long .L_OP_XOR_INT_LIT8 /* 0xdf */
+    .long .L_OP_SHL_INT_LIT8 /* 0xe0 */
+    .long .L_OP_SHR_INT_LIT8 /* 0xe1 */
+    .long .L_OP_USHR_INT_LIT8 /* 0xe2 */
+    .long .L_OP_IGET_VOLATILE /* 0xe3 */
+    .long .L_OP_IPUT_VOLATILE /* 0xe4 */
+    .long .L_OP_SGET_VOLATILE /* 0xe5 */
+    .long .L_OP_SPUT_VOLATILE /* 0xe6 */
+    .long .L_OP_IGET_OBJECT_VOLATILE /* 0xe7 */
+    .long .L_OP_IGET_WIDE_VOLATILE /* 0xe8 */
+    .long .L_OP_IPUT_WIDE_VOLATILE /* 0xe9 */
+    .long .L_OP_SGET_WIDE_VOLATILE /* 0xea */
+    .long .L_OP_SPUT_WIDE_VOLATILE /* 0xeb */
+    .long .L_OP_BREAKPOINT /* 0xec */
+    .long .L_OP_THROW_VERIFICATION_ERROR /* 0xed */
+    .long .L_OP_EXECUTE_INLINE /* 0xee */
+    .long .L_OP_EXECUTE_INLINE_RANGE /* 0xef */
+    .long .L_OP_INVOKE_OBJECT_INIT_RANGE /* 0xf0 */
+    .long .L_OP_RETURN_VOID_BARRIER /* 0xf1 */
+    .long .L_OP_IGET_QUICK /* 0xf2 */
+    .long .L_OP_IGET_WIDE_QUICK /* 0xf3 */
+    .long .L_OP_IGET_OBJECT_QUICK /* 0xf4 */
+    .long .L_OP_IPUT_QUICK /* 0xf5 */
+    .long .L_OP_IPUT_WIDE_QUICK /* 0xf6 */
+    .long .L_OP_IPUT_OBJECT_QUICK /* 0xf7 */
+    .long .L_OP_INVOKE_VIRTUAL_QUICK /* 0xf8 */
+    .long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE /* 0xf9 */
+    .long .L_OP_INVOKE_SUPER_QUICK /* 0xfa */
+    .long .L_OP_INVOKE_SUPER_QUICK_RANGE /* 0xfb */
+    .long .L_OP_IPUT_OBJECT_VOLATILE /* 0xfc */
+    .long .L_OP_SGET_OBJECT_VOLATILE /* 0xfd */
+    .long .L_OP_SPUT_OBJECT_VOLATILE /* 0xfe */
+    .long .L_OP_UNUSED_FF /* 0xff */
+
+    .global dvmAsmAltInstructionStart
+    .text
+dvmAsmAltInstructionStart:
+    .long .L_ALT_OP_NOP /* 0x00 */
+    .long .L_ALT_OP_MOVE /* 0x01 */
+    .long .L_ALT_OP_MOVE_FROM16 /* 0x02 */
+    .long .L_ALT_OP_MOVE_16 /* 0x03 */
+    .long .L_ALT_OP_MOVE_WIDE /* 0x04 */
+    .long .L_ALT_OP_MOVE_WIDE_FROM16 /* 0x05 */
+    .long .L_ALT_OP_MOVE_WIDE_16 /* 0x06 */
+    .long .L_ALT_OP_MOVE_OBJECT /* 0x07 */
+    .long .L_ALT_OP_MOVE_OBJECT_FROM16 /* 0x08 */
+    .long .L_ALT_OP_MOVE_OBJECT_16 /* 0x09 */
+    .long .L_ALT_OP_MOVE_RESULT /* 0x0a */
+    .long .L_ALT_OP_MOVE_RESULT_WIDE /* 0x0b */
+    .long .L_ALT_OP_MOVE_RESULT_OBJECT /* 0x0c */
+    .long .L_ALT_OP_MOVE_EXCEPTION /* 0x0d */
+    .long .L_ALT_OP_RETURN_VOID /* 0x0e */
+    .long .L_ALT_OP_RETURN /* 0x0f */
+    .long .L_ALT_OP_RETURN_WIDE /* 0x10 */
+    .long .L_ALT_OP_RETURN_OBJECT /* 0x11 */
+    .long .L_ALT_OP_CONST_4 /* 0x12 */
+    .long .L_ALT_OP_CONST_16 /* 0x13 */
+    .long .L_ALT_OP_CONST /* 0x14 */
+    .long .L_ALT_OP_CONST_HIGH16 /* 0x15 */
+    .long .L_ALT_OP_CONST_WIDE_16 /* 0x16 */
+    .long .L_ALT_OP_CONST_WIDE_32 /* 0x17 */
+    .long .L_ALT_OP_CONST_WIDE /* 0x18 */
+    .long .L_ALT_OP_CONST_WIDE_HIGH16 /* 0x19 */
+    .long .L_ALT_OP_CONST_STRING /* 0x1a */
+    .long .L_ALT_OP_CONST_STRING_JUMBO /* 0x1b */
+    .long .L_ALT_OP_CONST_CLASS /* 0x1c */
+    .long .L_ALT_OP_MONITOR_ENTER /* 0x1d */
+    .long .L_ALT_OP_MONITOR_EXIT /* 0x1e */
+    .long .L_ALT_OP_CHECK_CAST /* 0x1f */
+    .long .L_ALT_OP_INSTANCE_OF /* 0x20 */
+    .long .L_ALT_OP_ARRAY_LENGTH /* 0x21 */
+    .long .L_ALT_OP_NEW_INSTANCE /* 0x22 */
+    .long .L_ALT_OP_NEW_ARRAY /* 0x23 */
+    .long .L_ALT_OP_FILLED_NEW_ARRAY /* 0x24 */
+    .long .L_ALT_OP_FILLED_NEW_ARRAY_RANGE /* 0x25 */
+    .long .L_ALT_OP_FILL_ARRAY_DATA /* 0x26 */
+    .long .L_ALT_OP_THROW /* 0x27 */
+    .long .L_ALT_OP_GOTO /* 0x28 */
+    .long .L_ALT_OP_GOTO_16 /* 0x29 */
+    .long .L_ALT_OP_GOTO_32 /* 0x2a */
+    .long .L_ALT_OP_PACKED_SWITCH /* 0x2b */
+    .long .L_ALT_OP_SPARSE_SWITCH /* 0x2c */
+    .long .L_ALT_OP_CMPL_FLOAT /* 0x2d */
+    .long .L_ALT_OP_CMPG_FLOAT /* 0x2e */
+    .long .L_ALT_OP_CMPL_DOUBLE /* 0x2f */
+    .long .L_ALT_OP_CMPG_DOUBLE /* 0x30 */
+    .long .L_ALT_OP_CMP_LONG /* 0x31 */
+    .long .L_ALT_OP_IF_EQ /* 0x32 */
+    .long .L_ALT_OP_IF_NE /* 0x33 */
+    .long .L_ALT_OP_IF_LT /* 0x34 */
+    .long .L_ALT_OP_IF_GE /* 0x35 */
+    .long .L_ALT_OP_IF_GT /* 0x36 */
+    .long .L_ALT_OP_IF_LE /* 0x37 */
+    .long .L_ALT_OP_IF_EQZ /* 0x38 */
+    .long .L_ALT_OP_IF_NEZ /* 0x39 */
+    .long .L_ALT_OP_IF_LTZ /* 0x3a */
+    .long .L_ALT_OP_IF_GEZ /* 0x3b */
+    .long .L_ALT_OP_IF_GTZ /* 0x3c */
+    .long .L_ALT_OP_IF_LEZ /* 0x3d */
+    .long .L_ALT_OP_UNUSED_3E /* 0x3e */
+    .long .L_ALT_OP_UNUSED_3F /* 0x3f */
+    .long .L_ALT_OP_UNUSED_40 /* 0x40 */
+    .long .L_ALT_OP_UNUSED_41 /* 0x41 */
+    .long .L_ALT_OP_UNUSED_42 /* 0x42 */
+    .long .L_ALT_OP_UNUSED_43 /* 0x43 */
+    .long .L_ALT_OP_AGET /* 0x44 */
+    .long .L_ALT_OP_AGET_WIDE /* 0x45 */
+    .long .L_ALT_OP_AGET_OBJECT /* 0x46 */
+    .long .L_ALT_OP_AGET_BOOLEAN /* 0x47 */
+    .long .L_ALT_OP_AGET_BYTE /* 0x48 */
+    .long .L_ALT_OP_AGET_CHAR /* 0x49 */
+    .long .L_ALT_OP_AGET_SHORT /* 0x4a */
+    .long .L_ALT_OP_APUT /* 0x4b */
+    .long .L_ALT_OP_APUT_WIDE /* 0x4c */
+    .long .L_ALT_OP_APUT_OBJECT /* 0x4d */
+    .long .L_ALT_OP_APUT_BOOLEAN /* 0x4e */
+    .long .L_ALT_OP_APUT_BYTE /* 0x4f */
+    .long .L_ALT_OP_APUT_CHAR /* 0x50 */
+    .long .L_ALT_OP_APUT_SHORT /* 0x51 */
+    .long .L_ALT_OP_IGET /* 0x52 */
+    .long .L_ALT_OP_IGET_WIDE /* 0x53 */
+    .long .L_ALT_OP_IGET_OBJECT /* 0x54 */
+    .long .L_ALT_OP_IGET_BOOLEAN /* 0x55 */
+    .long .L_ALT_OP_IGET_BYTE /* 0x56 */
+    .long .L_ALT_OP_IGET_CHAR /* 0x57 */
+    .long .L_ALT_OP_IGET_SHORT /* 0x58 */
+    .long .L_ALT_OP_IPUT /* 0x59 */
+    .long .L_ALT_OP_IPUT_WIDE /* 0x5a */
+    .long .L_ALT_OP_IPUT_OBJECT /* 0x5b */
+    .long .L_ALT_OP_IPUT_BOOLEAN /* 0x5c */
+    .long .L_ALT_OP_IPUT_BYTE /* 0x5d */
+    .long .L_ALT_OP_IPUT_CHAR /* 0x5e */
+    .long .L_ALT_OP_IPUT_SHORT /* 0x5f */
+    .long .L_ALT_OP_SGET /* 0x60 */
+    .long .L_ALT_OP_SGET_WIDE /* 0x61 */
+    .long .L_ALT_OP_SGET_OBJECT /* 0x62 */
+    .long .L_ALT_OP_SGET_BOOLEAN /* 0x63 */
+    .long .L_ALT_OP_SGET_BYTE /* 0x64 */
+    .long .L_ALT_OP_SGET_CHAR /* 0x65 */
+    .long .L_ALT_OP_SGET_SHORT /* 0x66 */
+    .long .L_ALT_OP_SPUT /* 0x67 */
+    .long .L_ALT_OP_SPUT_WIDE /* 0x68 */
+    .long .L_ALT_OP_SPUT_OBJECT /* 0x69 */
+    .long .L_ALT_OP_SPUT_BOOLEAN /* 0x6a */
+    .long .L_ALT_OP_SPUT_BYTE /* 0x6b */
+    .long .L_ALT_OP_SPUT_CHAR /* 0x6c */
+    .long .L_ALT_OP_SPUT_SHORT /* 0x6d */
+    .long .L_ALT_OP_INVOKE_VIRTUAL /* 0x6e */
+    .long .L_ALT_OP_INVOKE_SUPER /* 0x6f */
+    .long .L_ALT_OP_INVOKE_DIRECT /* 0x70 */
+    .long .L_ALT_OP_INVOKE_STATIC /* 0x71 */
+    .long .L_ALT_OP_INVOKE_INTERFACE /* 0x72 */
+    .long .L_ALT_OP_UNUSED_73 /* 0x73 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_RANGE /* 0x74 */
+    .long .L_ALT_OP_INVOKE_SUPER_RANGE /* 0x75 */
+    .long .L_ALT_OP_INVOKE_DIRECT_RANGE /* 0x76 */
+    .long .L_ALT_OP_INVOKE_STATIC_RANGE /* 0x77 */
+    .long .L_ALT_OP_INVOKE_INTERFACE_RANGE /* 0x78 */
+    .long .L_ALT_OP_UNUSED_79 /* 0x79 */
+    .long .L_ALT_OP_UNUSED_7A /* 0x7a */
+    .long .L_ALT_OP_NEG_INT /* 0x7b */
+    .long .L_ALT_OP_NOT_INT /* 0x7c */
+    .long .L_ALT_OP_NEG_LONG /* 0x7d */
+    .long .L_ALT_OP_NOT_LONG /* 0x7e */
+    .long .L_ALT_OP_NEG_FLOAT /* 0x7f */
+    .long .L_ALT_OP_NEG_DOUBLE /* 0x80 */
+    .long .L_ALT_OP_INT_TO_LONG /* 0x81 */
+    .long .L_ALT_OP_INT_TO_FLOAT /* 0x82 */
+    .long .L_ALT_OP_INT_TO_DOUBLE /* 0x83 */
+    .long .L_ALT_OP_LONG_TO_INT /* 0x84 */
+    .long .L_ALT_OP_LONG_TO_FLOAT /* 0x85 */
+    .long .L_ALT_OP_LONG_TO_DOUBLE /* 0x86 */
+    .long .L_ALT_OP_FLOAT_TO_INT /* 0x87 */
+    .long .L_ALT_OP_FLOAT_TO_LONG /* 0x88 */
+    .long .L_ALT_OP_FLOAT_TO_DOUBLE /* 0x89 */
+    .long .L_ALT_OP_DOUBLE_TO_INT /* 0x8a */
+    .long .L_ALT_OP_DOUBLE_TO_LONG /* 0x8b */
+    .long .L_ALT_OP_DOUBLE_TO_FLOAT /* 0x8c */
+    .long .L_ALT_OP_INT_TO_BYTE /* 0x8d */
+    .long .L_ALT_OP_INT_TO_CHAR /* 0x8e */
+    .long .L_ALT_OP_INT_TO_SHORT /* 0x8f */
+    .long .L_ALT_OP_ADD_INT /* 0x90 */
+    .long .L_ALT_OP_SUB_INT /* 0x91 */
+    .long .L_ALT_OP_MUL_INT /* 0x92 */
+    .long .L_ALT_OP_DIV_INT /* 0x93 */
+    .long .L_ALT_OP_REM_INT /* 0x94 */
+    .long .L_ALT_OP_AND_INT /* 0x95 */
+    .long .L_ALT_OP_OR_INT /* 0x96 */
+    .long .L_ALT_OP_XOR_INT /* 0x97 */
+    .long .L_ALT_OP_SHL_INT /* 0x98 */
+    .long .L_ALT_OP_SHR_INT /* 0x99 */
+    .long .L_ALT_OP_USHR_INT /* 0x9a */
+    .long .L_ALT_OP_ADD_LONG /* 0x9b */
+    .long .L_ALT_OP_SUB_LONG /* 0x9c */
+    .long .L_ALT_OP_MUL_LONG /* 0x9d */
+    .long .L_ALT_OP_DIV_LONG /* 0x9e */
+    .long .L_ALT_OP_REM_LONG /* 0x9f */
+    .long .L_ALT_OP_AND_LONG /* 0xa0 */
+    .long .L_ALT_OP_OR_LONG /* 0xa1 */
+    .long .L_ALT_OP_XOR_LONG /* 0xa2 */
+    .long .L_ALT_OP_SHL_LONG /* 0xa3 */
+    .long .L_ALT_OP_SHR_LONG /* 0xa4 */
+    .long .L_ALT_OP_USHR_LONG /* 0xa5 */
+    .long .L_ALT_OP_ADD_FLOAT /* 0xa6 */
+    .long .L_ALT_OP_SUB_FLOAT /* 0xa7 */
+    .long .L_ALT_OP_MUL_FLOAT /* 0xa8 */
+    .long .L_ALT_OP_DIV_FLOAT /* 0xa9 */
+    .long .L_ALT_OP_REM_FLOAT /* 0xaa */
+    .long .L_ALT_OP_ADD_DOUBLE /* 0xab */
+    .long .L_ALT_OP_SUB_DOUBLE /* 0xac */
+    .long .L_ALT_OP_MUL_DOUBLE /* 0xad */
+    .long .L_ALT_OP_DIV_DOUBLE /* 0xae */
+    .long .L_ALT_OP_REM_DOUBLE /* 0xaf */
+    .long .L_ALT_OP_ADD_INT_2ADDR /* 0xb0 */
+    .long .L_ALT_OP_SUB_INT_2ADDR /* 0xb1 */
+    .long .L_ALT_OP_MUL_INT_2ADDR /* 0xb2 */
+    .long .L_ALT_OP_DIV_INT_2ADDR /* 0xb3 */
+    .long .L_ALT_OP_REM_INT_2ADDR /* 0xb4 */
+    .long .L_ALT_OP_AND_INT_2ADDR /* 0xb5 */
+    .long .L_ALT_OP_OR_INT_2ADDR /* 0xb6 */
+    .long .L_ALT_OP_XOR_INT_2ADDR /* 0xb7 */
+    .long .L_ALT_OP_SHL_INT_2ADDR /* 0xb8 */
+    .long .L_ALT_OP_SHR_INT_2ADDR /* 0xb9 */
+    .long .L_ALT_OP_USHR_INT_2ADDR /* 0xba */
+    .long .L_ALT_OP_ADD_LONG_2ADDR /* 0xbb */
+    .long .L_ALT_OP_SUB_LONG_2ADDR /* 0xbc */
+    .long .L_ALT_OP_MUL_LONG_2ADDR /* 0xbd */
+    .long .L_ALT_OP_DIV_LONG_2ADDR /* 0xbe */
+    .long .L_ALT_OP_REM_LONG_2ADDR /* 0xbf */
+    .long .L_ALT_OP_AND_LONG_2ADDR /* 0xc0 */
+    .long .L_ALT_OP_OR_LONG_2ADDR /* 0xc1 */
+    .long .L_ALT_OP_XOR_LONG_2ADDR /* 0xc2 */
+    .long .L_ALT_OP_SHL_LONG_2ADDR /* 0xc3 */
+    .long .L_ALT_OP_SHR_LONG_2ADDR /* 0xc4 */
+    .long .L_ALT_OP_USHR_LONG_2ADDR /* 0xc5 */
+    .long .L_ALT_OP_ADD_FLOAT_2ADDR /* 0xc6 */
+    .long .L_ALT_OP_SUB_FLOAT_2ADDR /* 0xc7 */
+    .long .L_ALT_OP_MUL_FLOAT_2ADDR /* 0xc8 */
+    .long .L_ALT_OP_DIV_FLOAT_2ADDR /* 0xc9 */
+    .long .L_ALT_OP_REM_FLOAT_2ADDR /* 0xca */
+    .long .L_ALT_OP_ADD_DOUBLE_2ADDR /* 0xcb */
+    .long .L_ALT_OP_SUB_DOUBLE_2ADDR /* 0xcc */
+    .long .L_ALT_OP_MUL_DOUBLE_2ADDR /* 0xcd */
+    .long .L_ALT_OP_DIV_DOUBLE_2ADDR /* 0xce */
+    .long .L_ALT_OP_REM_DOUBLE_2ADDR /* 0xcf */
+    .long .L_ALT_OP_ADD_INT_LIT16 /* 0xd0 */
+    .long .L_ALT_OP_RSUB_INT /* 0xd1 */
+    .long .L_ALT_OP_MUL_INT_LIT16 /* 0xd2 */
+    .long .L_ALT_OP_DIV_INT_LIT16 /* 0xd3 */
+    .long .L_ALT_OP_REM_INT_LIT16 /* 0xd4 */
+    .long .L_ALT_OP_AND_INT_LIT16 /* 0xd5 */
+    .long .L_ALT_OP_OR_INT_LIT16 /* 0xd6 */
+    .long .L_ALT_OP_XOR_INT_LIT16 /* 0xd7 */
+    .long .L_ALT_OP_ADD_INT_LIT8 /* 0xd8 */
+    .long .L_ALT_OP_RSUB_INT_LIT8 /* 0xd9 */
+    .long .L_ALT_OP_MUL_INT_LIT8 /* 0xda */
+    .long .L_ALT_OP_DIV_INT_LIT8 /* 0xdb */
+    .long .L_ALT_OP_REM_INT_LIT8 /* 0xdc */
+    .long .L_ALT_OP_AND_INT_LIT8 /* 0xdd */
+    .long .L_ALT_OP_OR_INT_LIT8 /* 0xde */
+    .long .L_ALT_OP_XOR_INT_LIT8 /* 0xdf */
+    .long .L_ALT_OP_SHL_INT_LIT8 /* 0xe0 */
+    .long .L_ALT_OP_SHR_INT_LIT8 /* 0xe1 */
+    .long .L_ALT_OP_USHR_INT_LIT8 /* 0xe2 */
+    .long .L_ALT_OP_IGET_VOLATILE /* 0xe3 */
+    .long .L_ALT_OP_IPUT_VOLATILE /* 0xe4 */
+    .long .L_ALT_OP_SGET_VOLATILE /* 0xe5 */
+    .long .L_ALT_OP_SPUT_VOLATILE /* 0xe6 */
+    .long .L_ALT_OP_IGET_OBJECT_VOLATILE /* 0xe7 */
+    .long .L_ALT_OP_IGET_WIDE_VOLATILE /* 0xe8 */
+    .long .L_ALT_OP_IPUT_WIDE_VOLATILE /* 0xe9 */
+    .long .L_ALT_OP_SGET_WIDE_VOLATILE /* 0xea */
+    .long .L_ALT_OP_SPUT_WIDE_VOLATILE /* 0xeb */
+    .long .L_ALT_OP_BREAKPOINT /* 0xec */
+    .long .L_ALT_OP_THROW_VERIFICATION_ERROR /* 0xed */
+    .long .L_ALT_OP_EXECUTE_INLINE /* 0xee */
+    .long .L_ALT_OP_EXECUTE_INLINE_RANGE /* 0xef */
+    .long .L_ALT_OP_INVOKE_OBJECT_INIT_RANGE /* 0xf0 */
+    .long .L_ALT_OP_RETURN_VOID_BARRIER /* 0xf1 */
+    .long .L_ALT_OP_IGET_QUICK /* 0xf2 */
+    .long .L_ALT_OP_IGET_WIDE_QUICK /* 0xf3 */
+    .long .L_ALT_OP_IGET_OBJECT_QUICK /* 0xf4 */
+    .long .L_ALT_OP_IPUT_QUICK /* 0xf5 */
+    .long .L_ALT_OP_IPUT_WIDE_QUICK /* 0xf6 */
+    .long .L_ALT_OP_IPUT_OBJECT_QUICK /* 0xf7 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_QUICK /* 0xf8 */
+    .long .L_ALT_OP_INVOKE_VIRTUAL_QUICK_RANGE /* 0xf9 */
+    .long .L_ALT_OP_INVOKE_SUPER_QUICK /* 0xfa */
+    .long .L_ALT_OP_INVOKE_SUPER_QUICK_RANGE /* 0xfb */
+    .long .L_ALT_OP_IPUT_OBJECT_VOLATILE /* 0xfc */
+    .long .L_ALT_OP_SGET_OBJECT_VOLATILE /* 0xfd */
+    .long .L_ALT_OP_SPUT_OBJECT_VOLATILE /* 0xfe */
+    .long .L_ALT_OP_UNUSED_FF /* 0xff */
+/* File: x86/entry.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+
+    .text
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(Thread* self)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp                 # save caller base pointer
+    movl    %esp, %ebp           # set our %ebp
+    movl    rSELF, %ecx          # get incoming rSELF
+/*
+ * At this point we've allocated one slot on the stack
+ * via push and stack is 8-byte aligned.  Allocate space
+ * for 9 spill slots, 4 local slots, 5 arg slots to bring
+ * us to 16-byte alignment
+ */
+    subl    $(FRAME_SIZE-4), %esp
+
+/* Spill callee save regs */
+    movl    %edi,EDI_SPILL(%ebp)
+    movl    %esi,ESI_SPILL(%ebp)
+    movl    %ebx,EBX_SPILL(%ebp)
+
+/* Set up "named" registers */
+    movl    offThread_pc(%ecx),rPC
+    movl    offThread_curFrame(%ecx),rFP
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+
+    /* Remember %esp for future "longjmp" */
+    movl    %esp,offThread_bailPtr(%ecx)
+
+    /* Fetch next instruction before potential jump */
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    movl        $0, offThread_inJitCodeCache(%ecx)
+    cmpl        $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+
+   /* Normal case: start executing the instruction at rPC */
+    GOTO_NEXT
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(Thread* self, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ *  esp+4 (arg0)  Thread* self
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab self
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+#ifdef WITH_JIT
+    .global     dvmNcgInvokeInterpreter
+    .type       dvmNcgInvokeInterpreter, %function
+/* input: start of method in %eax */
+dvmNcgInvokeInterpreter:
+    movl        %eax, rPC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx                    # %edx<- opcode
+    GOTO_NEXT_R %ecx                    # start executing the instruction at rPC
+#endif
+
+/*
+ * Strings
+ */
+    .section    .rodata
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+
+
+/* File: x86/footer.S */
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+#if defined(WITH_JIT)
+/*
+ * JIT-related re-entries into the interpreter.  In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle.  If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
+ */
+
+    .global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction.  We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
+dvmJitToInterpPunt:
+    GET_PC
+#if defined(WITH_JIT_TUNING)
+    movl   rPC, OUT_ARG0(%esp)
+    call   dvmBumpPunt
+#endif
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    movl        $0, offThread_inJitCodeCache(%ecx)
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+    .global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ *   0(%esp)          <= native return address within trace
+ *   rPC              <= Dalvik PC of this instruction
+ *   OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
+dvmJitToInterpSingleStep:
+/* TODO */
+    call     dvmAbort
+#if 0
+    pop    %eax
+    movl   rSELF, %ecx
+    movl   OUT_ARG0(%esp), %edx
+    movl   %eax,offThread_jitResumeNPC(%ecx)
+    movl   %edx,offThread_jitResumeDPC(%ecx)
+    movl   $kInterpEntryInstr,offThread_entryPoint(%ecx)
+    movl   $1,rINST     # changeInterp <= true
+    jmp    common_gotoBail
+#endif
+
+    .global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation.  Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    SPILL_TMP1(%eax)
+    call   dvmBumpNoChain
+    UNSPILL_TMP1(%eax)
+#endif
+    movl   %eax, rPC
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx                # ecx <- self
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmpl   $0, %eax
+    jz     1f
+    jmp    *%eax                     # exec translation if we've got one
+    # won't return
+1:
+    EXPORT_PC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation from the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    movl   %edx, OUT_ARG0(%esp)
+    call   dvmBumpNoChain
+#endif
+    movl   %ebx, rPC
+    lea    4(%esp), %esp #to recover the esp update due to function call
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx
+    cmpl   $0,%eax
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1f
+    jmp    *%eax              # jump to tranlation
+    # won't return
+
+/* No Translation - request one */
+1:
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmpl   $0, %eax          # JIT enabled?
+    jnz    2f                 # Request one if so
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx         # Continue interpreting if not
+    GOTO_NEXT_R %ecx
+2:
+    ## Looks like an EXPORT_PC is needed here. Now jmp to common_selectTrace2
+    movl   $kJitTSelectRequestHot,%eax # ask for trace select
+    jmp    common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target.  Reached via a call, and
+ * (TOS)->rPC.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    movl   0(%esp), %eax          # get return address
+    movl   %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    lea    4(%esp), %esp #to recover the esp update due to function call
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea    -17(%eax), %ebx #$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea    -4(%esp), %esp
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread # (pc, self)
+    lea    4(%esp), %esp
+    cmpl   $0,%eax
+    movl   rSELF, %ecx
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1b                 # no - ask for one
+    movl   %eax,OUT_ARG0(%esp)
+    movl   rINST,OUT_ARG1(%esp)
+    call   dvmJitChain        # Attempt dvmJitChain(codeAddr,chainAddr)
+    cmpl   $0,%eax           # Success?
+    jz     toInterpreter      # didn't chain - interpret
+    jmp    *%eax
+    # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+
+    .global     dvmJitToExceptionThrown
+dvmJitToExceptionThrown: //rPC in
+    movl   rSELF, %edx
+    GET_PC
+    movl   $0, offThread_inJitCodeCache(%edx)
+    jmp common_exceptionThrown
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+/* one input: the target rPC value */
+    movl        0(%esp), %eax          # get return address
+    movl        %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea         -17(%eax), %ebx #$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea         4(%esp), %esp
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    #lea         4(%esp), %esp
+    cmp         $0, %eax
+    je          toInterpreter
+    #lea         -8(%esp), %esp
+    movl        %ebx, OUT_ARG1(%esp)    # %ebx live thorugh dvmJitGetTraceAddrThread
+    movl        %eax, OUT_ARG0(%esp)    # first argument
+    call        dvmJitChain
+    #lea         8(%esp), %esp
+    cmp         $0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+dvmJitToInterpNoChain: #rPC in eax
+#if defined(WITH_JIT_TUNING)
+    SPILL_TMP1(%eax)
+    call   dvmBumpNoChain
+    UNSPILL_TMP1(%eax)
+#endif
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    movl        %eax, rPC
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmp         $0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+toInterpreter:
+    EXPORT_PC
+    movl        rSELF, %ecx
+    movl        offThread_curHandlerTable(%ecx), rIBASE
+    FETCH_INST
+    movl        offThread_pJitProfTable(%ecx), %eax
+    #Fallthrough
+
+/* ebx holds the pointer to the jit profile table
+   edx has the opCode */
+common_testUpdateProfile:
+    cmp         $0, %eax
+    je          4f
+/* eax holds the pointer to the jit profile table
+   edx has the opCode
+   rPC points to the next bytecode */
+
+common_updateProfile:
+    # quick & dirty hash
+    movl   rPC, %ecx
+    shrl   $12, %ecx
+    xorl   rPC, %ecx
+    andl   $((1<<JIT_PROF_SIZE_LOG_2)-1), %ecx
+    decb   (%ecx,%eax)
+    #jmp    1f # remove
+    jz     2f
+1:
+    GOTO_NEXT
+2:
+common_Profile:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection.  First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+    SPILL(rIBASE)
+    SPILL_TMP1(rINST)
+    movl        rSELF, rIBASE
+    GET_JIT_THRESHOLD rIBASE rINST  # leaves rSELF in %ecx
+    EXPORT_PC
+    movb   rINSTbl,(%ecx,%eax)   # reset counter
+    movl   rIBASE,rINST            # preserve rSELF
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   rIBASE,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    UNSPILL(rIBASE)
+    movl   %eax,offThread_inJitCodeCache(rINST)   # set the inJitCodeCache flag
+    UNSPILL_TMP1(rINST)
+    cmpl   $0,%eax
+    #jmp    1f # remove
+    jz     1f
+    jmp   *%eax        # TODO: decide call vs/ jmp!.  No return either way
+1:
+    movl   $kJitTSelectRequest,%eax
+    # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movzwl      offThread_subMode(%ebx), %ecx
+    and         $(kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+    movl        %eax, offThread_jitState(%ebx)
+    movl        rSELF, %eax
+    movl       %eax, OUT_ARG0(%esp)
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   mov          EBX_SPILL(%ebp), %ebx
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+common_selectTrace2:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movl        %ebx, OUT_ARG0(%esp)
+    movl        %eax, offThread_jitState(%ebx)
+    movzwl      offThread_subMode(%ebx), %ecx
+    mov         EBX_SPILL(%ebp), %ebx
+    and         (kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+
+
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+#endif
+
+/*
+ * For the invoke codes we need to know what register holds the "this" pointer. However
+ * it seems the this pointer is assigned consistently most times it is in %ecx but other
+ * times it is in OP_INVOKE_INTERFACE, OP_INVOKE_SUPER_QUICK, or OP_INVOKE_VIRTUAL_QUICK.
+*/
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = "this"
+ *   rINSTw trashed, must reload
+ *   rIBASE trashed, must reload before resuming interpreter
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST       # rINST<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+    test        rINST, rINST
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+
+
+   /*
+    * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count,
+    * %edx=&outs (&stackSaveArea).  (very few methods have > 10 args;
+    * could unroll for common cases)
+    */
+
+.LinvokeRangeArgs:
+    movl        %ebx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- save %ebx
+    lea         (rFP, %ecx, 4), %ecx    # %ecx<- &vCCCC
+    shll        $2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+    subl        LOCAL0_OFFSET(%ebp), %edx       # %edx<- update &outs
+    shrl        $2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+1:
+    movl        (%ecx), %ebx            # %ebx<- vCCCC
+    lea         4(%ecx), %ecx           # %ecx<- &vCCCC++
+    subl        $1, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+    movl        %ebx, (%edx)            # *outs<- vCCCC
+    lea         4(%edx), %edx           # outs++
+    jne         1b                      # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+    movl        LOCAL1_OFFSET(%ebp), %ebx       # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    * rIBASE trashed, must reload before resuming interpreter
+    */
+
+common_invokeMethodNoRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST       # rINST<- BA
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+    shrl        $4, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    movzwl      4(rPC), %ecx            # %ecx<- GFED
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+
+   /*
+    * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $2, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 2
+    movl        %ecx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $4, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $15, rINST             # rINSTw<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, (%edx)            # *outs<- vA
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+4:
+    shr         $12, %ecx              # %ecx<- G
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vG
+    movl        %ecx, (%edx)            # *outs<- vG
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+3:
+    and         $0x0f00, %ecx          # %ecx<- 0F00
+    shr         $8, %ecx               # %ecx<- F
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vF
+    movl        %ecx, (%edx)            # *outs<- vF
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+2:
+    and         $0x00f0, %ecx          # %ecx<- 00E0
+    shr         $4, %ecx               # %ecx<- E
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vE
+    movl        %ecx, (%edx)            # *outs<- vE
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+1:
+    and         $0x000f, %ecx          # %ecx<- 000D
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vD
+    movl        %ecx, -4(%edx)          # *--outs<- vD
+0:
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+    movl        %eax, LOCAL0_OFFSET(%ebp)       # LOCAL0_OFFSET<- methodToCall
+    shl         $2, %edx               # %edx<- update offset
+    SAVEAREA_FROM_FP %eax               # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    movl        rSELF,%edx              # %edx<- pthread
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offThread_interpStackEnd(%edx), %edx # %edx<- self->interpStackEnd
+    movl        %edx, TMP_SPILL1(%ebp)  # spill self->interpStackEnd
+    shl         $2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         TMP_SPILL1(%ebp), %eax  # compare interpStackEnd and bottom
+    movl        LOCAL0_OFFSET(%ebp), %eax       # %eax<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rSELF,%ecx              # %ecx<- pthread
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+#if defined(WITH_JIT)
+    movl        $0, offStackSaveArea_returnAddr(%edx)
+#endif
+
+    /* Any special actions to take? */
+    cmpw        $0, offThread_subMode(%ecx)
+    jne         2f                     # Yes - handle them
+1:
+    testl       $ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+    movl        %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "self" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offThread_method(%ecx) # self->method<- methodToCall
+    movl        %edx, offThread_methodClassDex(%ecx) # self->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        $1, offThread_debugIsMethodEntry(%ecx)
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # curFrame<-newFP
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    /* rPC is already updated */
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+2:
+    /*
+     * On entry, preserve all:
+     *  %eax: method
+     *  %ecx: self
+     *  %edx: new save area
+     */
+    SPILL_TMP1(%eax)                   # preserve methodToCall
+    SPILL_TMP2(%edx)                   # preserve newSaveArea
+    movl        rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl        %ecx, OUT_ARG0(%esp)
+    movl        %eax, OUT_ARG1(%esp)
+    call        dvmReportInvoke        # (self, method)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%edx)
+    movl        rSELF,%ecx             # restore rSELF
+    jmp         1b
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea, %ecx=self
+    */
+
+.LinvokeNative:
+    movl        offThread_jniLocal_topCookie(%ecx), rINST # rINST<- self->localRef->...
+    movl        rINST, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, LOCAL2_OFFSET(%ebp)  # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), rINST # rINST<- newFP
+    movl        rINST, offThread_curFrame(%ecx)  # curFrame<- newFP
+    cmpw        $0, offThread_subMode(%ecx)  # Anything special going on?
+    jne         11f                     # yes - handle it
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)    # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+7:
+    movl        LOCAL2_OFFSET(%ebp), %ecx    # %ecx<- newSaveArea
+    movl        rSELF, %eax             # %eax<- self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    jne         common_exceptionThrown  # handle exception
+    movl        offThread_curHandlerTable(%eax),rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx                    # jump to next instruction
+
+11:
+    /*
+     * Handle any special subMode actions
+     * %eax=methodToCall, rINST=newFP, %ecx=self
+     */
+    SPILL_TMP1(%eax)                    # save methodTocall
+    movl        rPC, offThread_pc(%ecx)
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPreNativeInvoke # (methodToCall, self, fp)
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF,%ecx              # restore self
+
+    /* Do the native call */
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)   # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPostNativeInvoke # (methodToCall, self, fp)
+    jmp         7b                      # rejoin
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        rSELF,%eax              # %eax<- self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    movl    rSELF, %ecx
+    SAVEAREA_FROM_FP %eax                       # %eax<- saveArea(old)
+    cmpw    $0, offThread_subMode(%ecx)          # special action needed?
+    jne     19f                                   # go if so
+14:
+
+    movl        offStackSaveArea_prevFrame(%eax), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), rINST # rINST<- method we are returning to
+    cmpl        $0, rINST               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    movl        offStackSaveArea_savedPc(%eax), rPC # rPC<- saveAreaOld->savedPc
+#if defined(WITH_JIT)
+    movl        offStackSaveArea_returnAddr(%eax), %ecx
+#endif
+    movl        rSELF, %eax
+    movl        rINST, offThread_method(%eax) # glue->method<- newSave->method
+    movl        offMethod_clazz(rINST), rINST # rINST<- method->clazz
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+#if defined(WITH_JIT)
+    //update self->offThread_inJitCodeCache
+    movl        %ecx, offThread_inJitCodeCache(%eax)
+#endif
+    movl        offClassObject_pDvmDex(rINST), rINST # rINST<- method->clazz->pDvmDex
+    movl        rINST, offThread_methodClassDex(%eax) # glue->pDvmDex<- method->clazz->pDvmDex
+#if defined(WITH_JIT)
+    cmp         $0, %ecx
+    je          .returnToBC
+    movl        %ecx, %eax
+    jmp         *%eax
+#endif
+
+.returnToBC:
+
+#if defined(WITH_JIT)
+    FETCH_INST_OPCODE  3, %ecx                 # %eax<- next instruction hi; fetch, advance
+    // %ecx has the opcode
+    addl         $6, rPC               # 3*2 = 6
+    SPILL_TMP1   (%ecx)
+    movl         rSELF, %ecx
+    FETCH_INST
+    UNSPILL_TMP1   (%ecx)
+    movzbl      1(rPC), rINST
+    jmp     *(rIBASE,%ecx,4)
+#else
+    FETCH_INST_WORD 3
+    ADVANCE_PC 3
+    GOTO_NEXT
+#endif
+
+19:
+    /*
+     * Handle special subMode actions
+     * On entry, rFP: prevFP, %ecx: self, %eax: saveArea
+     */
+    SPILL_TMP1(%ebx)
+    movl     offStackSaveArea_prevFrame(%eax), %ebx # %ebx<- saveArea->PrevFrame
+    movl     rPC, offThread_pc(%ecx)          # update interpSave.pc
+    movl     %ebx, offThread_curFrame(%ecx)    # update interpSave.curFrame
+    movl     %ecx, OUT_ARG0(%esp)             # parameter self
+    call     dvmReportReturn                  # (self)
+    UNSPILL_TMP1(%ebx)
+    movl     rSELF, %ecx                      # restore self
+    SAVEAREA_FROM_FP %eax                     # restore saveArea
+    jmp      14b
+
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST holds changeInterp
+ *    ecx holds self pointer
+ *
+ * expected profile: dvmMterpStdBail(Thread *self, bool changeInterp)
+ */
+common_gotoBail:
+    movl   rPC,offThread_pc(%ecx)     # export state to self
+    movl   rFP,offThread_curFrame(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)      # self in arg0
+    movl   rINST,OUT_ARG1(%esp)     # changeInterp in arg1
+    call   dvmMterpStdBail          # bail out....
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ *
+ * eax = Method* methodToCall
+ * ecx = "this"
+ * edx = rSELF
+ * ebx = free to use
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     $0, %ecx
+    je      2f
+    movl    offObject_clazz(%ecx), %ecx
+2:
+    movl    rSELF, %ebx
+    movl    %eax, offThread_methodToCall(%ebx)
+    movl    %ecx, offThread_callsiteClass(%ebx)
+    ret
+#endif
+
+#if defined(WITH_JIT)
+
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     %ecx: &dvmDex->pResFields[field]
+     *     %eax:  field pointer (must preserve)
+     */
+common_verifyField:
+    movl    %ebx, TMP_SPILL1(%ebp)
+    movl     rSELF, %ebx
+    movzwl   offThread_subMode(%ebx), %ebx
+    andl     $kSubModeJitTraceBuild, %ebx
+    movl    TMP_SPILL1(%ebp), %ebx
+    jne      1f
+    ret
+1:
+    movl    (%ecx), %ecx
+    cmp     $0, %ecx
+    je      1f
+    ret
+1:
+    SPILL_TMP1(%eax)
+    SPILL_TMP2(%edx)
+    movl     rSELF, %ecx
+    # Because we call into this helper from a bytecode, we have
+    # to be careful not to write over the return address when using
+    # the OUT_ARG macros
+    lea      -8(%esp), %esp
+    movl     %ecx, OUT_ARG0(%esp)
+    movl     rPC, OUT_ARG1(%esp)
+    call     dvmJitEndTraceSelect
+    lea      8(%esp), %esp
+    UNSPILL_TMP2(%edx)
+    UNSPILL_TMP1(%eax)
+    ret
+#endif
+
+/*
+ * After returning from a "selfd" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+     movl  rSELF, %eax
+     movl  offThread_pc(%eax),rPC
+     movl  offThread_curFrame(%eax),rFP
+     movl  offThread_curHandlerTable(%eax),rIBASE
+     FETCH_INST
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC
+    movl    $.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowArithmeticException
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, len in eax
+ */
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)                  # arg0<- len
+    call    dvmThrowNegativeArraySizeException   # (len)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, method name in eax
+ */
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNoSuchMethodError
+    jmp     common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one.  Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+    EXPORT_PC
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNullPointerException
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ * On entry:
+ *    eax <- array object
+ *    ecx <- index
+ */
+common_errArrayIndex:
+    EXPORT_PC
+    movl    offArrayObject_length(%eax), %eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmThrowArrayIndexOutOfBoundsException   # args (length, index)
+    jmp     common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * NOTE: special subMode handling done in dvmMterp_exceptionThrown
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC
+    movl       rSELF, %ecx
+    movl       %ecx, OUT_ARG0(%esp)
+    call       dvmCheckSuspendPending
+
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %edx   # %edx <- self->exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmAddTrackedAlloc      # don't let the exception be GCed
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax    # get subMode flags
+    movl       $0, offThread_exception(%ecx)
+
+    # Special subMode?
+    cmpl       $0, %eax                # any special subMode handling needed?
+    je         8f                      # go if so
+
+    # Manage debugger bookkeeping
+    movl       rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl       rFP, offThread_curFrame(%ecx) # update interpSave.curFrame
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmReportExceptionThrow # (self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+
+8:
+    /*
+    * set up args and a local for &fp
+    */
+    lea        20(%esp), %esp          # raise %esp
+    movl       rFP, (%esp)               # save fp
+    movl       %esp, %eax              # %eax = &fp
+    lea        -20(%esp), %esp         # reset %esp
+    movl       %eax, OUT_ARG4(%esp)    # Arg 4 = &fp
+    movl       $0, OUT_ARG3(%esp)      # Arg 3 = false
+    movl       %edx, OUT_ARG2(%esp)    # Arg 2 = exception
+    movl       %ecx, OUT_ARG0(%esp)    # Arg 0 = self
+
+    movl       offThread_method(%ecx), %eax # %eax = self->method
+    movl       offMethod_insns(%eax), %eax  # %eax = self->method->insn
+    movl       rPC, %ecx
+    subl       %eax, %ecx              # %ecx = pc - self->method->insn
+    sar        $1, %ecx                # adjust %ecx for code offset
+    movl       %ecx, OUT_ARG1(%esp)    # Arg 1 = %ecx
+
+    /* call, %eax gets catchRelPc (a code-unit offset) */
+    SPILL_TMP1(%edx)                   # save exception
+    call       dvmFindCatchBlock       # call(self, relPc, exc, scan?, &fp)
+    UNSPILL_TMP1(%edx)                 # restore exception
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    movl       rSELF, %ecx
+    cmpl       $0, offThread_stackOverflowed(%ecx) # did we overflow?
+    je         1f                         # no, skip ahead
+    movl       %eax, rFP                  # save relPc result in rFP
+    movl       %ecx, OUT_ARG0(%esp)       # Arg 0 = self
+    movl       %edx, OUT_ARG1(%esp)       # Arg 1 = exception
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow    # call(self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rFP, %eax                  # restore result
+    movl       rSELF, %ecx
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    movl       20(%esp), rFP              # retrieve the updated rFP
+    cmpl       $0, %eax                  # is catchRelPc < 0?
+    jl         .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP rINST             # rINST<- new save area
+    movl       offStackSaveArea_method(rINST), rINST # rINST<- new method
+    movl       rINST, offThread_method(%ecx)         # self->method = new method
+    movl       offMethod_clazz(rINST), %ecx          # %ecx = method->clazz
+    movl       offMethod_insns(rINST), rINST         # rINST = method->insn
+    movl       offClassObject_pDvmDex(%ecx), %ecx    # %ecx = method->clazz->pDvmDex
+    lea        (rINST, %eax, 2), rPC      # rPC<- method->insns + catchRelPc
+    movl       rSELF, rINST
+    movl       %ecx, offThread_methodClassDex(rINST) # self->pDvmDex = method->clazz->pDvmDex
+
+    /* release the tracked alloc on the exception */
+    movl       %edx, OUT_ARG0(%esp)       # Arg 0 = exception
+    movl       rINST, OUT_ARG1(%esp)      # Arg 1 = self
+    SPILL_TMP1(%edx)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    UNSPILL_TMP1(%edx)
+
+    /* restore the exception if the handler wants it */
+    movl       rSELF, %ecx
+    FETCH_INST
+    movzbl     rINSTbl, %eax
+    cmpl       $OP_MOVE_EXCEPTION, %eax   # is it "move-exception"?
+    jne        1f
+    movl       %edx, offThread_exception(%ecx) # restore exception
+1:
+    movl       offThread_curHandlerTable(%ecx), rIBASE # refresh rIBASE
+    GOTO_NEXT
+
+.LnotCaughtLocally: # %edx = exception
+    /* fix stack overflow if necessary */
+    movl       rSELF, %ecx
+    movl       offThread_stackOverflowed(%ecx), %eax
+    cmpl       $0, %eax                   # did we overflow earlier?
+    je         1f
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow
+    UNSPILL_TMP1(%edx)
+
+1:
+    movl       rSELF, %ecx
+    movl       %edx, offThread_exception(%ecx) #restore exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    movl       rSELF, %ecx
+    jmp        common_gotoBail            # bail out
+
+common_abort:
+    movl    $0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImplA:
+    .asciz  "filled-new-array only implemented for 'int'"
+
diff --git a/vm/mterp/out/InterpC-allstubs.cpp b/vm/mterp/out/InterpC-allstubs.cpp
new file mode 100644
index 0000000..1ef8783
--- /dev/null
+++ b/vm/mterp/out/InterpC-allstubs.cpp
@@ -0,0 +1,4069 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_NOP.cpp */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.cpp */
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.cpp */
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.cpp */
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.cpp */
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.cpp */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.cpp */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.cpp */
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.cpp */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.cpp */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.cpp */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.cpp */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.cpp */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.cpp */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.cpp */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.cpp */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.cpp */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.cpp */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.cpp */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.cpp */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.cpp */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_THROW.cpp */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.cpp */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.cpp */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.cpp */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.cpp */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.cpp */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.cpp */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.cpp */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.cpp */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.cpp */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.cpp */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.cpp */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.cpp */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.cpp */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.cpp */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.cpp */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.cpp */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.cpp */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.cpp */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.cpp */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.cpp */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.cpp */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.cpp */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.cpp */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.cpp */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.cpp */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.cpp */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.cpp */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.cpp */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.cpp */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.cpp */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.cpp */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.cpp */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.cpp */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.cpp */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.cpp */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.cpp */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.cpp */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.cpp */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.cpp */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.cpp */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.cpp */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.cpp */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.cpp */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.cpp */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.cpp */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.cpp */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.cpp */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.cpp */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.cpp */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.cpp */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.cpp */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.cpp */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.cpp */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.cpp */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.cpp */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.cpp */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.cpp */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.cpp */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.cpp */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.cpp */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.cpp */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.cpp */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.cpp */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.cpp */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.cpp */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.cpp */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.cpp */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.cpp */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.cpp */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.cpp */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.cpp */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.cpp */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.cpp */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.cpp */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.cpp */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.cpp */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.cpp */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.cpp */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.cpp */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.cpp */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_IGET_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.cpp */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: cstubs/entry.cpp */
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) (const void*) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point.  This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+void dvmMterpStdRun(Thread* self)
+{
+    jmp_buf jmpBuf;
+
+    self->interpSave.bailPtr = &jmpBuf;
+
+    /* We exit via a longjmp */
+    if (setjmp(jmpBuf)) {
+        LOGVV("mterp threadid=%d returning", dvmThreadSelf()->threadId);
+        return;
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(Thread* self);
+
+        u2 inst = /*self->interpSave.*/pc[0];
+        /*
+         * In mterp, dvmCheckBefore is handled via the altHandlerTable,
+         * while in the portable interpreter it is part of the handler
+         * FINISH code.  For allstubs, we must do an explicit check
+         * in the interpretation loop.
+         */
+        if (self->interpBreak.ctl.subMode) {
+            dvmCheckBefore(pc, fp, self);
+        }
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        (void) gDvmMterpHandlerNames;   /* avoid gcc "defined but not used" */
+        LOGVV("handler %p %s",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(self);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(Thread* self)
+{
+    jmp_buf* pJmpBuf = (jmp_buf*) self->interpSave.bailPtr;
+    longjmp(*pJmpBuf, 1);
+}
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/out/InterpC-armv5te-vfp.cpp b/vm/mterp/out/InterpC-armv5te-vfp.cpp
new file mode 100644
index 0000000..9d6b458
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te-vfp.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv5te.cpp b/vm/mterp/out/InterpC-armv5te.cpp
new file mode 100644
index 0000000..99831b1
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a-neon.cpp b/vm/mterp/out/InterpC-armv7-a-neon.cpp
new file mode 100644
index 0000000..7a3434c
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a-neon.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a.cpp b/vm/mterp/out/InterpC-armv7-a.cpp
new file mode 100644
index 0000000..1dcfb68
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a.cpp
@@ -0,0 +1,1259 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.cpp */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+  // TODO: Clang does not support asm declaration syntax.
+#ifndef __clang__
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rSELF     asm("r6");
+    register uint32_t rINST     asm("r7");
+    register uint32_t rIBASE    asm("r8");
+    register uint32_t r9        asm("r9");
+    register uint32_t r10       asm("r10");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rINST=%08x\n",
+        rPC, rFP, rSELF, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+#endif
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->shorty);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-mips.cpp b/vm/mterp/out/InterpC-mips.cpp
new file mode 100644
index 0000000..69260da
--- /dev/null
+++ b/vm/mterp/out/InterpC-mips.cpp
@@ -0,0 +1,2268 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'mips'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: mips/debug.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose MIPS registers, along with some other info.
+ *
+ */
+void dvmMterpDumpMipsRegs(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3)
+{
+    register uint32_t rPC       asm("s0");
+    register uint32_t rFP       asm("s1");
+    register uint32_t rSELF     asm("s2");
+    register uint32_t rIBASE    asm("s3");
+    register uint32_t rINST     asm("s4");
+    register uint32_t rOBJ      asm("s5");
+    register uint32_t rBIX      asm("s6");
+    register uint32_t rTEMP	asm("s7");
+
+    //extern char dvmAsmInstructionStart[];
+
+    printf("REGS: a0=%08x a1=%08x a2=%08x a3=%08x\n", a0, a1, a2, a3);
+    printf("    : rPC=%08x rFP=%08x rSELF=%08x rIBASE=%08x\n",
+        rPC, rFP, rSELF, rIBASE);
+    printf("    : rINST=%08x rOBJ=%08x rBIX=%08x rTEMP=%08x \n", rINST, rOBJ, rBIX, rTEMP);
+
+    //Thread* self = (Thread*) rSELF;
+    //const Method* method = self->method;
+    printf("    + self is %p\n", dvmThreadSelf());
+    //printf("    + currently in %s.%s %s\n",
+    //    method->clazz->descriptor, method->name, method->signature);
+    //printf("    + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+    //printf("    + next handler for 0x%02x = %p\n",
+    //    rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+    printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+    printf("  prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+        saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc);
+#else
+    printf("  prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+        saveArea->prevFrame, saveArea->savedPc,
+        saveArea->method, saveArea->xtra.currentPc,
+        *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+    /*
+     * It is a direct (non-virtual) method if it is static, private,
+     * or a constructor.
+     */
+    bool isDirect =
+        ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+        (method->name[0] == '<');
+
+    char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+    printf("<%c:%s.%s %s> ",
+            isDirect ? 'D' : 'V',
+            method->clazz->descriptor,
+            method->name,
+            desc);
+
+    free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-portable.cpp b/vm/mterp/out/InterpC-portable.cpp
new file mode 100644
index 0000000..0328aa8
--- /dev/null
+++ b/vm/mterp/out/InterpC-portable.cpp
@@ -0,0 +1,4015 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portable'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: portable/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+#define JIT_STUB_HACK(x)
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()                                                    \
+    self->interpSave.pc = pc;                                              \
+    self->interpSave.curFrame = fp;
+#define PC_TO_SELF() self->interpSave.pc = pc;
+
+/*
+ * Instruction framing.  For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ */
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        if (self->interpBreak.ctl.subMode) {                                \
+            dvmCheckBefore(pc, fp, self);                                   \
+        }                                                                   \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+
+#define OP_END
+
+/*
+ * The "goto" targets just turn into goto statements.  The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        methodCallRange = _methodCallRange;                                 \
+        goto _target;                                                       \
+    } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.  If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: portable/entry.cpp */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+void dvmInterpretPortable(Thread* self)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+#endif
+    DvmDex* methodClassDex;     // curMethod->clazz->pDvmDex
+    JValue retval;
+
+    /* core state */
+    const Method* curMethod;    // method we're interpreting
+    const u2* pc;               // program counter
+    u4* fp;                     // frame pointer
+    u2 inst;                    // current instruction
+    /* instruction decoding */
+    u4 ref;                     // 16 or 32-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+
+    /* copy state in */
+    curMethod = self->interpSave.method;
+    pc = self->interpSave.pc;
+    fp = self->interpSave.curFrame;
+    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: %s.%s pc=%#x fp=%p",
+        self->threadId, curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging.
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, curMethod);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if 0
+    if (self->debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, self->interpSave.curFrame, false);
+    }
+#endif
+
+    FINISH(0);                  /* fetch and execute first instruction */
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.cpp */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+    /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+     * "move-wide v6, v7" and "move-wide v7, v6" */
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move-wide/from16 v%d,v%d  (v%d=0x%08llx)", vdst, vsrc1,
+        vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+        kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+    SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+    FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.cpp */
+/* File: c/OP_MOVE.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.cpp */
+/* File: c/OP_MOVE_FROM16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.cpp */
+/* File: c/OP_MOVE_16.cpp */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+    vdst = FETCH(1);
+    vsrc1 = FETCH(2);
+    ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+        (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+        kSpacing, vdst, GET_REGISTER(vsrc1));
+    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+    FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+    SET_REGISTER_WIDE(vdst, retval.j);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.cpp */
+/* File: c/OP_MOVE_RESULT.cpp */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+         (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+         vdst, kSpacing+4, vdst,retval.i);
+    SET_REGISTER(vdst, retval.i);
+    FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.cpp */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+    vdst = INST_AA(inst);
+    ILOGV("|move-exception v%d", vdst);
+    assert(self->exception != NULL);
+    SET_REGISTER(vdst, (u4)self->exception);
+    dvmClearException(self);
+    FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.cpp */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return-wide v%d", vsrc1);
+    retval.j = GET_REGISTER_WIDE(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.cpp */
+/* File: c/OP_RETURN.cpp */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+    vsrc1 = INST_AA(inst);
+    ILOGV("|return%s v%d",
+        (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+    retval.i = GET_REGISTER(vsrc1);
+    GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.cpp */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+    {
+        s4 tmp;
+
+        vdst = INST_A(inst);
+        tmp = (s4) (INST_B(inst) << 28) >> 28;  // sign extend 4-bit value
+        ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.cpp */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER(vdst, (s2) vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.cpp */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER(vdst, tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+    SET_REGISTER(vdst, vsrc1 << 16);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+    SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+    {
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, (s4) tmp);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+    {
+        u8 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u8)FETCH(2) << 16;
+        tmp |= (u8)FETCH(3) << 32;
+        tmp |= (u8)FETCH(4) << 48;
+        ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+        SET_REGISTER_WIDE(vdst, tmp);
+    }
+    FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.cpp */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+    vdst = INST_AA(inst);
+    vsrc1 = FETCH(1);
+    ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+    SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.cpp */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+    {
+        StringObject* strObj;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+        strObj = dvmDexGetResolvedString(methodClassDex, ref);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, ref);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.cpp */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+    {
+        StringObject* strObj;
+        u4 tmp;
+
+        vdst = INST_AA(inst);
+        tmp = FETCH(1);
+        tmp |= (u4)FETCH(2) << 16;
+        ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+        strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+        if (strObj == NULL) {
+            EXPORT_PC();
+            strObj = dvmResolveString(curMethod->clazz, tmp);
+            if (strObj == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) strObj);
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.cpp */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            EXPORT_PC();
+            clazz = dvmResolveClass(curMethod->clazz, ref, true);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+        SET_REGISTER(vdst, (u4) clazz);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.cpp */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+    {
+        Object* obj;
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-enter v%d %s(0x%08x)",
+            vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+        ILOGV("+ locking %p %s", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC */
+        dvmLockObject(self, obj);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.cpp */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+    {
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|monitor-exit v%d %s(0x%08x)",
+            vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /*
+             * The exception needs to be processed at the *following*
+             * instruction, not the current instruction (see the Dalvik
+             * spec).  Because we're jumping to an exception handler,
+             * we're not actually at risk of skipping an instruction
+             * by doing so.
+             */
+            ADJUST_PC(1);           /* monitor-exit width is 1 */
+            GOTO_exceptionThrown();
+        }
+        ILOGV("+ unlocking %p %s", obj, obj->clazz->descriptor);
+        if (!dvmUnlockObject(self, obj)) {
+            assert(dvmCheckException(self));
+            ADJUST_PC(1);
+            GOTO_exceptionThrown();
+        }
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.cpp */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                clazz = dvmResolveClass(curMethod->clazz, ref, false);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            if (!dvmInstanceof(obj->clazz, clazz)) {
+                dvmThrowClassCastException(obj->clazz, clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.cpp */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* clazz;
+        Object* obj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);   /* object to check */
+        ref = FETCH(1);         /* class to check against */
+        ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+        obj = (Object*)GET_REGISTER(vsrc1);
+        if (obj == NULL) {
+            SET_REGISTER(vdst, 0);
+        } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+            if (!checkForNullExportPC(obj, fp, pc))
+                GOTO_exceptionThrown();
+#endif
+            clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+            if (clazz == NULL) {
+                EXPORT_PC();
+                clazz = dvmResolveClass(curMethod->clazz, ref, true);
+                if (clazz == NULL)
+                    GOTO_exceptionThrown();
+            }
+            SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.cpp */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+    {
+        ArrayObject* arrayObj;
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        ILOGV("|array-length v%d,v%d  (%p)", vdst, vsrc1, arrayObj);
+        if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+            GOTO_exceptionThrown();
+        /* verifier guarantees this is an array reference */
+        SET_REGISTER(vdst, arrayObj->length);
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.cpp */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+    {
+        ClassObject* clazz;
+        Object* newObj;
+
+        EXPORT_PC();
+
+        vdst = INST_AA(inst);
+        ref = FETCH(1);
+        ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+        clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (clazz == NULL) {
+            clazz = dvmResolveClass(curMethod->clazz, ref, false);
+            if (clazz == NULL)
+                GOTO_exceptionThrown();
+        }
+
+        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+            GOTO_exceptionThrown();
+
+#if defined(WITH_JIT)
+        /*
+         * The JIT needs dvmDexGetResolvedClass() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (!dvmDexGetResolvedClass(methodClassDex, ref))) {
+            /* Class initialization is still ongoing - end the trace */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage(gDvm.exInstantiationError,
+        //        clazz->descriptor);
+        //    GOTO_exceptionThrown();
+        //}
+        newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        if (newObj == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newObj);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        s4 length;
+
+        EXPORT_PC();
+
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);       /* length reg */
+        ref = FETCH(1);
+        ILOGV("|new-array v%d,v%d,class@0x%04x  (%d elements)",
+            vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+        length = (s4) GET_REGISTER(vsrc1);
+        if (length < 0) {
+            dvmThrowNegativeArraySizeException(length);
+            GOTO_exceptionThrown();
+        }
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+        SET_REGISTER(vdst, (u4) newArray);
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+    GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.cpp */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+    GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.cpp */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA)   /*vAA, +BBBBBBBB*/
+    {
+        const u2* arrayData;
+        s4 offset;
+        ArrayObject* arrayObj;
+
+        EXPORT_PC();
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+        arrayData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (arrayData < curMethod->insns ||
+            arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            dvmThrowInternalError("bad fill array data");
+            GOTO_exceptionThrown();
+        }
+#endif
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+            GOTO_exceptionThrown();
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_THROW.cpp */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+    {
+        Object* obj;
+
+        /*
+         * We don't create an exception here, but the process of searching
+         * for a catch block can do class lookups and throw exceptions.
+         * We need to update the saved PC.
+         */
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);
+        ILOGV("|throw v%d  (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+        obj = (Object*) GET_REGISTER(vsrc1);
+        if (!checkForNull(obj)) {
+            /* will throw a null pointer exception */
+            LOGVV("Bad exception");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.cpp */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+    vdst = INST_AA(inst);
+    if ((s1)vdst < 0)
+        ILOGV("|goto -0x%02x", -((s1)vdst));
+    else
+        ILOGV("|goto +0x%02x", ((s1)vdst));
+    ILOGV("> branch taken");
+    if ((s1)vdst < 0)
+        PERIODIC_CHECKS((s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.cpp */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+    {
+        s4 offset = (s2) FETCH(1);          /* sign-extend next code unit */
+
+        if (offset < 0)
+            ILOGV("|goto/16 -0x%04x", -offset);
+        else
+            ILOGV("|goto/16 +0x%04x", offset);
+        ILOGV("> branch taken");
+        if (offset < 0)
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.cpp */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+    {
+        s4 offset = FETCH(1);               /* low-order 16 bits */
+        offset |= ((s4) FETCH(2)) << 16;    /* high-order 16 bits */
+
+        if (offset < 0)
+            ILOGV("|goto/32 -0x%08x", -offset);
+        else
+            ILOGV("|goto/32 +0x%08x", offset);
+        ILOGV("> branch taken");
+        if (offset <= 0)    /* allowed to branch to self */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.cpp */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|packed-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.cpp */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+    {
+        const u2* switchData;
+        u4 testVal;
+        s4 offset;
+
+        vsrc1 = INST_AA(inst);
+        offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+        ILOGV("|sparse-switch v%d +0x%04x", vsrc1, offset);
+        switchData = pc + offset;       // offset in 16-bit units
+#ifndef NDEBUG
+        if (switchData < curMethod->insns ||
+            switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+        {
+            /* should have been caught in verifier */
+            EXPORT_PC();
+            dvmThrowInternalError("bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.cpp */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.cpp */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.cpp */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.cpp */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.cpp */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.cpp */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.cpp */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.cpp */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.cpp */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.cpp */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.cpp */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.cpp */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.cpp */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.cpp */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.cpp */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.cpp */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.cpp */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.cpp */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.cpp */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.cpp */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.cpp */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.cpp */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.cpp */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.cpp */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.cpp */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.cpp */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+    {
+        ArrayObject* arrayObj;
+        Object* obj;
+        u2 arrayInfo;
+        EXPORT_PC();
+        vdst = INST_AA(inst);       /* AA: source value */
+        arrayInfo = FETCH(1);
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */
+        vsrc2 = arrayInfo >> 8;     /* CC: index */
+        ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+        if (!checkForNull((Object*) arrayObj))
+            GOTO_exceptionThrown();
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+            dvmThrowArrayIndexOutOfBoundsException(
+                arrayObj->length, GET_REGISTER(vsrc2));
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {
+                ALOGV("Can't put a '%s'(%p) into array type='%s'(%p)",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->clazz->descriptor, arrayObj);
+                dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);
+                GOTO_exceptionThrown();
+            }
+        }
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+        dvmSetObjectArrayElement(arrayObj,
+                                 GET_REGISTER(vsrc2),
+                                 (Object *)GET_REGISTER(vdst));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.cpp */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.cpp */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.cpp */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.cpp */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.cpp */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.cpp */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.cpp */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.cpp */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.cpp */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.cpp */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.cpp */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible.  In practice, many popular VMs don't
+ * do this because it slows down a very common operation.  It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.cpp */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.cpp */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.cpp */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.cpp */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.cpp */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.cpp */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.cpp */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.cpp */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.cpp */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.cpp */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.cpp */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.cpp */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.cpp */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.cpp */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.cpp */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.cpp */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.cpp */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.cpp */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.cpp */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.cpp */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.cpp */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.cpp */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.cpp */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.cpp */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.cpp */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.cpp */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.cpp */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.cpp */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.cpp */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.cpp */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.cpp */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.cpp */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.cpp */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.cpp */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.cpp */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.cpp */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.cpp */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.cpp */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.cpp */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.cpp */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.cpp */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.cpp */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.cpp */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.cpp */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.cpp */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.cpp */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_FLOAT(vdst,
+            fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.cpp */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+    {
+        u2 srcRegs;
+        vdst = INST_AA(inst);
+        srcRegs = FETCH(1);
+        vsrc1 = srcRegs & 0xff;
+        vsrc2 = srcRegs >> 8;
+        ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+        SET_REGISTER_DOUBLE(vdst,
+            fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.cpp */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.cpp */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.cpp */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.cpp */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.cpp */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_FLOAT(vdst,
+        fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.cpp */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.cpp */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+    vdst = INST_A(inst);
+    vsrc1 = INST_B(inst);
+    ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+    SET_REGISTER_DOUBLE(vdst,
+        fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+    FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.cpp */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+    {
+        vdst = INST_A(inst);
+        vsrc1 = INST_B(inst);
+        vsrc2 = FETCH(1);
+        ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.cpp */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.cpp */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+    {
+        u2 litInfo;
+        vdst = INST_AA(inst);
+        litInfo = FETCH(1);
+        vsrc1 = litInfo & 0xff;
+        vsrc2 = litInfo >> 8;
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+        SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.cpp */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.cpp */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.cpp */
+HANDLE_OPCODE(OP_BREAKPOINT)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpcode = dvmGetOriginalOpcode(pc);
+        ALOGV("+++ break 0x%02x (0x%04x -> 0x%04x)", originalOpcode, inst,
+            INST_REPLACE_OP(inst, originalOpcode));
+        inst = INST_REPLACE_OP(inst, originalOpcode);
+        FINISH_BKPT(originalOpcode);
+    }
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.cpp */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+    EXPORT_PC();
+    vsrc1 = INST_AA(inst);
+    ref = FETCH(1);             /* class/field/method ref */
+    dvmThrowVerificationError(curMethod, vsrc1, ref);
+    GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+    {
+        /*
+         * This has the same form as other method calls, but we ignore
+         * the 5th argument (vA).  This is chiefly because the first four
+         * arguments to a function on ARM are in registers.
+         *
+         * We only set the arguments that are actually used, leaving
+         * the rest uninitialized.  We're assuming that, if the method
+         * needs them, they'll be specified in the call.
+         *
+         * However, this annoys gcc when optimizations are enabled,
+         * causing a "may be used uninitialized" warning.  Quieting
+         * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+         * on empty method).  Note that valgrind is perfectly happy
+         * either way as the uninitialiezd values are never actually
+         * used.
+         */
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_B(inst);       /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* 0-4 register indices */
+        ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+            vsrc1, ref, vdst);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst >> 12);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst & 0x0f);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_IGET_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.cpp */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.cpp */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.cpp */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    ALOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.cpp */
+/*--- end of opcodes ---*/
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    self->interpSave.retval = retval;
+}
+
diff --git a/vm/mterp/out/InterpC-x86.cpp b/vm/mterp/out/InterpC-x86.cpp
new file mode 100644
index 0000000..77dc888
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86.cpp
@@ -0,0 +1,2250 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.cpp */
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h>                   // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines.  These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ *   WITH_INSTR_CHECKS
+ *   WITH_TRACKREF_CHECKS
+ *   EASY_GDB
+ *   NDEBUG
+ */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * Some architectures require 64-bit alignment for access to 64-bit data
+ * types.  We can't just use pointers to copy 64-bit values out of our
+ * interpreted register set, because gcc may assume the pointer target is
+ * aligned and generate invalid code.
+ *
+ * There are two common approaches:
+ *  (1) Use a union that defines a 32-bit pair and a 64-bit value.
+ *  (2) Call memcpy().
+ *
+ * Depending upon what compiler you're using and what options are specified,
+ * one may be faster than the other.  For example, the compiler might
+ * convert a memcpy() of 8 bytes into a series of instructions and omit
+ * the call.  The union version could cause some strange side-effects,
+ * e.g. for a while ARM gcc thought it needed separate storage for each
+ * inlined instance, and generated instructions to zero out ~700 bytes of
+ * stack space at the top of the interpreter.
+ *
+ * The default is to use memcpy().  The current gcc for ARM seems to do
+ * better with the union.
+ */
+#if defined(__ARM_EABI__)
+# define NO_UNALIGN_64__UNION
+#endif
+/*
+ * MIPS ABI requires 64-bit alignment for access to 64-bit data types.
+ *
+ * Use memcpy() to do the transfer
+ */
+#if defined(__mips__)
+/* # define NO_UNALIGN_64__UNION */
+#endif
+
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code.  This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter.  "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do {                                            \
+        int myoff = _offset;        /* deref only once */                   \
+        if (pc + myoff < curMethod->insns ||                                \
+            pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+        {                                                                   \
+            char* desc;                                                     \
+            desc = dexProtoCopyMethodDescriptor(&curMethod->prototype);     \
+            ALOGE("Invalid branch %d at 0x%04x in %s.%s %s",                 \
+                myoff, (int) (pc - curMethod->insns),                       \
+                curMethod->clazz->descriptor, curMethod->name, desc);       \
+            free(desc);                                                     \
+            dvmAbort();                                                     \
+        }                                                                   \
+        pc += myoff;                                                        \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#else
+# define ADJUST_PC(_offset) do {                                            \
+        pc += _offset;                                                      \
+        EXPORT_EXTRA_PC();                                                  \
+    } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do {                                             \
+        char debugStrBuf[128];                                              \
+        snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__);            \
+        if (curMethod != NULL)                                              \
+            ALOG(_level, LOG_TAG"i", "%-2d|%04x%s",                          \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            ALOG(_level, LOG_TAG"i", "%-2d|####%s",                          \
+                self->threadId, debugStrBuf);                               \
+    } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = "            ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.ll;
+#else
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { s8 ll; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.ll = val;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &val, 8);
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.parts[0] = ptr[0];
+    conv.parts[1] = ptr[1];
+    return conv.d;
+#else
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+    union { double d; u4 parts[2]; } conv;
+
+    ptr += idx;
+    conv.d = dval;
+    ptr[0] = conv.parts[0];
+    ptr[1] = conv.parts[1];
+#else
+    memcpy(&ptr[idx], &dval, 8);
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access.  Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putLongToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+# define GET_REGISTER_FLOAT(_idx) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+    ( (_idx) < curMethod->registersSize ? \
+        (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+    ( (_idx) < curMethod->registersSize-1 ? \
+        (void)putDoubleToArray(fp, (_idx), (_val)) : assert(!"bad reg") )
+#else
+# define GET_REGISTER(_idx)                 (fp[(_idx)])
+# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx)       ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx)             ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val)       SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx)          getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val)    putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter.  We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset)     (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst)    ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by the various exception throw routines, so that the
+ * exception stack trace can be generated correctly.  If we don't do this,
+ * the offset within the current method won't be shown correctly.  See the
+ * notes in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC()         (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Check to see if "obj" is NULL.  If so, throw an exception.  Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/*
+ * Check to see if "obj" is NULL.  If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+    if (obj == NULL) {
+        EXPORT_PC();
+        dvmThrowNullPointerException(NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsHeapAddress(obj)) {
+        ALOGE("Invalid object %p", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        ALOGE("Invalid object class %p (in %p)", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.cpp */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    extern "C" void dvmMterp_##_target(Thread* self, ## __VA_ARGS__);
+
+/* (void)xxx to quiet unused variable compiler warnings. */
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(Thread* self, ## __VA_ARGS__) {                 \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;                                       \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;        \
+        (void)methodToCall; (void)debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into Thread struct
+ * references.  (These are undefined down in "footer.cpp".)
+ */
+#define retval                  self->interpSave.retval
+#define pc                      self->interpSave.pc
+#define fp                      self->interpSave.curFrame
+#define curMethod               self->interpSave.method
+#define methodClassDex          self->interpSave.methodClassDex
+#define debugTrackedRefStart    self->interpSave.debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+#if defined(WITH_JIT)
+#define JIT_STUB_HACK(x) x
+#else
+#define JIT_STUB_HACK(x)
+#endif
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()
+#define PC_TO_SELF()
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "self" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ * (void)xxx to quiet unused variable compiler warnings.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    extern "C" void dvmMterp_##_op(Thread* self);                           \
+    void dvmMterp_##_op(Thread* self) {                                     \
+        u4 ref;                                                             \
+        u2 vsrc1, vsrc2, vdst;                                              \
+        u2 inst = FETCH(0);                                                 \
+        (void)ref; (void)vsrc1; (void)vsrc2; (void)vdst; (void)inst;
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.  Further, debugger/profiler checks are handled
+ * before handler execution in mterp, so we don't do them here either.
+ */
+#if defined(WITH_JIT)
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {        \
+            dvmCheckJit(pc, self);                                          \
+        }                                                                   \
+        return;                                                             \
+    }
+#else
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        return;                                                             \
+    }
+#endif
+
+#define FINISH_BKPT(_opcode)       /* FIXME? */
+#define DISPATCH_EXTENDED(_opcode) /* FIXME? */
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements.  Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown()                                              \
+    do {                                                                    \
+        dvmMterp_exceptionThrown(self);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(self);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(self, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(self, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(self)
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.cpp */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+    u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor.  These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype)                \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_totype(vdst,                                         \
+            GET_REGISTER##_fromtype(vsrc1));                                \
+        FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype,       \
+        _tovtype, _tortype)                                                 \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+    {                                                                       \
+        /* spec defines specific handling for +/- inf and NaN values */     \
+        _fromvtype val;                                                     \
+        _tovtype intMin, intMax, result;                                    \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        val = GET_REGISTER##_fromrtype(vsrc1);                              \
+        intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1);                 \
+        intMax = ~intMin;                                                   \
+        result = (_tovtype) val;                                            \
+        if (val >= intMax)          /* +inf */                              \
+            result = intMax;                                                \
+        else if (val <= intMin)     /* -inf */                              \
+            result = intMin;                                                \
+        else if (val != val)        /* NaN */                               \
+            result = 0;                                                     \
+        else                                                                \
+            result = (_tovtype) val;                                        \
+        SET_REGISTER##_tortype(vdst, result);                               \
+    }                                                                       \
+    FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type)                        \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1);                \
+        SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1));                    \
+        FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal)          \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        int result;                                                         \
+        u2 regs;                                                            \
+        _varType val1, val2;                                                \
+        vdst = INST_AA(inst);                                               \
+        regs = FETCH(1);                                                    \
+        vsrc1 = regs & 0xff;                                                \
+        vsrc2 = regs >> 8;                                                  \
+        ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);         \
+        val1 = GET_REGISTER##_type(vsrc1);                                  \
+        val2 = GET_REGISTER##_type(vsrc2);                                  \
+        if (val1 == val2)                                                   \
+            result = 0;                                                     \
+        else if (val1 < val2)                                               \
+            result = -1;                                                    \
+        else if (val1 > val2)                                               \
+            result = 1;                                                     \
+        else                                                                \
+            result = (_nanVal);                                             \
+        ILOGV("+ result=%d", result);                                       \
+        SET_REGISTER(vdst, result);                                         \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp)                             \
+    HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/)                                \
+        vsrc1 = INST_A(inst);                                               \
+        vsrc2 = INST_B(inst);                                               \
+        if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) {       \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2,        \
+                branchOffset);                                              \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2);             \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp)                            \
+    HANDLE_OPCODE(_opcode /*vAA, +BBBB*/)                                   \
+        vsrc1 = INST_AA(inst);                                              \
+        if ((s4) GET_REGISTER(vsrc1) _cmp 0) {                              \
+            int branchOffset = (s2)FETCH(1);    /* sign-extended */         \
+            ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset);    \
+            ILOGV("> branch taken");                                        \
+            if (branchOffset < 0)                                           \
+                PERIODIC_CHECKS(branchOffset);                              \
+            FINISH(branchOffset);                                           \
+        } else {                                                            \
+            ILOGV("|if-%s v%d,-", (_opname), vsrc1);                        \
+            FINISH(2);                                                      \
+        }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type)                    \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1);                       \
+        SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx);    \
+        FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            secondVal = GET_REGISTER(vsrc2);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2));     \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op)                     \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1);                   \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f));    \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/)                               \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        vsrc2 = FETCH(1);                                                   \
+        ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x",                             \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s2) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) {         \
+                /* won't generate /lit16 instr for this; check anyway */    \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op (s2) vsrc2;                           \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            /* non-div/rem case */                                          \
+            SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2);         \
+        }                                                                   \
+        FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, result;                                            \
+            firstVal = GET_REGISTER(vsrc1);                                 \
+            if ((s1) vsrc2 == 0) {                                          \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) {         \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op ((s1) vsrc2);                         \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2);                   \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op)                \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/)                               \
+    {                                                                       \
+        u2 litInfo;                                                         \
+        vdst = INST_AA(inst);                                               \
+        litInfo = FETCH(1);                                                 \
+        vsrc1 = litInfo & 0xff;                                             \
+        vsrc2 = litInfo >> 8;       /* constant */                          \
+        ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x",                              \
+            (_opname), vdst, vsrc1, vsrc2);                                 \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f));                  \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        if (_chkdiv != 0) {                                                 \
+            s4 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER(vdst);                                  \
+            secondVal = GET_REGISTER(vsrc1);                                \
+            if (secondVal == 0) {                                           \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u4)firstVal == 0x80000000 && secondVal == -1) {            \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER(vdst, result);                                     \
+        } else {                                                            \
+            SET_REGISTER(vdst,                                              \
+                (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1));      \
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op)               \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1);             \
+        SET_REGISTER(vdst,                                                  \
+            _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f));     \
+        FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vsrc1);                            \
+            secondVal = GET_REGISTER_WIDE(vsrc2);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+        }                                                                   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op)                    \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);       \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        if (_chkdiv != 0) {                                                 \
+            s8 firstVal, secondVal, result;                                 \
+            firstVal = GET_REGISTER_WIDE(vdst);                             \
+            secondVal = GET_REGISTER_WIDE(vsrc1);                           \
+            if (secondVal == 0LL) {                                         \
+                EXPORT_PC();                                                \
+                dvmThrowArithmeticException("divide by zero");              \
+                GOTO_exceptionThrown();                                     \
+            }                                                               \
+            if ((u8)firstVal == 0x8000000000000000ULL &&                    \
+                secondVal == -1LL)                                          \
+            {                                                               \
+                if (_chkdiv == 1)                                           \
+                    result = firstVal;  /* division */                      \
+                else                                                        \
+                    result = 0;         /* remainder */                     \
+            } else {                                                        \
+                result = firstVal _op secondVal;                            \
+            }                                                               \
+            SET_REGISTER_WIDE(vdst, result);                                \
+        } else {                                                            \
+            SET_REGISTER_WIDE(vdst,                                         \
+                (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+        }                                                                   \
+        FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op)              \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1);            \
+        SET_REGISTER_WIDE(vdst,                                             \
+            _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+        FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op)                            \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);      \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2));       \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op)                           \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        u2 srcRegs;                                                         \
+        vdst = INST_AA(inst);                                               \
+        srcRegs = FETCH(1);                                                 \
+        vsrc1 = srcRegs & 0xff;                                             \
+        vsrc2 = srcRegs >> 8;                                               \
+        ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);     \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2));     \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op)                      \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1);           \
+        SET_REGISTER_FLOAT(vdst,                                            \
+            GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1));        \
+        FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op)                     \
+    HANDLE_OPCODE(_opcode /*vA, vB*/)                                       \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);                                               \
+        ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1);          \
+        SET_REGISTER_DOUBLE(vdst,                                           \
+            GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1));      \
+        FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);                                               \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;    /* array ptr */                        \
+        vsrc2 = arrayInfo >> 8;      /* index */                            \
+        ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)]);      \
+        ILOGV("+ AGET[%d]=%#x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));   \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/)                                \
+    {                                                                       \
+        ArrayObject* arrayObj;                                              \
+        u2 arrayInfo;                                                       \
+        EXPORT_PC();                                                        \
+        vdst = INST_AA(inst);       /* AA: source value */                  \
+        arrayInfo = FETCH(1);                                               \
+        vsrc1 = arrayInfo & 0xff;   /* BB: array ptr */                     \
+        vsrc2 = arrayInfo >> 8;     /* CC: index */                         \
+        ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2);        \
+        arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);                      \
+        if (!checkForNull((Object*) arrayObj))                              \
+            GOTO_exceptionThrown();                                         \
+        if (GET_REGISTER(vsrc2) >= arrayObj->length) {                      \
+            dvmThrowArrayIndexOutOfBoundsException(                         \
+                arrayObj->length, GET_REGISTER(vsrc2));                     \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*)(void*)arrayObj->contents)[GET_REGISTER(vsrc2)] =          \
+            GET_REGISTER##_regsize(vdst);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits.  Consider:
+ *   short foo = -1  (sets a 32-bit register to 0xffffffff)
+ *   iput-quick foo  (writes all 32 bits to the field)
+ *   short bar = 1   (sets a 32-bit register to 0x00000001)
+ *   iput-short      (writes the low 16 bits to the field)
+ *   iget-quick foo  (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field.  This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time.  On
+ * a device with a 16-bit data bus this is sub-optimal.  (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            dvmGetField##_ftype(obj, ifield->byteOffset));                  \
+        ILOGV("+ IGET '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iget%s-quick v%d,v%d,field@+%u",                            \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref));        \
+        ILOGV("+ IGETQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        InstField* ifield;                                                  \
+        Object* obj;                                                        \
+        EXPORT_PC();                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNull(obj))                                             \
+            GOTO_exceptionThrown();                                         \
+        ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref);  \
+        if (ifield == NULL) {                                               \
+            ifield = dvmResolveInstField(curMethod->clazz, ref);            \
+            if (ifield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+        }                                                                   \
+        dvmSetField##_ftype(obj, ifield->byteOffset,                        \
+            GET_REGISTER##_regsize(vdst));                                  \
+        ILOGV("+ IPUT '%s'=0x%08llx", ifield->name,                         \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize)             \
+    HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/)                           \
+    {                                                                       \
+        Object* obj;                                                        \
+        vdst = INST_A(inst);                                                \
+        vsrc1 = INST_B(inst);   /* object ptr */                            \
+        ref = FETCH(1);         /* field offset */                          \
+        ILOGV("|iput%s-quick v%d,v%d,field@0x%04x",                         \
+            (_opname), vdst, vsrc1, ref);                                   \
+        obj = (Object*) GET_REGISTER(vsrc1);                                \
+        if (!checkForNullExportPC(obj, fp, pc))                             \
+            GOTO_exceptionThrown();                                         \
+        dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst));        \
+        ILOGV("+ IPUTQ %d=0x%08llx", ref,                                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+    }                                                                       \
+    FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Because the portable interpreter is not involved with the JIT
+ * and trace building, we only need the extra check here when this
+ * code is massaged into a stub called from an assembly interpreter.
+ * This is controlled by the JIT_STUB_HACK maco.
+ */
+
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize)                   \
+    HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/)                              \
+    {                                                                       \
+        StaticField* sfield;                                                \
+        vdst = INST_AA(inst);                                               \
+        ref = FETCH(1);         /* field ref */                             \
+        ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref);           \
+        sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+        if (sfield == NULL) {                                               \
+            EXPORT_PC();                                                    \
+            sfield = dvmResolveStaticField(curMethod->clazz, ref);          \
+            if (sfield == NULL)                                             \
+                GOTO_exceptionThrown();                                     \
+            if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) {      \
+                JIT_STUB_HACK(dvmJitEndTraceSelect(self,pc));               \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->name, (u8)GET_REGISTER##_regsize(vdst));                \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_IGET_WIDE_VOLATILE.cpp */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.cpp */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.cpp */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.cpp */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.cpp */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebugProfile) {
+            if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        } else {
+            if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+                GOTO_exceptionThrown();
+        }
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_OBJECT_INIT_RANGE.cpp */
+HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    {
+        Object* obj;
+
+        vsrc1 = FETCH(2);               /* reg number of "this" pointer */
+        obj = GET_REGISTER_AS_OBJECT(vsrc1);
+
+        if (!checkForNullExportPC(obj, fp, pc))
+            GOTO_exceptionThrown();
+
+        /*
+         * The object should be marked "finalizable" when Object.<init>
+         * completes normally.  We're going to assume it does complete
+         * (by virtue of being nothing but a return-void) and set it now.
+         */
+        if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) {
+            EXPORT_PC();
+            dvmSetFinalizable(obj);
+            if (dvmGetException(self))
+                GOTO_exceptionThrown();
+        }
+
+        if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
+            /* behave like OP_INVOKE_DIRECT_RANGE */
+            GOTO_invoke(invokeDirect, true);
+        }
+        FINISH(3);
+    }
+OP_END
+
+/* File: c/OP_RETURN_VOID_BARRIER.cpp */
+HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;   /* placate valgrind */
+#endif
+    ANDROID_MEMBAR_STORE();
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/gotoTargets.cpp */
+/*
+ * C footer.  This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target".  In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction.  Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange, bool)
+    {
+        ClassObject* arrayClass;
+        ArrayObject* newArray;
+        u4* contents;
+        char typeCh;
+        int i;
+        u4 arg5;
+
+        EXPORT_PC();
+
+        ref = FETCH(1);             /* class ref */
+        vdst = FETCH(2);            /* first 4 regs -or- range base */
+
+        if (methodCallRange) {
+            vsrc1 = INST_AA(inst);  /* #of elements */
+            arg5 = -1;              /* silence compiler warning */
+            ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+        } else {
+            arg5 = INST_A(inst);
+            vsrc1 = INST_B(inst);   /* #of elements */
+            ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+               vsrc1, ref, vdst, arg5);
+        }
+
+        /*
+         * Resolve the array class.
+         */
+        arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+        if (arrayClass == NULL) {
+            arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+            if (arrayClass == NULL)
+                GOTO_exceptionThrown();
+        }
+        /*
+        if (!dvmIsArrayClass(arrayClass)) {
+            dvmThrowRuntimeException(
+                "filled-new-array needs array class");
+            GOTO_exceptionThrown();
+        }
+        */
+        /* verifier guarantees this is an array class */
+        assert(dvmIsArrayClass(arrayClass));
+        assert(dvmIsClassInitialized(arrayClass));
+
+        /*
+         * Create an array of the specified type.
+         */
+        LOGVV("+++ filled-new-array type is '%s'", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowRuntimeException("bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            ALOGE("non-int primitives not implemented");
+            dvmThrowInternalError(
+                "filled-new-array not implemented for anything but 'int'");
+            GOTO_exceptionThrown();
+        }
+
+        newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+        if (newArray == NULL)
+            GOTO_exceptionThrown();
+
+        /*
+         * Fill in the elements.  It's legal for vsrc1 to be zero.
+         */
+        contents = (u4*)(void*)newArray->contents;
+        if (methodCallRange) {
+            for (i = 0; i < vsrc1; i++)
+                contents[i] = GET_REGISTER(vdst+i);
+        } else {
+            assert(vsrc1 <= 5);
+            if (vsrc1 == 5) {
+                contents[4] = GET_REGISTER(arg5);
+                vsrc1--;
+            }
+            for (i = 0; i < vsrc1; i++) {
+                contents[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+        }
+        if (typeCh == 'L' || typeCh == '[') {
+            dvmWriteBarrierArray(newArray, 0, newArray->length);
+        }
+
+        retval.l = (Object*)newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange, bool)
+    {
+        Method* baseMethod;
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->methodToCall = methodToCall;
+        self->callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            /*
+             * This can happen if you create two classes, Base and Sub, where
+             * Sub is a sub-class of Base.  Declare a protected abstract
+             * method foo() in Base, and invoke foo() from a method in Base.
+             * Base is an "abstract base class" and is never instantiated
+             * directly.  Now, Override foo() in Sub, and use Sub.  This
+             * Works fine unless Sub stops providing an implementation of
+             * the method.
+             */
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            ALOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s",
+                baseMethod->clazz->descriptor, baseMethod->name,
+                (u4) baseMethod->methodIndex,
+                methodToCall->clazz->descriptor, methodToCall->name);
+            //dvmDumpClass(baseMethod->clazz);
+            //dvmDumpClass(methodToCall->clazz);
+            dvmDumpAllClasses(0);
+        }
+#endif
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+    {
+        Method* baseMethod;
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        /*
+         * Resolve the method.  This is the correct method for the static
+         * type of the object.  We also verify access permissions here.
+         * The first arg to dvmResolveMethod() is just the referring class
+         * (used for class loaders and such), so we don't want to pass
+         * the superclass into the resolution call.
+         */
+        baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (baseMethod == NULL) {
+            baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+            if (baseMethod == NULL) {
+                ILOGV("+ unknown method or access denied");
+                GOTO_exceptionThrown();
+            }
+        }
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in that class' superclass.
+         */
+        if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+            /*
+             * Method does not exist in the superclass.  Could happen if
+             * superclass gets updated.
+             */
+            dvmThrowNoSuchMethodError(baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+    {
+        Object* thisPtr;
+        ClassObject* thisClass;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+        thisClass = thisPtr->clazz;
+
+        /*
+         * Given a class and a method index, find the Method* with the
+         * actual code we want to execute.
+         */
+        methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+                        methodClassDex);
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisClass;
+        self->methodToCall = methodToCall;
+#endif
+        if (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+        methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+        if (methodToCall == NULL) {
+            methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+                            METHOD_DIRECT);
+            if (methodToCall == NULL) {
+                ILOGV("+ unknown direct method");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    EXPORT_PC();
+
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    if (methodCallRange)
+        ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+    else
+        ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+            vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+    methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+    if (methodToCall == NULL) {
+        methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+        if (methodToCall == NULL) {
+            ILOGV("+ unknown method");
+            GOTO_exceptionThrown();
+        }
+
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Include the check if this code is being used as a stub
+         * called from the assembly interpreter.
+         */
+        if ((self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) &&
+            (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL)) {
+            /* Class initialization is still ongoing */
+            dvmJitEndTraceSelect(self,pc);
+        }
+#endif
+    }
+    GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+    {
+        Object* thisPtr;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        /*
+         * The object against which we are executing a method is always
+         * in the first argument.
+         */
+        if (methodCallRange) {
+            assert(vsrc1 > 0);
+            ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisPtr = (Object*) GET_REGISTER(vdst);
+        } else {
+            assert((vsrc1>>4) > 0);
+            ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+        }
+
+        if (!checkForNull(thisPtr))
+            GOTO_exceptionThrown();
+
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < (unsigned int) thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        self->callsiteClass = thisPtr->clazz;
+        self->methodToCall = methodToCall;
+#endif
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* vtable index */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        if (methodCallRange) {
+            ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+                vsrc1, ref, vdst, vdst+vsrc1-1);
+            thisReg = vdst;
+        } else {
+            ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+                vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+            thisReg = vdst & 0x0f;
+        }
+        /* impossible in well-formed code, but we must check nevertheless */
+        if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+            GOTO_exceptionThrown();
+
+#if 0   /* impossible in optimized + verified code */
+        if (ref >= curMethod->clazz->super->vtableCount) {
+            dvmThrowNoSuchMethodError(NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < (unsigned int) curMethod->clazz->super->vtableCount);
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method's class.
+         *
+         * We're using the current method's class' superclass, not the
+         * superclass of "this".  This is because we might be executing
+         * in a method inherited from a superclass, and we want to run
+         * in the method's class' superclass.
+         */
+        methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowAbstractMethodError("abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s",
+            ref, methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * General handling for return-void, return, and return-wide.  Put the
+     * return value in "retval" before jumping here.
+     */
+GOTO_TARGET(returnFromMethod)
+    {
+        StackSaveArea* saveArea;
+
+        /*
+         * We must do this BEFORE we pop the previous stack frame off, so
+         * that the GC can see the return value (if any) in the local vars.
+         *
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(0);
+
+        ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+            retval.j, curMethod->clazz->descriptor, curMethod->name,
+            curMethod->shorty);
+        //DUMP_REGS(curMethod, fp);
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+        debugSaveArea = saveArea;
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = (u4*)saveArea->prevFrame;
+        assert(fp != NULL);
+
+        /* Handle any special subMode requirements */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportReturn(self);
+        }
+
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame");
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->interpSave.curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = saveArea->savedPc;
+        ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+
+        /* use FINISH on the caller's invoke instruction */
+        //u2 invokeInstr = INST_INST(FETCH(0));
+        if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+            invokeInstr <= OP_INVOKE_INTERFACE*/)
+        {
+            FINISH(3);
+        } else {
+            //ALOGE("Unknown invoke instr %02x at %d",
+            //    invokeInstr, (int) (pc - curMethod->insns));
+            assert(false);
+        }
+    }
+GOTO_TARGET_END
+
+
+    /*
+     * Jump here when the code throws an exception.
+     *
+     * By the time we get here, the Throwable has been created and the stack
+     * trace has been saved off.
+     */
+GOTO_TARGET(exceptionThrown)
+    {
+        Object* exception;
+        int catchRelPc;
+
+        PERIODIC_CHECKS(0);
+
+        /*
+         * We save off the exception and clear the exception status.  While
+         * processing the exception we might need to load some Throwable
+         * classes, and we don't want class loader exceptions to get
+         * confused with this one.
+         */
+        assert(dvmCheckException(self));
+        exception = dvmGetException(self);
+        dvmAddTrackedAlloc(exception, self);
+        dvmClearException(self);
+
+        ALOGV("Handling exception %s at %s:%d",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+        /*
+         * Report the exception throw to any "subMode" watchers.
+         *
+         * TODO: if the exception was thrown by interpreted code, control
+         * fell through native, and then back to us, we will report the
+         * exception at the point of the throw and again here.  We can avoid
+         * this by not reporting exceptions when we jump here directly from
+         * the native call code above, but then we won't report exceptions
+         * that were thrown *from* the JNI code (as opposed to *through* it).
+         *
+         * The correct solution is probably to ignore from-native exceptions
+         * here, and have the JNI exception code do the reporting to the
+         * debugger.
+         */
+        if (self->interpBreak.ctl.subMode != 0) {
+            PC_FP_TO_SELF();
+            dvmReportExceptionThrow(self, exception);
+        }
+
+        /*
+         * We need to unroll to the catch block or the nearest "break"
+         * frame.
+         *
+         * A break frame could indicate that we have reached an intermediate
+         * native call, or have gone off the top of the stack and the thread
+         * needs to exit.  Either way, we return from here, leaving the
+         * exception raised.
+         *
+         * If we do find a catch block, we want to transfer execution to
+         * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
+         */
+        catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                    exception, false, (void**)(void*)&fp);
+
+        /*
+         * Restore the stack bounds after an overflow.  This isn't going to
+         * be correct in all circumstances, e.g. if JNI code devours the
+         * exception this won't happen until some other exception gets
+         * thrown.  If the code keeps pushing the stack bounds we'll end
+         * up aborting the VM.
+         *
+         * Note we want to do this *after* the call to dvmFindCatchBlock,
+         * because that may need extra stack space to resolve exception
+         * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
+         */
+        if (self->stackOverflowed)
+            dvmCleanupStackOverflow(self, exception);
+
+        if (catchRelPc < 0) {
+            /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("Exception %s from %s:%d not caught locally",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+            dvmSetException(self, exception);
+            dvmReleaseTrackedAlloc(exception, self);
+            GOTO_bail();
+        }
+
+#if DVM_SHOW_EXCEPTION >= 3
+        {
+            const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+            ALOGD("Exception %s thrown from %s:%d to %s:%d",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->interpSave.curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->interpSave.curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        self->interpSave.method = curMethod;
+        //methodClass = curMethod->clazz;
+        methodClassDex = curMethod->clazz->pDvmDex;
+        pc = curMethod->insns + catchRelPc;
+        ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+            curMethod->name, curMethod->shorty);
+        DUMP_REGS(curMethod, fp, false);            // show all regs
+
+        /*
+         * Restore the exception if the handler wants it.
+         *
+         * The Dalvik spec mandates that, if an exception handler wants to
+         * do something with the exception, the first instruction executed
+         * must be "move-exception".  We can pass the exception along
+         * through the thread struct, and let the move-exception instruction
+         * clear it for us.
+         *
+         * If the handler doesn't call move-exception, we don't want to
+         * finish here with an exception still pending.
+         */
+        if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+            dvmSetException(self, exception);
+
+        dvmReleaseTrackedAlloc(exception, self);
+        FINISH(0);
+    }
+GOTO_TARGET_END
+
+
+
+    /*
+     * General handling for invoke-{virtual,super,direct,static,interface},
+     * including "quick" variants.
+     *
+     * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+     * depending on whether this is a "/range" instruction.
+     *
+     * For a range call:
+     *  "vsrc1" holds the argument count (8 bits)
+     *  "vdst" holds the first argument in the range
+     * For a non-range call:
+     *  "vsrc1" holds the argument count (4 bits) and the 5th argument index
+     *  "vdst" holds four 4-bit register indices
+     *
+     * The caller must EXPORT_PC before jumping here, because any method
+     * call can throw a stack overflow exception.
+     */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+    u2 count, u2 regs)
+    {
+        STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+        //printf("range=%d call=%p count=%d regs=0x%04x\n",
+        //    methodCallRange, methodToCall, count, regs);
+        //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+        //    methodToCall->name, methodToCall->shorty);
+
+        u4* outs;
+        int i;
+
+        /*
+         * Copy args.  This may corrupt vsrc1/vdst.
+         */
+        if (methodCallRange) {
+            // could use memcpy or a "Duff's device"; most functions have
+            // so few args it won't matter much
+            assert(vsrc1 <= curMethod->outsSize);
+            assert(vsrc1 == methodToCall->insSize);
+            outs = OUTS_FROM_FP(fp, vsrc1);
+            for (i = 0; i < vsrc1; i++)
+                outs[i] = GET_REGISTER(vdst+i);
+        } else {
+            u4 count = vsrc1 >> 4;
+
+            assert(count <= curMethod->outsSize);
+            assert(count == methodToCall->insSize);
+            assert(count <= 5);
+
+            outs = OUTS_FROM_FP(fp, count);
+#if 0
+            if (count == 5) {
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+                count--;
+            }
+            for (i = 0; i < (int) count; i++) {
+                outs[i] = GET_REGISTER(vdst & 0x0f);
+                vdst >>= 4;
+            }
+#else
+            // This version executes fewer instructions but is larger
+            // overall.  Seems to be a teensy bit faster.
+            assert((vdst >> 16) == 0);  // 16 bits -or- high 16 bits clear
+            switch (count) {
+            case 5:
+                outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+            case 4:
+                outs[3] = GET_REGISTER(vdst >> 12);
+            case 3:
+                outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+            case 2:
+                outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+            case 1:
+                outs[0] = GET_REGISTER(vdst & 0x0f);
+            default:
+                ;
+            }
+#endif
+        }
+    }
+
+    /*
+     * (This was originally a "goto" target; I've kept it separate from the
+     * stuff above in case we want to refactor things again.)
+     *
+     * At this point, we have the arguments stored in the "outs" area of
+     * the current method's stack frame, and the method to call in
+     * "methodToCall".  Push a new stack frame.
+     */
+    {
+        StackSaveArea* newSaveArea;
+        u4* newFp;
+
+        ILOGV("> %s%s.%s %s",
+            dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+            methodToCall->clazz->descriptor, methodToCall->name,
+            methodToCall->shorty);
+
+        newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+        newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+        /* verify that we have enough space */
+        if (true) {
+            u1* bottom;
+            bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+            if (bottom < self->interpStackEnd) {
+                /* stack overflow */
+                ALOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //ALOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p",
+            //    fp, newFp, newSaveArea, bottom);
+        }
+
+#ifdef LOG_INSTR
+        if (methodToCall->registersSize > methodToCall->insSize) {
+            /*
+             * This makes valgrind quiet when we print registers that
+             * haven't been initialized.  Turn it off when the debug
+             * messages are disabled -- we want valgrind to report any
+             * used-before-initialized issues.
+             */
+            memset(newFp, 0xcc,
+                (methodToCall->registersSize - methodToCall->insSize) * 4);
+        }
+#endif
+
+#ifdef EASY_GDB
+        newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+        newSaveArea->prevFrame = fp;
+        newSaveArea->savedPc = pc;
+#if defined(WITH_JIT) && defined(MTERP_STUB)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (self->interpBreak.ctl.subMode != 0) {
+            /*
+             * We mark ENTER here for both native and non-native
+             * calls.  For native calls, we'll mark EXIT on return.
+             * For non-native calls, EXIT is marked in the RETURN op.
+             */
+            PC_TO_SELF();
+            dvmReportInvoke(self, methodToCall);
+        }
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            self->interpSave.method = curMethod;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = newFp;
+            self->interpSave.curFrame = fp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+            self->debugIsMethodEntry = true;        // profiling, debugging
+            ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+                curMethod->name, curMethod->shorty);
+            DUMP_REGS(curMethod, fp, true);         // show input args
+            FINISH(0);                              // jump to method start
+        } else {
+            /* set this up for JNI locals, even if not a JNI native */
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+
+            self->interpSave.curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                  methodToCall->name, methodToCall->shorty);
+
+            /*
+             * Jump through native call bridge.  Because we leave no
+             * space for locals on native calls, "newFp" points directly
+             * to the method arguments.
+             */
+            (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+            if (self->interpBreak.ctl.subMode != 0) {
+                dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
+            }
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->interpSave.curFrame = newSaveArea->prevFrame;
+            fp = newSaveArea->prevFrame;
+
+            /*
+             * If the native code threw an exception, or interpreted code
+             * invoked by the native call threw one and nobody has cleared
+             * it, jump to our local exception handling.
+             */
+            if (dvmCheckException(self)) {
+                ALOGV("Exception thrown by/below native code");
+                GOTO_exceptionThrown();
+            }
+
+            ILOGD("> retval=0x%llx (leaving native)", retval.j);
+            ILOGD("> (return from native %s.%s to %s.%s %s)",
+                methodToCall->clazz->descriptor, methodToCall->name,
+                curMethod->clazz->descriptor, curMethod->name,
+                curMethod->shorty);
+
+            //u2 invokeInstr = INST_INST(FETCH(0));
+            if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+                invokeInstr <= OP_INVOKE_INTERFACE*/)
+            {
+                FINISH(3);
+            } else {
+                //ALOGE("Unknown invoke instr %02x at %d",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.cpp */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/portable/enddefs.cpp b/vm/mterp/portable/enddefs.cpp
new file mode 100644
index 0000000..ef28e2f
--- /dev/null
+++ b/vm/mterp/portable/enddefs.cpp
@@ -0,0 +1,7 @@
+/*--- end of opcodes ---*/
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    self->interpSave.retval = retval;
+}
diff --git a/vm/mterp/portable/entry.cpp b/vm/mterp/portable/entry.cpp
new file mode 100644
index 0000000..f8c01eb
--- /dev/null
+++ b/vm/mterp/portable/entry.cpp
@@ -0,0 +1,63 @@
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+void dvmInterpretPortable(Thread* self)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
+#endif
+    DvmDex* methodClassDex;     // curMethod->clazz->pDvmDex
+    JValue retval;
+
+    /* core state */
+    const Method* curMethod;    // method we're interpreting
+    const u2* pc;               // program counter
+    u4* fp;                     // frame pointer
+    u2 inst;                    // current instruction
+    /* instruction decoding */
+    u4 ref;                     // 16 or 32-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+
+    /* copy state in */
+    curMethod = self->interpSave.method;
+    pc = self->interpSave.pc;
+    fp = self->interpSave.curFrame;
+    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: %s.%s pc=%#x fp=%p",
+        self->threadId, curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+
+    /*
+     * Handle any ongoing profiling and prep for debugging.
+     */
+    if (self->interpBreak.ctl.subMode != 0) {
+        TRACE_METHOD_ENTER(self, curMethod);
+        self->debugIsMethodEntry = true;   // Always true on startup
+    }
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if 0
+    if (self->debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, self->interpSave.curFrame, false);
+    }
+#endif
+
+    FINISH(0);                  /* fetch and execute first instruction */
+
+/*--- start of opcodes ---*/
diff --git a/vm/mterp/portable/stubdefs.cpp b/vm/mterp/portable/stubdefs.cpp
new file mode 100644
index 0000000..015057e
--- /dev/null
+++ b/vm/mterp/portable/stubdefs.cpp
@@ -0,0 +1,82 @@
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+#define JIT_STUB_HACK(x)
+
+/*
+ * InterpSave's pc and fp must be valid when breaking out to a
+ * "Reportxxx" routine.  Because the portable interpreter uses local
+ * variables for these, we must flush prior.  Stubs, however, use
+ * the interpSave vars directly, so this is a nop for stubs.
+ */
+#define PC_FP_TO_SELF()                                                    \
+    self->interpSave.pc = pc;                                              \
+    self->interpSave.curFrame = fp;
+#define PC_TO_SELF() self->interpSave.pc = pc;
+
+/*
+ * Instruction framing.  For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ */
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        if (self->interpBreak.ctl.subMode) {                                \
+            dvmCheckBefore(pc, fp, self);                                   \
+        }                                                                   \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+
+#define OP_END
+
+/*
+ * The "goto" targets just turn into goto statements.  The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        methodCallRange = _methodCallRange;                                 \
+        goto _target;                                                       \
+    } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started.  If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+    }
diff --git a/vm/mterp/rebuild.sh b/vm/mterp/rebuild.sh
new file mode 100755
index 0000000..80419a5
--- /dev/null
+++ b/vm/mterp/rebuild.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 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.
+
+#
+# Rebuild for all known targets.  Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+
+for arch in portable allstubs armv5te armv5te-vfp armv7-a armv7-a-neon mips x86; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+
+# These aren't actually used, so just go ahead and remove them.  The correct
+# approach is to prevent them from being generated in the first place, but
+# this will do for now.
+echo Removing unneeded assembly source for portable interpreter
+rm -f out/InterpAsm-portable.S
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE.S b/vm/mterp/x86/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..27a47c1
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    movq     (rFP, %eax, 4), %xmm0      # %xmm0<- vBB
+    movq     (rFP, %ecx, 4), %xmm1      # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    addsd    %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq     %xmm0, (rFP, rINST, 4)     # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
+
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d917578
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    addsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_ADD_FLOAT.S b/vm/mterp/x86/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..61a6f68
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..fd61457
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_INT.S b/vm/mterp/x86/OP_ADD_INT.S
new file mode 100644
index 0000000..b95a2db
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"addl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_2ADDR.S b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..5d4681b
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"addl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT16.S b/vm/mterp/x86/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..d96821d
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT8.S b/vm/mterp/x86/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..5d477fb
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG.S b/vm/mterp/x86/OP_ADD_LONG.S
new file mode 100644
index 0000000..bc157f6
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"addl (rFP,%ecx,4),rIBASE", "instr2":"adcl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG_2ADDR.S b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..1f9d97e
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"addl %eax,(rFP,rINST,4)","instr2":"adcl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_AGET.S b/vm/mterp/x86/OP_AGET.S
new file mode 100644
index 0000000..42dfa0a
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET.S
@@ -0,0 +1,24 @@
+%default { "load":"movl", "shift":"4" }
+%verify "executed"
+    /*
+     * Array get, 32 bits or less.  vAA <- vBB[vCC].
+     *
+     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    $load     offArrayObject_contents(%eax,%ecx,$shift),%eax
+.L${opcode}_finish:
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_AGET_BOOLEAN.S b/vm/mterp/x86/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..27f6de0
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_BYTE.S b/vm/mterp/x86/OP_AGET_BYTE.S
new file mode 100644
index 0000000..49c83e1
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movsbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_CHAR.S b/vm/mterp/x86/OP_AGET_CHAR.S
new file mode 100644
index 0000000..b5f6bcf
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzwl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_OBJECT.S b/vm/mterp/x86/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..0c43394
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S"
diff --git a/vm/mterp/x86/OP_AGET_SHORT.S b/vm/mterp/x86/OP_AGET_SHORT.S
new file mode 100644
index 0000000..aeeffa3
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movswl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_WIDE.S b/vm/mterp/x86/OP_AGET_WIDE.S
new file mode 100644
index 0000000..32266bc
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_WIDE.S
@@ -0,0 +1,24 @@
+%verify "executed"
+    /*
+     * Array get, 64 bits.  vAA <- vBB[vCC].
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD %ecx rINST 0
+    SET_VREG_WORD %eax rINST 1
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_AND_INT.S b/vm/mterp/x86/OP_AND_INT.S
new file mode 100644
index 0000000..3bb8757
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"andl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_2ADDR.S b/vm/mterp/x86/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..2dee26b
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"andl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT16.S b/vm/mterp/x86/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..e0fe168
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT8.S b/vm/mterp/x86/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..62c68dc
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG.S b/vm/mterp/x86/OP_AND_LONG.S
new file mode 100644
index 0000000..06df873
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"andl (rFP,%ecx,4),rIBASE", "instr2":"andl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG_2ADDR.S b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..419850e
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"andl %eax,(rFP,rINST,4)","instr2":"andl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_APUT.S b/vm/mterp/x86/OP_APUT.S
new file mode 100644
index 0000000..f51c9c7
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT.S
@@ -0,0 +1,25 @@
+%default { "reg":"rINST", "store":"movl", "shift":"4" }
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,$shift),%eax
+.L${opcode}_finish:
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    $store     $reg,(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_APUT_BOOLEAN.S b/vm/mterp/x86/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..fb1e8db
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" {"reg":"rINSTbl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_BYTE.S b/vm/mterp/x86/OP_APUT_BYTE.S
new file mode 100644
index 0000000..366c8c5
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTbl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_CHAR.S b/vm/mterp/x86/OP_APUT_CHAR.S
new file mode 100644
index 0000000..9c87384
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTw", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_OBJECT.S b/vm/mterp/x86/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..0850e1c
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_OBJECT.S
@@ -0,0 +1,66 @@
+%verify "executed"
+    /*
+     * Array put, 32 bits or less.  vBB[vCC] <- vAA
+     *
+     * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    GET_VREG_R  rINST rINST             # rINST<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects
+                                        #    arrayObj in eax
+                                        #    index in ecx
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST<- vAA
+     */
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST,rINST                    # storing null reference?
+    je        .L${opcode}_skip_check
+    SPILL_TMP1(%ecx)                         # save target address
+    SPILL_TMP2(%eax)                         # save object head
+    movl      offObject_clazz(%eax),%eax     # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST),%ecx    # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    movl      %ecx,sReg0                     # store the two classes for later
+    movl      %eax,sReg1
+    SPILL(rIBASE)
+    call      dvmCanPutArrayElement          # test object type vs. array type
+    UNSPILL(rIBASE)
+    UNSPILL_TMP1(%ecx)                       # recover target address
+    testl     %eax,%eax
+    movl      rSELF,%eax
+    jne       .L${opcode}_types_okay
+
+    # The types don't match.  We need to throw an ArrayStoreException.
+    EXPORT_PC
+    movl      sReg0,%eax                     # restore the two classes...
+    movl      %eax,OUT_ARG0(%esp)
+    movl      sReg1,%ecx
+    movl      %ecx,OUT_ARG1(%esp)
+    call      dvmThrowArrayStoreExceptionIncompatibleElement # ...and throw
+    jmp       common_exceptionThrown
+
+.L${opcode}_types_okay:
+    movl      offThread_cardTable(%eax),%eax   # get card table base
+    movl      rINST,(%ecx)                   # store into array
+    UNSPILL_TMP2(rINST)                      # recover object head
+    FETCH_INST_OPCODE 2 %ecx
+    shrl      $$GC_CARD_SHIFT,rINST          # object head to card number
+    movb      %al,(%eax,rINST)               # mark card using object head
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_skip_check:
+    movl      rINST,(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_APUT_SHORT.S b/vm/mterp/x86/OP_APUT_SHORT.S
new file mode 100644
index 0000000..9c87384
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"rINSTw", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_WIDE.S b/vm/mterp/x86/OP_APUT_WIDE.S
new file mode 100644
index 0000000..cd1e723
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_WIDE.S
@@ -0,0 +1,24 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC]<-vAA.
+     *
+     */
+    /* op vAA, vBB, vCC */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    GET_VREG_R  %eax %eax               # eax<- vBB (array object)
+    GET_VREG_R  %ecx %ecx               # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jae       common_errArrayIndex      # index >= length, bail.  Expects:
+                                        #   arrayObj in eax
+                                        #   index in ecx
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD %ecx rINST 0
+    GET_VREG_WORD rINST rINST 1
+    movl      %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_ARRAY_LENGTH.S b/vm/mterp/x86/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..25caca3
--- /dev/null
+++ b/vm/mterp/x86/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+   mov      rINST,%eax                # eax<- BA
+   sarl     $$4,rINST                 # rINST<- B
+   GET_VREG_R %ecx rINST              # ecx<- vB (object ref)
+   andb     $$0xf,%al                 # eax<- A
+   testl    %ecx,%ecx                 # is null?
+   je       common_errNullObject
+   movl     offArrayObject_length(%ecx),rINST
+   FETCH_INST_OPCODE 1 %ecx
+   ADVANCE_PC 1
+   SET_VREG rINST %eax
+   GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_BREAKPOINT.S b/vm/mterp/x86/OP_BREAKPOINT.S
new file mode 100644
index 0000000..6da9957
--- /dev/null
+++ b/vm/mterp/x86/OP_BREAKPOINT.S
@@ -0,0 +1,19 @@
+%verify "executed"
+    /*
+     * Breakpoint handler.
+     *
+     * Restart this instruction with the original opcode.  By
+     * the time we get here, the breakpoint will have already been
+     * handled.  We also assume that all other special "checkBefore"
+     * actions have been handled, so we'll transition directly
+     * to the real handler
+     */
+    SPILL(rIBASE)
+    movl    rPC,OUT_ARG0(%esp)
+    call    dvmGetOriginalOpcode
+    UNSPILL(rIBASE)
+    movl    rSELF,%ecx
+    movzbl  1(rPC),rINST
+    movl    offThread_mainHandlerTable(%ecx),%ecx
+    jmp     *(%ecx,%eax,4)
+
diff --git a/vm/mterp/x86/OP_CHECK_CAST.S b/vm/mterp/x86/OP_CHECK_CAST.S
new file mode 100644
index 0000000..0543a99
--- /dev/null
+++ b/vm/mterp/x86/OP_CHECK_CAST.S
@@ -0,0 +1,77 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    movl      rSELF,%ecx
+    GET_VREG_R  rINST,rINST             # rINST<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST,rINST               # is oject null?
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    je        .L${opcode}_okay          # null obj, cast always succeeds
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved class
+    movl      offObject_clazz(rINST),%ecx # ecx<- obj->clazz
+    testl     %eax,%eax                 # have we resolved this before?
+    je        .L${opcode}_resolve       # no, go do it now
+.L${opcode}_resolved:
+    cmpl      %eax,%ecx                 # same class (trivial success)?
+    jne       .L${opcode}_fullcheck     # no, do full check
+.L${opcode}_okay:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST holds object
+     */
+.L${opcode}_fullcheck:
+    movl    %eax,sReg0                 # we'll need the desired class on failure
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmInstanceofNonTrivial    # eax<- boolean result
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # failed?
+    jne     .L${opcode}_okay           # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException.
+    EXPORT_PC
+    movl    offObject_clazz(rINST),%eax
+    movl    %eax,OUT_ARG0(%esp)                 # arg0<- obj->clazz
+    movl    sReg0,%ecx
+    movl    %ecx,OUT_ARG1(%esp)                 # arg1<- desired class
+    call    dvmThrowClassCastException
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST holds object
+     */
+.L${opcode}_resolve:
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)        # arg1<- BBBB
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+    movl    $$0,OUT_ARG2(%esp)         # arg2<- false
+    movl    %ecx,OUT_ARG0(%esp)        # arg0<- method->clazz
+    SPILL(rIBASE)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rIBASE)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST),%ecx  # ecx<- obj->clazz
+    jmp     .L${opcode}_resolved       # pick up where we left off
diff --git a/vm/mterp/x86/OP_CMPG_DOUBLE.S b/vm/mterp/x86/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..1388d7c
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_DOUBLE.S
@@ -0,0 +1,33 @@
+%default {"is_double":"1","nanval":"1"}
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "left arg NaN"
+%verify "right arg NaN"
+    /* float/double_cmp[gl] vAA, vBB, vCC */
+    movzbl    3(rPC),%eax             # eax<- CC
+    movzbl    2(rPC),%ecx             # ecx<- BB
+    .if $is_double
+    fldl     (rFP,%eax,4)
+    fldl     (rFP,%ecx,4)
+    .else
+    flds     (rFP,%eax,4)
+    flds     (rFP,%ecx,4)
+    .endif
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    FETCH_INST_OPCODE 2 %eax
+    jp       .L${opcode}_isNaN
+    je       .L${opcode}_finish
+    sbbl     %ecx,%ecx
+    jb       .L${opcode}_finish
+    incl     %ecx
+.L${opcode}_finish:
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
+
+.L${opcode}_isNaN:
+    movl      $$$nanval,%ecx
+    jmp       .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_CMPG_FLOAT.S b/vm/mterp/x86/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..bd6674a
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"1"}
diff --git a/vm/mterp/x86/OP_CMPL_DOUBLE.S b/vm/mterp/x86/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..54ff0d8
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"1","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMPL_FLOAT.S b/vm/mterp/x86/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bbb674d
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMP_LONG.S b/vm/mterp/x86/OP_CMP_LONG.S
new file mode 100644
index 0000000..5202c27
--- /dev/null
+++ b/vm/mterp/x86/OP_CMP_LONG.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+    /*
+     * Compare two 64-bit values.  Puts 0, 1, or -1 into the destination
+     * register based on the results of the comparison.
+     */
+    // TUNING: rework to avoid rIBASE spill
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)
+    movzbl    3(rPC),rIBASE            # rIBASE- CC
+    GET_VREG_WORD %eax %ecx,1          # eax<- v[BB+1]
+    GET_VREG_WORD %ecx %ecx 0          # ecx<- v[BB+0]
+    cmpl      4(rFP,rIBASE,4),%eax
+    jl        .L${opcode}_smaller
+    jg        .L${opcode}_bigger
+    sub       (rFP,rIBASE,4),%ecx
+    ja        .L${opcode}_bigger
+    jb        .L${opcode}_smaller
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_bigger:
+    movl      $$1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_smaller:
+    movl      $$-1,%ecx
+    SET_VREG %ecx rINST
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST.S b/vm/mterp/x86/OP_CONST.S
new file mode 100644
index 0000000..cb70824
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const vAA, #+BBBBbbbb */
+    movl      2(rPC),%eax             # grab all 32 bits at once
+    movl      rINST,rINST             # rINST<- AA
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG %eax rINST               # vAA<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_16.S b/vm/mterp/x86/OP_CONST_16.S
new file mode 100644
index 0000000..b956ee6
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_16.S
@@ -0,0 +1,7 @@
+%verify "executed"
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    SET_VREG %ecx rINST                # vAA<- ssssBBBB
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_CONST_4.S b/vm/mterp/x86/OP_CONST_4.S
new file mode 100644
index 0000000..3db437a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    movsx   rINSTbl,%eax              # eax<-ssssssBx
+    movl    $$0xf,rINST
+    andl    %eax,rINST                # rINST<- A
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    sarl    $$4,%eax
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_CLASS.S b/vm/mterp/x86/OP_CONST_CLASS.S
new file mode 100644
index 0000000..8b12226
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_CLASS.S
@@ -0,0 +1,37 @@
+
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    /* const/class vAA, Class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG  %eax rINST               # vAA<- rResClasses[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     $$1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_HIGH16.S b/vm/mterp/x86/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..9ecf7c6
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_HIGH16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG %eax rINST                   # vAA<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_STRING.S b/vm/mterp/x86/OP_CONST_STRING.S
new file mode 100644
index 0000000..538cace
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING.S
@@ -0,0 +1,36 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_STRING_JUMBO.S b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..6148244
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,36 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBBBBBB */
+    movl      rSELF,%ecx
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offThread_methodClassDex(%ecx),%ecx# ecx<- self->methodClassDex
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    FETCH_INST_OPCODE 3 %ecx
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG  %eax rINST               # vAA<- rResString[BBBB]
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    movl     rSELF,%eax
+    EXPORT_PC
+    movl     offThread_method(%eax),%eax # eax<- self->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rIBASE)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    FETCH_INST_OPCODE 3 %ecx
+    SET_VREG %eax rINST
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE.S b/vm/mterp/x86/OP_CONST_WIDE.S
new file mode 100644
index 0000000..253af78
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    movl      2(rPC),%eax         # eax<- lsw
+    movzbl    rINSTbl,%ecx        # ecx<- AA
+    movl      6(rPC),rINST        # rINST<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST,4(%ecx)
+    movl      %eax,(%ecx)
+    FETCH_INST_OPCODE 5 %ecx
+    ADVANCE_PC 5
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_16.S b/vm/mterp/x86/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..ee8004c
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/16 vAA, #+BBBB */
+    movswl    2(rPC),%eax               # eax<- ssssBBBB
+    SPILL(rIBASE)                       # preserve rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST 1        # store msw
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_32.S b/vm/mterp/x86/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..12cdd01
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_32.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    movl     2(rPC),%eax                # eax<- BBBBbbbb
+    SPILL(rIBASE)                       # save rIBASE (cltd trashes it)
+    cltd                                # rIBASE:eax<- ssssssssssssBBBB
+    SET_VREG_WORD rIBASE rINST,1        # store msw
+    FETCH_INST_OPCODE 3 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 0          # store lsw
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..15a0c55
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD %eax rINST 1            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD %eax rINST 0            # v[AA+0]<- eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE.S b/vm/mterp/x86/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..8ccff2e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..60a0072
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT.S b/vm/mterp/x86/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..740e26e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2ed12b6
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_INT.S b/vm/mterp/x86/OP_DIV_INT.S
new file mode 100644
index 0000000..8afdcb0
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_2ADDR.S b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..a1ae0f9
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT16.S b/vm/mterp/x86/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..4817734
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT8.S b/vm/mterp/x86/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..f59838c
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_LONG.S b/vm/mterp/x86/OP_DIV_LONG.S
new file mode 100644
index 0000000..0dc5546
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG.S
@@ -0,0 +1,46 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rIBASE)                      # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .L${opcode}_check_zero
+    cmpl     $$-1,%eax
+    je       .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+.L${opcode}_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     $routine
+.L${opcode}_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                 # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .L${opcode}_notSpecial
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rIBASE,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD rIBASE %ecx 0
+    GET_VREG_WORD %ecx %ecx 1
+    testl    rIBASE,rIBASE
+    jne      .L${opcode}_notSpecial1
+    cmpl     $$0x80000000,%ecx
+    jne      .L${opcode}_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $special,rIBASE
+    jmp      .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DIV_LONG_2ADDR.S b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..4722098
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,47 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div/2addr vA, vB */
+    movzbl    rINSTbl,%eax
+    shrl      $$4,%eax                  # eax<- B
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SPILL(rIBASE)                       # save rIBASE/%edx
+    GET_VREG_WORD rIBASE %eax 0
+    GET_VREG_WORD %eax %eax 1
+    movl     rIBASE,OUT_ARG2(%esp)
+    testl    %eax,%eax
+    je       .L${opcode}_check_zero
+    cmpl     $$-1,%eax
+    je       .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+.L${opcode}_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rIBASE,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     $routine
+.L${opcode}_finish:
+    SET_VREG_WORD rIBASE rINST 1
+    UNSPILL(rIBASE)                    # restore rIBASE/%edx
+    SET_VREG_WORD %eax rINST 0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_check_zero:
+    testl   rIBASE,rIBASE
+    jne     .L${opcode}_notSpecial
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rIBASE,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD rIBASE rINST 0
+    GET_VREG_WORD %ecx rINST 1
+    testl    rIBASE,rIBASE
+    jne      .L${opcode}_notSpecial1
+    cmpl     $$0x80000000,%ecx
+    jne      .L${opcode}_notSpecial1
+    /* minint / -1, return minint on div, 0 on rem */
+    xorl     %eax,%eax
+    movl     $special,rIBASE
+    jmp      .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..12e73a2
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fldl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_INT.S b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..7cd993a
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_LONG.S b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..b80b020
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE.S b/vm/mterp/x86/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..c739fdf
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE.S
@@ -0,0 +1,106 @@
+%verify "executed"
+%verify "exception handled"
+    /*
+     * Execute a "native inline" instruction.
+     *
+     * We will be calling through a function table:
+     *
+     * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+     *
+     * Ignores argument count - always loads 4.
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    movl      rSELF,%ecx
+    EXPORT_PC
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    SPILL(rIBASE)                       # preserve rIBASE
+    movl      offThread_subMode(%ecx), %edx # edx<- submode flags
+    andl      $$kSubModeDebugProfile, %edx # debug or profile mode active?
+    jnz       .L${opcode}_debugprofile   # yes, take slow path
+.L${opcode}_resume:
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    call      .L${opcode}_continue      # make call; will return after
+    UNSPILL(rIBASE)                     # restore rIBASE
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue:
+    /*
+     * Extract args, call function.
+     *  ecx = #of args (0-4)
+     *  eax = call index
+     *  @esp = return addr
+     *  esp is -4 from normal
+     *
+     *  Go ahead and load all 4 args, even if not used.
+     */
+    movzwl    4(rPC),rIBASE
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rIBASE,%ecx
+    GET_VREG_R  %ecx %ecx
+    sarl      $$4,rIBASE
+    movl      %ecx,4+OUT_ARG3(%esp)
+
+    sall      $$4,%eax      # index *= sizeof(table entry)
+    jmp       *gDvmInlineOpsTable(%eax)
+    # will return to caller of .L${opcode}_continue
+
+    /*
+     * We're debugging or profiling.
+     * eax: opIndex
+     */
+.L${opcode}_debugprofile:
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- BBBB
+    SPILL_TMP1(%eax)                    # save opIndex
+    call      dvmResolveInlineNative    # dvmResolveInlineNative(opIndex)
+    movl      rSELF,%ecx                # restore self
+    testl     %eax,%eax                 # method resolved?
+    movl      %eax,%edx                 # save possibly resolved method in edx
+    UNSPILL_TMP1(%eax)                  # in case not resolved, restore opIndex
+    jz        .L${opcode}_resume        # not resolved, just move on
+    SPILL_TMP2(%edx)                    # save method
+    movl      %edx,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastMethodTraceEnter   # dvmFastMethodTraceEnter(method,self)
+    movl      rSELF,%ecx                # restore self
+    UNSPILL_TMP1(%eax)                  # restore opIndex
+    leal      offThread_retval(%ecx),%ecx # ecx<- &self->retval
+    movl      %ecx,OUT_ARG4(%esp)       # needed for pResult of inline operation handler
+    call      .L${opcode}_continue      # make call; will return after
+    SPILL_TMP1(%eax)                    # save result of inline
+    UNSPILL_TMP2(%eax)                  # restore method
+    movl      rSELF,%ecx                # restore self
+    movl      %eax,OUT_ARG0(%esp)       # arg0<- method
+    movl      %ecx,OUT_ARG1(%esp)       # arg1<- self
+    call      dvmFastNativeMethodTraceExit # dvmFastNativeMethodTraceExit(method,self)
+    UNSPILL(rIBASE)                     # restore rIBASE
+    UNSPILL_TMP1(%eax)                  # restore result of inline
+    testl     %eax,%eax                 # successful?
+    jz        common_exceptionThrown    # no, handle exception
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..dde53aa
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,129 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+    /*
+     * Create a new array with elements filled from registers.
+     *
+     * for: filled-new-array, filled-new-array/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+    movl    rSELF,%eax
+    movl    offThread_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rIBASE)                             # preserve rIBASE
+    movl    (%eax,%ecx,4),%eax                # eax<- resolved class
+    EXPORT_PC
+    testl   %eax,%eax                         # already resolved?
+    jne     .L${opcode}_continue              # yes, continue
+    # less frequent path, so we'll redo some work
+    movl    rSELF,%eax
+    movl    $$0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offThread_method(%eax),%eax         # eax<- self->method
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    testl   %eax,%eax                         # null?
+    je      common_exceptionThrown            # yes, handle it
+
+       # note: fall through to .L${opcode}_continue
+
+    /*
+     * On entry:
+     *    eax holds array class [r0]
+     *    rINST holds AA or BB [r10]
+     *    ecx is scratch
+     */
+.L${opcode}_continue:
+    movl    offClassObject_descriptor(%eax),%ecx  # ecx<- arrayClass->descriptor
+    movl    $$ALLOC_DONT_TRACK,OUT_ARG2(%esp)     # arg2<- flags
+    movzbl  1(%ecx),%ecx                          # ecx<- descriptor[1]
+    movl    %eax,OUT_ARG0(%esp)                   # arg0<- arrayClass
+    movl    rSELF,%eax
+    cmpb    $$'I',%cl                             # supported?
+    je      1f
+    cmpb    $$'L',%cl
+    je      1f
+    cmpb    $$'[',%cl
+    jne      .L${opcode}_notimpl                  # no, not handled yet
+1:
+    movl    %ecx,offThread_retval+4(%eax)           # save type
+    .if      (!$isrange)
+    SPILL_TMP1(rINST)                              # save copy, need "B" later
+    sarl    $$4,rINST
+    .endif
+    movl    rINST,OUT_ARG1(%esp)                  # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass     # eax<- call(arrayClass, length, flags)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offThread_retval(%ecx)             # retval.l<- new array
+    movzwl  4(rPC),%ecx                           # ecx<- FEDC or CCCC
+    leal    offArrayObject_contents(%eax),%eax    # eax<- newArray->contents
+
+/* at this point:
+ *     eax is pointer to tgt
+ *     rINST is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL1 is BA
+ *  We now need to copy values from registers into the array
+ */
+
+    .if $isrange
+    # set up src pointer
+    SPILL_TMP2(%esi)
+    SPILL_TMP3(%edi)
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    %eax,%edi         # set up dst ptr
+    movl    rINST,%ecx        # load count register
+    rep
+    movsd
+    UNSPILL_TMP2(%esi)
+    UNSPILL_TMP3(%edi)
+    movl    rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .else
+    testl  rINST,rINST
+    je     4f
+    UNSPILL_TMP1(rIBASE)      # restore "BA"
+    andl   $$0x0f,rIBASE      # rIBASE<- 0000000A
+    sall   $$16,rIBASE        # rIBASE<- 000A0000
+    orl    %ecx,rIBASE        # rIBASE<- 000AFEDC
+3:
+    movl   $$0xf,%ecx
+    andl   rIBASE,%ecx        # ecx<- next reg to load
+    GET_VREG_R %ecx %ecx
+    shrl   $$4,rIBASE
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $$1,rINST
+    jne    3b
+4:
+    movl   rSELF,%ecx
+    movl    offThread_retval+4(%ecx),%eax      # eax<- type
+    .endif
+
+    cmpb    $$'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offThread_retval(%ecx),%eax        # eax<- object head
+    movl    offThread_cardTable(%ecx),%ecx     # card table base
+    shrl    $$GC_CARD_SHIFT,%eax             # convert to card num
+    movb    %cl,(%ecx,%eax)                  # mark card based on object head
+5:
+    UNSPILL(rIBASE)                          # restore rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    movl    $$.LstrFilledNewArrayNotImplA,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowInternalError
+    jmp     common_exceptionThrown
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..dc6cc4d
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/x86/OP_FILL_ARRAY_DATA.S b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..5ca17a6
--- /dev/null
+++ b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG_R %eax rINST
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..11b2c23
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"flds","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_INT.S b/vm/mterp/x86/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..8d55286
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_LONG.S b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..2493b13
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_GOTO.S b/vm/mterp/x86/OP_GOTO.S
new file mode 100644
index 0000000..a7913d1
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 8-bit offset.
+     *
+     * The branch distance is a signed code-unit offset, which we need to
+     * double to get a byte offset.
+     */
+    /* goto +AA */
+    movl    rSELF,%ecx
+    movsbl  rINSTbl,%eax          # eax<- ssssssAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_16.S b/vm/mterp/x86/OP_GOTO_16.S
new file mode 100644
index 0000000..84220d0
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_16.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset
+     */
+    /* goto/16 +AAAA */
+    movl    rSELF,%ecx
+    movswl  2(rPC),%eax            # eax<- ssssAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_32.S b/vm/mterp/x86/OP_GOTO_32.S
new file mode 100644
index 0000000..c3d1050
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_32.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit offset.
+     */
+    /* goto/32 AAAAAAAA */
+    movl    rSELF,%ecx
+    movl    2(rPC),%eax            # eax<- AAAAAAAA
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IF_EQ.S b/vm/mterp/x86/OP_IF_EQ.S
new file mode 100644
index 0000000..a8a38b6
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_EQZ.S b/vm/mterp/x86/OP_IF_EQZ.S
new file mode 100644
index 0000000..67fd5d7
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_GE.S b/vm/mterp/x86/OP_IF_GE.S
new file mode 100644
index 0000000..6930328
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GEZ.S b/vm/mterp/x86/OP_IF_GEZ.S
new file mode 100644
index 0000000..71e9127
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GT.S b/vm/mterp/x86/OP_IF_GT.S
new file mode 100644
index 0000000..b987443
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_GTZ.S b/vm/mterp/x86/OP_IF_GTZ.S
new file mode 100644
index 0000000..9d7b697
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_LE.S b/vm/mterp/x86/OP_IF_LE.S
new file mode 100644
index 0000000..b9ac008
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LEZ.S b/vm/mterp/x86/OP_IF_LEZ.S
new file mode 100644
index 0000000..26afc85
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LT.S b/vm/mterp/x86/OP_IF_LT.S
new file mode 100644
index 0000000..79aba48
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_LTZ.S b/vm/mterp/x86/OP_IF_LTZ.S
new file mode 100644
index 0000000..01f4daa
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_NE.S b/vm/mterp/x86/OP_IF_NE.S
new file mode 100644
index 0000000..552b74c
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IF_NEZ.S b/vm/mterp/x86/OP_IF_NEZ.S
new file mode 100644
index 0000000..b2fdb14
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IGET.S b/vm/mterp/x86/OP_IGET.S
new file mode 100644
index 0000000..5740690
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET.S
@@ -0,0 +1,53 @@
+%default { "load":"movl", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field get.
+     *
+     * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # needed by dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    $load   (%ecx,%eax,1),%ecx                  # ecx<- obj.field (8/16/32 bits)
+    FETCH_INST_OPCODE 2 %eax
+    UNSPILL(rIBASE)
+    SET_VREG %ecx rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_IGET_BOOLEAN.S b/vm/mterp/x86/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0829e2c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S" { "load":"movzbl", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IGET_BYTE.S b/vm/mterp/x86/OP_IGET_BYTE.S
new file mode 100644
index 0000000..2350f8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movsbl", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IGET_CHAR.S b/vm/mterp/x86/OP_IGET_CHAR.S
new file mode 100644
index 0000000..4218a8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+%include "x86/OP_IGET.S" { "load":"movzwl", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IGET_OBJECT.S b/vm/mterp/x86/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..84c0a98
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..89ebd36
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET_QUICK.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_QUICK.S b/vm/mterp/x86/OP_IGET_QUICK.S
new file mode 100644
index 0000000..86f1f66
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $$0,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      (%ecx,%eax,1),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SET_VREG  %eax rINST                # fp[A]<- result
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IGET_SHORT.S b/vm/mterp/x86/OP_IGET_SHORT.S
new file mode 100644
index 0000000..71f0d34
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movswl", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IGET_VOLATILE.S b/vm/mterp/x86/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_WIDE.S b/vm/mterp/x86/OP_IGET_WIDE.S
new file mode 100644
index 0000000..723c9b7
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit instance field get.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)                               # preserve rIBASE
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)               # for dvmResolveInstField
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save objpointer across call
+    movl    rPC,OUT_ARG0(%esp)                  # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    movl    (%eax),%ecx                         # ecx<- lsw
+    movl    4(%eax),%eax                        # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                             # restore rIBASE
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IGET_WIDE_QUICK.S b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..dd63c73
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    cmpl      $$0,%ecx                  # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%eax        # eax<- address of 64-bit source
+    movl      (%eax),%ecx               # ecx<- lsw
+    movl      4(%eax),%eax              # eax<- msw
+    andb      $$0xf,rINSTbl             # rINST<- A
+    SET_VREG_WORD %ecx rINST 0          # v[A+0]<- lsw
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1          # v[A+1]<- msw
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_INSTANCE_OF.S b/vm/mterp/x86/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..c54f4f7
--- /dev/null
+++ b/vm/mterp/x86/OP_INSTANCE_OF.S
@@ -0,0 +1,93 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+    /*
+     * Check to see if an object reference is an instance of a class.
+     *
+     * Most common situation is a non-null object, being compared against
+     * an already-resolved class.
+     */
+    /* instance-of vA, vB, class@CCCC */
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    rSELF,%ecx
+    testl   %eax,%eax                   # object null?
+    movl    offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)                       # preserve rIBASE
+    je      .L${opcode}_store           # null obj, not instance, store it
+    movzwl  2(rPC),rIBASE               # rIBASE<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rIBASE,4),%ecx        # ecx<- resolved class
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    testl   %ecx,%ecx                   # have we resolved this before?
+    je      .L${opcode}_resolve         # not resolved, do it now
+.L${opcode}_resolved:  # eax<- obj->clazz, ecx<- resolved class
+    cmpl    %eax,%ecx                   # same class (trivial success)?
+    je      .L${opcode}_trivial         # yes, trivial finish
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST has BA
+     */
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    # fall through to ${opcode}_store
+
+    /*
+     * eax holds boolean result
+     * rINST holds BA
+     */
+.L${opcode}_store:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $$0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    SET_VREG %eax rINST                 # vA<- eax
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.L${opcode}_trivial:
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    andb    $$0xf,rINSTbl               # <- A
+    ADVANCE_PC 2
+    movl    $$1,%eax
+    SET_VREG %eax rINST                 # vA<- true
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rIBASE holds BBBB
+     *  rINST holds BA
+     */
+.L${opcode}_resolve:
+    movl    rIBASE,OUT_ARG1(%esp)         # arg1<- BBBB
+    movl    rSELF,%ecx
+    movl    offThread_method(%ecx),%ecx
+    movl    $$1,OUT_ARG2(%esp)          # arg2<- true
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    EXPORT_PC
+    movl    %ecx,OUT_ARG0(%esp)         # arg0<- method->clazz
+    call    dvmResolveClass             # eax<- resolved ClassObject ptr
+    testl   %eax,%eax                   # success?
+    je      common_exceptionThrown      # no, handle exception
+/* Now, we need to sync up with fast path.  We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+    movl    %eax,%ecx                   # ecx<- resolved class
+    movl    rINST,%eax                  # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB (obj)
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    jmp     .L${opcode}_resolved
diff --git a/vm/mterp/x86/OP_INT_TO_BYTE.S b/vm/mterp/x86/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..44d032b
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movsbl %al,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_CHAR.S b/vm/mterp/x86/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..49c8055
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movzwl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_DOUBLE.S b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..e9c5640
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_INT_TO_FLOAT.S b/vm/mterp/x86/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..06d658d
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_INT_TO_LONG.S b/vm/mterp/x86/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..551efaf
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* int to long vA, vB */
+    movzbl  rINSTbl,%eax                # eax<- +A
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    andb    $$0xf,rINSTbl               # rINST<- A
+    SPILL(rIBASE)                       # cltd trashes rIBASE/edx
+    cltd                                # rINST:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD rIBASE rINST 1        # v[A+1]<- rIBASE/rPC
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[A+0]<- %eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_INT_TO_SHORT.S b/vm/mterp/x86/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..d5dc5d8
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movswl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT.S b/vm/mterp/x86/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..7d27c6f
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT.S
@@ -0,0 +1,53 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a direct method call.
+     *
+     * (We could defer the "is 'this' pointer null" test to the common
+     * method invocation code, and use a flag to indicate that static
+     * calls don't count.  If we do this as part of copying the arguments
+     * out we could avoiding loading the first arg twice.)
+     *
+     * for: invoke-direct, invoke-direct/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rIBASE            # rIBASE<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!$isrange)
+    andl      $$0xf,rIBASE             # rIBASE<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG_R  %ecx rIBASE            # ecx<- "this" ptr
+    je        .L${opcode}_resolve      # not resolved, do it now
+.L${opcode}_finish:
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethod${routine}  # no, continue on
+    jmp       common_errNullObject
+
+    /*
+     * On entry:
+     *   TMP_SPILL  <- "this" register
+     * Things a bit ugly on this path, but it's the less
+     * frequent one.  We'll have to do some reloading.
+     */
+.L${opcode}_resolve:
+     SPILL_TMP1(%ecx)
+     movl     rSELF,%ecx
+     movl     offThread_method(%ecx),%ecx  # ecx<- self->method
+     movzwl   2(rPC),%eax      # reference (BBBB or CCCC)
+     movl     offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+     movl     $$METHOD_DIRECT,OUT_ARG2(%esp)
+     movl     %eax,OUT_ARG1(%esp)
+     movl     %ecx,OUT_ARG0(%esp)
+     call     dvmResolveMethod # eax<- call(clazz, ref, flags)
+     UNSPILL_TMP1(%ecx)
+     testl    %eax,%eax
+     jne      .L${opcode}_finish
+     jmp      common_exceptionThrown
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..140430a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..26d2d5c
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,34 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle an interface method call.
+     *
+     * for: invoke-interface, invoke-interface/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl     4(rPC),%eax              # eax<- FEDC or CCCC
+    movl       rSELF,%ecx
+    .if        (!$isrange)
+    andl       $$0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R   %eax %eax              # eax<- "this"
+    EXPORT_PC
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       %eax, TMP_SPILL1(%ebp)
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offThread_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offThread_method(%ecx),%ecx           # ecx<- method
+    movl       %eax,OUT_ARG3(%esp)                 # arg3<- dex
+    movzwl     2(rPC),%eax                         # eax<- BBBB
+    movl       %ecx,OUT_ARG2(%esp)                 # arg2<- method
+    movl       %eax,OUT_ARG1(%esp)                 # arg1<- BBBB
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    movl       TMP_SPILL1(%ebp), %ecx
+    jmp        common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..cbebedb
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S
new file mode 100644
index 0000000..f6e3f42
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT_RANGE.S
@@ -0,0 +1,53 @@
+%default { "cccc":"2" }
+%verify "executed"
+%verify "finalizable class"
+    /*
+     * Invoke Object.<init> on an object.  In practice we know that
+     * Object's nullary constructor doesn't do anything, so we just
+     * skip it unless a debugger is active.
+     */
+    movzwl     4(rPC),%eax              # eax<- CCCC, offset = 2 * cccc
+    GET_VREG_R %ecx, %eax               # ecx<- "this" ptr
+    testl      %ecx,%ecx                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       offObject_clazz(%ecx), %eax # eax<- obj->clazz
+    SPILL_TMP1(rIBASE)                  # save %edx
+    movl       offClassObject_accessFlags(%eax), %edx # edx<- clazz->accessFlags
+    andl       $$CLASS_ISFINALIZABLE, %edx # is this class finalizable?
+    jnz        .L${opcode}_setFinal     # yes, go
+.L${opcode}_finish:
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax
+    andl       $$kSubModeDebuggerActive, %eax # debugger active?
+    jnz        .L${opcode}_debugger     # Yes - skip optimization
+    UNSPILL_TMP1(rIBASE)
+    FETCH_INST_OPCODE 3 %ecx            # 3 = cccc + 1
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
+%break
+
+.L${opcode}_setFinal:
+    EXPORT_PC                           # can throw
+    movl       %ecx, OUT_ARG0(%esp)     # arg1<- obj
+    call       dvmSetFinalizable        # call dvmSetFinalizable(obj)
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %eax # eax<- self->exception
+    cmpl       $$0, %eax                # exception pending?
+    jne        common_exceptionThrown   # yes, handle it
+    jmp        .L${opcode}_finish
+
+    /*
+     * A debugger is attached, so we need to go ahead and do
+     * this.  For simplicity, we'll just jump directly to the
+     * corresponding handler.  Note that we can't use
+     * rIBASE here because it may be in single-step mode.
+     * Load the primary table base directly.
+     */
+.L${opcode}_debugger:
+    movl    offThread_mainHandlerTable(%ecx), %ecx # load main handler table
+    movl       $$OP_INVOKE_DIRECT_RANGE, %eax
+    /*
+     * We can't use GOTO_NEXT here since we want to jump directly to
+     * handler without touching rIBASE.
+     */
+    jmp        *(%ecx,%eax,4)
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC.S b/vm/mterp/x86/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..d5004bc
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC.S
@@ -0,0 +1,68 @@
+%default { "routine":"NoRange","isrange":"0" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a static method call.
+     *
+     * for: invoke-static, invoke-static/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+#if defined(WITH_JIT)
+    movl     %edx, TMP_SPILL1(%ebp)
+    lea      (%ecx,%eax,4), %edx
+    movl     %edx, TMP_SPILL2(%ebp)
+    movl     TMP_SPILL1(%ebp), %edx
+#endif
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    movl      $$0, %ecx                 # make "this" null
+    testl     %eax,%eax
+    jne       common_invokeMethod${routine}
+
+    movl      rSELF,%ecx
+    movl      offThread_method(%ecx),%ecx # ecx<- self->method
+    movzwl    2(rPC),%eax
+    movl      offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+    movl      %eax,OUT_ARG1(%esp)       # arg1<- BBBB
+    movl      %ecx,OUT_ARG0(%esp)       # arg0<- clazz
+    movl      $$METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rIBASE)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rIBASE)
+    testl     %eax,%eax                 # got null?
+#if defined(WITH_JIT)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      rSELF,%ecx
+    movzwl    offThread_subMode(%ecx), %ecx
+    je        common_exceptionThrown    # null, handle exception
+    andl      $$kSubModeJitTraceBuild, %ecx # is trace under construction?
+    movl      $$0, %ecx                 # make "this" null
+    je        common_invokeMethod${routine} # no (%eax=method, %ecx="this")
+    movl      TMP_SPILL2(%ebp), %edx
+    cmpl      $$0, (%edx)                  # finished resolving
+    movl      TMP_SPILL1(%ebp), %edx
+    jne        common_invokeMethod${routine} # yes (%eax=method, %ecx="this")
+    movl      rSELF, %edx
+    movl      %edx, OUT_ARG0(%esp)
+    movl      rPC, OUT_ARG1(%esp)
+    movl      %eax, TMP_SPILL2(%ebp)
+    movl      %ecx, TMP_SPILL3(%ebp)
+    SPILL(rIBASE)
+    call      dvmJitEndTraceSelect
+    UNSPILL(rIBASE)
+    movl      TMP_SPILL1(%ebp), %edx
+    movl      TMP_SPILL2(%ebp), %eax
+    movl      TMP_SPILL3(%ebp), %ecx
+    jmp       common_invokeMethod${routine}
+#else
+    movl      $$0, %ecx                 # make "this" null
+    jne       common_invokeMethod${routine}
+    jmp       common_exceptionThrown
+#endif
+
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..da6d0bf
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_STATIC.S" { "routine":"Range","isrange":"1" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER.S b/vm/mterp/x86/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..d210daf
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER.S
@@ -0,0 +1,69 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle a "super" method call.
+     *
+     * for: invoke-super, invoke-super/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,rINST
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(rINST),%ecx # ecx<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offThread_method(rINST),%eax # eax<- method
+    movzwl    4(rPC),rINST              # rINST<- GFED or CCCC
+    .if       (!$isrange)
+    andl      $$0xf,rINST               # rINST<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %edx rINST             # %edx<- "this" ptr
+    testl     %edx,%edx                # null "this"?
+    SPILL_TMP1(%edx)
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    je       .L${opcode}_resolve
+    /*
+     * At this point:
+     *  ecx = resolved base method [r0]
+     *  eax = method->clazz [r9]
+     */
+.L${opcode}_continue:
+    movl    offClassObject_super(%eax),%eax   # eax<- method->clazz->super
+    movzwl  offMethod_methodIndex(%ecx),%edx  # edx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%edx # compare(methodIndex,vtableCount)
+    jae     .L${opcode}_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%edx,4),%eax        # eax<- vtable[methodIndex]
+    UNSPILL_TMP1(%edx)
+    movl    %edx, %ecx
+    jmp     common_invokeMethod${routine}
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.L${opcode}_resolve:
+    SPILL_TMP2(%eax)                    # method->clazz
+    movl    %eax,OUT_ARG0(%esp)         # arg0<- method->clazz
+    movzwl  2(rPC),%ecx                 # ecx<- BBBB
+    movl    $$METHOD_VIRTUAL,OUT_ARG2(%esp)  # arg2<- resolver method type
+    movl    %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP2(%eax)                  # restore method->clazz
+    jne     .L${opcode}_continue        # good to go - continue
+    jmp     common_exceptionThrown      # handle exception
+
+    /*
+     * Throw a NoSuchMethodError with the method name as the message.
+     *  ecx = resolved base method
+     */
+.L${opcode}_nsm:
+    movl    offMethod_name(%ecx),%eax
+    jmp     common_errNoSuchMethod
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..b93fce5
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,28 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+    /*
+     * Handle an optimized "super" method call.
+     *
+     * for: [opt] invoke-super-quick, invoke-super-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%ecx
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offThread_method(%ecx),%ecx # ecx<- current method
+    .if       (!$isrange)
+    andl      $$0xf,%eax                # eax<- D (or stays CCCC)
+    .endif
+    movl      offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    GET_VREG_R  %eax %eax               # eax<- "this"
+    movl      offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+    testl     %eax,%eax                 # null "this"?
+    je        common_errNullObject      # "this" is null, throw exception
+    movl       %eax, TMP_SPILL1(%ebp)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    movl      TMP_SPILL1(%ebp), %ecx
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..e0a27a3
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..ffbcf8c
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..23dee67
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,48 @@
+
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+    /*
+     * Handle a virtual method call.
+     *
+     * for: invoke-virtual, invoke-virtual/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movl      rSELF,%eax
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offThread_methodClassDex(%eax),%eax  # eax<- pDvmDex
+    EXPORT_PC
+    movl      offDvmDex_pResMethods(%eax),%eax   # eax<- pDvmDex->pResMethods
+    movl      (%eax,%ecx,4),%eax          # eax<- resolved baseMethod
+    testl     %eax,%eax                   # already resolved?
+    jne       .L${opcode}_continue        # yes, continue
+    movl      rSELF,%eax
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offThread_method(%eax),%eax   # eax<- self->method
+    movl      offMethod_clazz(%eax),%eax  # ecx<- method->clazz
+    movl      %eax,OUT_ARG0(%esp)         # arg0<- clazz
+    movl      $$METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+    call      dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    testl     %eax,%eax                   # got null?
+    jne       .L${opcode}_continue        # no, continue
+    jmp       common_exceptionThrown      # yes, handle exception
+
+    /* At this point:
+     *   eax = resolved base method
+     *   ecx = scratch
+     */
+.L${opcode}_continue:
+    movzwl    4(rPC),%ecx               # ecx<- GFED or CCCC
+    .if       (!$isrange)
+    andl      $$0xf,%ecx                # ecx<- D (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- "this"
+    movzwl    offMethod_methodIndex(%eax),%eax  # eax<- baseMethod->methodIndex
+    testl     %ecx,%ecx                 # null this?
+    je        common_errNullObject      # go if so
+    movl      offObject_clazz(%ecx),%edx  # edx<- thisPtr->clazz
+    movl      offClassObject_vtable(%edx),%edx # edx<- thisPtr->clazz->vtable
+    movl      (%edx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..58f784d
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+    /*
+     * Handle an optimized virtual method call.
+     *
+     * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+     */
+    /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+    /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+    movzwl    4(rPC),%ecx               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%edx               # ecx<- BBBB
+    .if     (!$isrange)
+    andl      $$0xf,%ecx                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG_R  %ecx %ecx               # ecx<- vC ("this" ptr)
+    testl     %ecx,%ecx                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%ecx),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC                           # might throw later - get ready
+    movl      (%eax,%edx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..53d7602
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..e91a89e
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_IPUT.S b/vm/mterp/x86/OP_IPUT.S
new file mode 100644
index 0000000..2c718b2
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT.S
@@ -0,0 +1,54 @@
+
+%default { "store":"movl", "reg":"rINST", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit instance field put.
+     *
+     * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL   (rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                       # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    $store   $reg,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_BOOLEAN.S b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..1bdde9d
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb","reg":"rINSTbl", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IPUT_BYTE.S b/vm/mterp/x86/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3a8652d
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb", "reg":"rINSTbl", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IPUT_CHAR.S b/vm/mterp/x86/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..ba8d38e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINSTw", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT.S b/vm/mterp/x86/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..6c9fd6e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT.S
@@ -0,0 +1,61 @@
+%default { "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                  # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           # returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    GET_VREG_R rINST rINST                      # rINST<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    movl    rINST,(%ecx,%eax)      # obj.field <- v[A](8/16/32 bits)
+    movl    rSELF,%eax
+    testl   rINST,rINST                         # stored a NULL?
+    movl    offThread_cardTable(%eax),%eax      # get card table base
+    je      1f                                  # skip card mark if null store
+    shrl    $$GC_CARD_SHIFT,%ecx                # object head to card number
+    movb    %al,(%eax,%ecx)                     # mark card using object head
+1:
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..b628e57
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,23 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    movl      rSELF,%eax
+    testl     rINST,rINST               # did we store null?
+    movl      offThread_cardTable(%eax),%eax  # get card table base
+    je        1f                            # skip card mark if null store
+    shrl      $$GC_CARD_SHIFT,%ecx          # object head to card number
+    movb      %al,(%eax,%ecx)               # mark card based on object head
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3959c06
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_IPUT_QUICK.S b/vm/mterp/x86/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..cd4fe3b
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_R  rINST,rINST             # rINST<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST,(%ecx,%eax,1)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_SHORT.S b/vm/mterp/x86/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..299953a
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINSTw", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IPUT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..caf007e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S"
diff --git a/vm/mterp/x86/OP_IPUT_WIDE.S b/vm/mterp/x86/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..d481d02
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE.S
@@ -0,0 +1,55 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit instance field put.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    movl    rSELF,%ecx
+    SPILL(rIBASE)
+    movzwl  2(rPC),rIBASE                       # rIBASE<- 0000CCCC
+    movl    offThread_methodClassDex(%ecx),%eax # eax<- DvmDex
+    movzbl  rINSTbl,%ecx                        # ecx<- BA
+    sarl    $$4,%ecx                            # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax     # eax<- pDvmDex->pResFields
+    andb    $$0xf,rINSTbl                       # rINST<- A
+    GET_VREG_R %ecx %ecx                        # ecx<- fp[B], the object ptr
+    movl    (%eax,rIBASE,4),%eax                # resolved entry
+    testl   %eax,%eax                           # is resolved entry null?
+    jne     .L${opcode}_finish                  # no, already resolved
+    movl    rIBASE,OUT_ARG1(%esp)
+    movl    rSELF,rIBASE
+    EXPORT_PC
+    movl    offThread_method(rIBASE),rIBASE     # rIBASE<- current method
+    movl    offMethod_clazz(rIBASE),rIBASE      # rIBASE<- method->clazz
+    SPILL_TMP1(%ecx)                            # save obj pointer across call
+    movl    rIBASE,OUT_ARG0(%esp)               # pass in method->clazz
+    call    dvmResolveInstField                 #  ... to dvmResolveInstField
+    UNSPILL_TMP1(%ecx)
+    testl   %eax,%eax                           #  ... which returns InstrField ptr
+    jne     .L${opcode}_finish
+    jmp     common_exceptionThrown
+
+.L${opcode}_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax  # eax<- byte offset of field
+    testl   %ecx,%ecx                           # object null?
+    je      common_errNullObject                # object was null
+    leal    (%ecx,%eax,1),%eax                  # eax<- address of field
+    GET_VREG_WORD %ecx rINST 0                  # ecx<- lsw
+    GET_VREG_WORD rINST rINST 1                 # rINST<- msw
+    movl    rINST,4(%eax)
+    movl    %ecx,(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..12eeed6
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_R  %ecx %ecx               # vB (object we're operating on)
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl      %ecx,%ecx                # is object null?
+    je        common_errNullObject
+    leal      (%ecx,%eax,1),%ecx        # ecx<- Address of 64-bit target
+    andb      $$0xf,rINSTbl             # rINST<- A
+    GET_VREG_WORD %eax rINST 0          # eax<- lsw
+    GET_VREG_WORD rINST rINST 1         # rINST<- msw
+    movl      %eax,(%ecx)
+    movl      rINST,4(%ecx)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_LONG_TO_DOUBLE.S b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..7235315
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_LONG_TO_FLOAT.S b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..2c4359a
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstps"}
diff --git a/vm/mterp/x86/OP_LONG_TO_INT.S b/vm/mterp/x86/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..bf5060f
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MONITOR_ENTER.S b/vm/mterp/x86/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..a630db1
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_ENTER.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    FETCH_INST_WORD 1
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC                           # need for precise GC
+    je     common_errNullObject
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rIBASE)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MONITOR_EXIT.S b/vm/mterp/x86/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..98bc373
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_EXIT.S
@@ -0,0 +1,29 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+    /*
+     * Unlock an object.
+     *
+     * Exceptions that occur when unlocking a monitor need to appear as
+     * if they happened at the following instruction.  See the Dalvik
+     * instruction spec.
+     */
+    /* monitor-exit vAA */
+    GET_VREG_R %eax rINST
+    movl    rSELF,%ecx
+    EXPORT_PC
+    testl   %eax,%eax                   # null object?
+    je      .L${opcode}_errNullObject   # go if so
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC 1
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT_R %ecx
+.L${opcode}_errNullObject:
+    ADVANCE_PC 1                        # advance before throw
+    jmp     common_errNullObject
diff --git a/vm/mterp/x86/OP_MOVE.S b/vm/mterp/x86/OP_MOVE.S
new file mode 100644
index 0000000..ec05288
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINSTbl,%eax          # eax<- BA
+    andb   $$0xf,%al             # eax<- A
+    shrl   $$4,rINST            # rINST<- B
+    GET_VREG_R rINST rINST
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG rINST %eax           # fp[A]<-fp[B]
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_16.S b/vm/mterp/x86/OP_MOVE_16.S
new file mode 100644
index 0000000..e25230a
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/16, move-object/16 */
+    /* op vAAAA, vBBBB */
+    movzwl    4(rPC),%ecx              # ecx<- BBBB
+    movzwl    2(rPC),%eax              # eax<- AAAA
+    GET_VREG_R  rINST %ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    SET_VREG  rINST %eax
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_EXCEPTION.S b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..0853866
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* move-exception vAA */
+    movl    rSELF,%ecx
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG %eax rINST                # fp[AA]<- exception object
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    movl    $$0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_MOVE_FROM16.S b/vm/mterp/x86/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..120edb5
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINSTbl,%eax              # eax <= AA
+    movw     2(rPC),rINSTw             # rINSTw <= BBBB
+    GET_VREG_R rINST rINST             # rINST- fp[BBBB]
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG rINST %eax                # fp[AA]<- ecx]
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT.S b/vm/mterp/x86/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..f32d5a6
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_16.S b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..859e4db
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_16.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..fef4401
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT.S b/vm/mterp/x86/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..4a6917b
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    movl     rSELF,%eax                    # eax<- rSELF
+    movl     offThread_retval(%eax),%eax   # eax<- self->retval.l
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    SET_VREG  %eax rINST                   # fp[AA]<- retval.l
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a3f1b1f
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..022bdb3
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    movl    rSELF,%ecx
+    movl    offThread_retval(%ecx),%eax
+    movl    4+offThread_retval(%ecx),%ecx
+    SET_VREG_WORD %eax rINST 0     # v[AA+0] <- eax
+    SET_VREG_WORD %ecx rINST 1     # v[AA+1] <- ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE.S b/vm/mterp/x86/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..df59574
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* move-wide vA, vB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzbl    rINSTbl,%ecx                # ecx <- BA
+    sarl      $$4,rINST                   # rINST<- B
+    GET_VREG_WORD %eax rINST 0            # eax<- v[B+0]
+    GET_VREG_WORD rINST rINST 1           # rINST<- v[B+1]
+    andb      $$0xf,%cl                   # ecx <- A
+    SET_VREG_WORD rINST %ecx 1            # v[A+1]<- rINST
+    SET_VREG_WORD %eax %ecx 0             # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_16.S b/vm/mterp/x86/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..26ea8a4
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/16 vAAAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    4(rPC),%ecx            # ecx<- BBBB
+    movzwl    2(rPC),%eax            # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0       # rINSTw_WORD<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1        # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0       # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1        # v[AAAA+1]<- ecx
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..3d820ad
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* move-wide/from16 vAA, vBBBB */
+    /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+    movzwl    2(rPC),%ecx              # ecx<- BBBB
+    movzbl    rINSTbl,%eax             # eax<- AAAA
+    GET_VREG_WORD rINST %ecx 0         # rINST<- v[BBBB+0]
+    GET_VREG_WORD %ecx %ecx 1          # ecx<- v[BBBB+1]
+    SET_VREG_WORD rINST %eax 0         # v[AAAA+0]<- rINST
+    SET_VREG_WORD %ecx %eax 1          # v[AAAA+1]<- eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE.S b/vm/mterp/x86/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..7d74f78
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    mulsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB * vCC
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d277689
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,15 @@
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    mulsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_MUL_FLOAT.S b/vm/mterp/x86/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..5515c4f
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..c8a3bf7
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_INT.S b/vm/mterp/x86/OP_MUL_INT.S
new file mode 100644
index 0000000..a958114
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * 32-bit binary multiplication.
+     */
+    /* mul vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    SPILL(rIBASE)
+    imull    (rFP,%ecx,4),%eax      # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_2ADDR.S b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..ebd5160
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* mul vA, vB */
+    movzx   rINSTbl,%ecx              # ecx<- A+
+    sarl    $$4,rINST                 # rINST<- B
+    GET_VREG_R %eax rINST             # eax<- vB
+    andb    $$0xf,%cl                 # ecx<- A
+    SPILL(rIBASE)
+    imull   (rFP,%ecx,4),%eax         # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT16.S b/vm/mterp/x86/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..a196c7e
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT16.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl              # rINST<- A
+    SPILL(rIBASE)
+    imull     %ecx,%eax                 # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT8.S b/vm/mterp/x86/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..f36ffc7
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT8.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* mul/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    SPILL(rIBASE)
+    imull     %ecx,%eax                # trashes rIBASE/edx
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG  %eax rINST
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_LONG.S b/vm/mterp/x86/OP_MUL_LONG.S
new file mode 100644
index 0000000..7cf6ccb
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG.S
@@ -0,0 +1,37 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * We could definately use more free registers for
+     * this code.   We spill rINSTw (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill edi (rFP)
+     * for use as the vB pointer and esi (rPC) for use
+     * as the vC pointer.  Yuck.
+     */
+    /* mul-long vAA, vBB, vCC */
+    movzbl    2(rPC),%eax              # eax<- B
+    movzbl    3(rPC),%ecx              # ecx<- C
+    SPILL_TMP2(%esi)                   # save Dalvik PC
+    SPILL(rFP)
+    SPILL(rINST)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # esi<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(%esi),%ecx             # ecx<- Bmsw
+    imull     (rFP),%ecx               # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax              # eax<- Cmsw
+    imull     (%esi),%eax              # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx                # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax               # eax<- Clsw
+    mull      (%esi)                   # eax<- (Clsw*Alsw)
+    UNSPILL(rINST)
+    UNSPILL(rFP)
+    leal      (%ecx,rIBASE),rIBASE # full result now in rIBASE:%eax
+    UNSPILL_TMP2(%esi)             # Restore Dalvik PC
+    FETCH_INST_OPCODE 2 %ecx       # Fetch next instruction
+    movl      rIBASE,4(rFP,rINST,4)# v[B+1]<- rIBASE
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,rINST,4)   # v[B]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_MUL_LONG_2ADDR.S b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..9a0930c
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,37 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, 2-addr version
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill %edx (rIBASE) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and rIBASE as computational
+     * temps.  On top of that, we'll spill %esi (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINSTbl,%eax             # eax<- BA
+    andb      $$0xf,%al                # eax<- A
+    sarl      $$4,rINST                # rINST<- B
+    SPILL_TMP2(%esi)
+    SPILL(rFP)
+    SPILL(rIBASE)
+    leal      (rFP,%eax,4),%esi        # %esi<- &v[A]
+    leal      (rFP,rINST,4),rFP        # rFP<- &v[B]
+    movl      4(%esi),%ecx             # ecx<- Amsw
+    imull     (rFP),%ecx               # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax              # eax<- Bmsw
+    imull     (%esi),%eax              # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx                # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax               # eax<- Blsw
+    mull      (%esi)                   # eax<- (Blsw*Alsw)
+    leal      (%ecx,rIBASE),rIBASE     # full result now in %edx:%eax
+    movl      rIBASE,4(%esi)           # v[A+1]<- rIBASE
+    movl      %eax,(%esi)              # v[A]<- %eax
+    UNSPILL_TMP2(%esi)
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    UNSPILL(rFP)
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NEG_DOUBLE.S b/vm/mterp/x86/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..7b24914
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_NEG_FLOAT.S b/vm/mterp/x86/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..e785155
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_NEG_INT.S b/vm/mterp/x86/OP_NEG_INT.S
new file mode 100644
index 0000000..4dfd6d3
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"negl %eax"}
diff --git a/vm/mterp/x86/OP_NEG_LONG.S b/vm/mterp/x86/OP_NEG_LONG.S
new file mode 100644
index 0000000..9543351
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_LONG.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx        # ecx<- BA
+    sarl      $$4,%ecx            # ecx<- B
+    andb      $$0xf,rINSTbl       # rINST<- A
+    GET_VREG_WORD %eax %ecx 0     # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1     # ecx<- v[B+1]
+    negl      %eax
+    adcl      $$0,%ecx
+    negl      %ecx
+    SET_VREG_WORD %eax rINST 0    # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1    # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_NEW_ARRAY.S b/vm/mterp/x86/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..c9690d3
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_ARRAY.S
@@ -0,0 +1,64 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+    /*
+     * Allocate an array of objects, specified with the array class
+     * and a count.
+     *
+     * The verifier guarantees that this is an array class, so we don't
+     * check for it here.
+     */
+    /* new-array vA, vB, class@CCCC */
+    movl    rSELF,%ecx
+    EXPORT_PC
+    movl    offThread_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    SPILL(rIBASE)
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINSTbl,%eax
+    sarl    $$4,%eax                          # eax<- B
+    GET_VREG_R %eax %eax                      # eax<- vB (array length)
+    andb    $$0xf,rINSTbl                     # rINST<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail, passing len in eax
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .L${opcode}_finish                # yes, fast path
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+    movl    rSELF,%ecx
+    SPILL_TMP1(%eax)                   # save array length
+    movl    offThread_method(%ecx),%ecx  # ecx<- self->method
+    movzwl  2(rPC),%eax                # eax<- CCCC
+    movl    offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $$0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    movl    %eax,%ecx
+    UNSPILL_TMP1(%eax)
+    testl   %ecx,%ecx                  # successful resolution?
+    je      common_exceptionThrown     # no, bail.
+# fall through to ${opcode}_finish
+
+    /*
+     * Finish allocation
+     *
+     * ecx holds class
+     * eax holds array length (vB)
+     */
+.L${opcode}_finish:
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    movl    $$ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    SET_VREG %eax rINST
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..5638e10
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_INSTANCE.S
@@ -0,0 +1,105 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rIBASE)
+    SPILL_TMP2(%ebx)
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC
+#if defined(WITH_JIT)
+    lea       (%ecx,%eax,4),%ebx        # ebx <- &resolved class
+#endif
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    testl     %ecx,%ecx                 # resolved?
+    je        .L${opcode}_resolve       # no, go do it
+.L${opcode}_resolved:  # on entry, ecx<- class
+    cmpb      $$CLASS_INITIALIZED,offClassObject_status(%ecx)
+    jne       .L${opcode}_needinit
+.L${opcode}_initialized:  # on entry, ecx<- class
+    movl      $$ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+#if defined(WITH_JIT)
+        /*
+     * The JIT needs the class to be fully resolved before it can
+     * include this instruction in a trace.
+     */
+    movl    rSELF, %ecx
+    movl    offThread_subMode(%ecx), %ecx
+    andl    $$kSubModeJitTraceBuild, %ecx # under construction?
+    jne     .L${opcode}_jitCheck
+#endif
+.L${opcode}_end:
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+#if defined(WITH_JIT)
+    /*
+     * Check to see if we need to stop the trace building early.
+     * eax: new object
+     */
+.L${opcode}_jitCheck:
+    cmp     $$0, (%ebx)                   # okay?
+    jne     .L${opcode}_end        # yes, finish
+    SPILL_TMP1(%eax)                     # preserve new object
+    movl    rSELF, %ecx
+    movl    %ecx, OUT_ARG0(%esp)
+    movl    rPC, OUT_ARG1(%esp)
+    call    dvmJitEndTraceSelect         # (self, pc)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%ebx)
+    SET_VREG %eax rINST                  # vAA <- new object
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+#endif
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.L${opcode}_needinit:
+    SPILL_TMP1(%ecx)                    # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP1(%ecx)                  # restore object
+    testl   %eax,%eax                   # success?
+    jne     .L${opcode}_initialized     # success, continue
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.L${opcode}_resolve:
+    movl    rSELF,%ecx
+    movzwl  2(rPC),%eax
+    movl    offThread_method(%ecx),%ecx   # ecx<- self->method
+    movl    %eax,OUT_ARG1(%esp)
+    movl    offMethod_clazz(%ecx),%ecx  # ecx<- method->clazz
+    movl    $$0,OUT_ARG2(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmResolveClass             # call(clazz,off,flags)
+    movl    %eax,%ecx                   # ecx<- resolved ClassObject ptr
+    testl   %ecx,%ecx                   # success?
+    jne     .L${opcode}_resolved        # good to go
+    jmp     common_exceptionThrown      # no, handle exception
diff --git a/vm/mterp/x86/OP_NOP.S b/vm/mterp/x86/OP_NOP.S
new file mode 100644
index 0000000..99fa2b6
--- /dev/null
+++ b/vm/mterp/x86/OP_NOP.S
@@ -0,0 +1,4 @@
+%verify "executed"
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_NOT_INT.S b/vm/mterp/x86/OP_NOT_INT.S
new file mode 100644
index 0000000..c910716
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"notl %eax"}
diff --git a/vm/mterp/x86/OP_NOT_LONG.S b/vm/mterp/x86/OP_NOT_LONG.S
new file mode 100644
index 0000000..54e1c44
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- BA
+    sarl      $$4,%ecx           # ecx<- B
+    andb      $$0xf,rINSTbl      # rINST<- A
+    GET_VREG_WORD %eax %ecx 0    # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1    # ecx<- v[B+1]
+    notl      %eax
+    notl      %ecx
+    SET_VREG_WORD %eax rINST 0   # v[A+0]<- eax
+    FETCH_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1   # v[A+1]<- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_OR_INT.S b/vm/mterp/x86/OP_OR_INT.S
new file mode 100644
index 0000000..9453bfd
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"orl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_2ADDR.S b/vm/mterp/x86/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..db69633
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"orl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT16.S b/vm/mterp/x86/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..fa70e99
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"orl     %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT8.S b/vm/mterp/x86/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..5761806
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"orl     %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG.S b/vm/mterp/x86/OP_OR_LONG.S
new file mode 100644
index 0000000..90f0e3e
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"orl (rFP,%ecx,4),rIBASE", "instr2":"orl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG_2ADDR.S b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..52d740e
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"orl %eax,(rFP,rINST,4)","instr2":"orl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_PACKED_SWITCH.S b/vm/mterp/x86/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..af0df62
--- /dev/null
+++ b/vm/mterp/x86/OP_PACKED_SWITCH.S
@@ -0,0 +1,28 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+    /*
+     * Handle a packed-switch or sparse-switch instruction.  In both cases
+     * we decode it and hand it off to a helper function.
+     *
+     * We don't really expect backward branches in a switch statement, but
+     * they're perfectly legal, so we check for them here.
+     *
+     * for: packed-switch, sparse-switch
+     */
+    /* op vAA, +BBBB */
+    movl    2(rPC),%ecx           # ecx<- BBBBbbbb
+    GET_VREG_R %eax rINST         # eax<- vAA
+    leal    (rPC,%ecx,2),%ecx     # ecx<- PC + BBBBbbbb*2
+    movl    %eax,OUT_ARG1(%esp)   # ARG1<- vAA
+    movl    %ecx,OUT_ARG0(%esp)   # ARG0<- switchData
+    call    $func
+    movl    rSELF,%ecx
+    ADVANCE_PC_INDEXED %eax
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_DOUBLE.S b/vm/mterp/x86/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..d670a31
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    fldl     (rFP,%ecx,4)           # vCC to fp stack
+    fldl     (rFP,%eax,4)           # vCC to fp stack
+    FETCH_INST_OPCODE 2 %ecx
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC 2
+    fstpl    (rFP,rINST,4)           # %st to vAA
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..ee751d0
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    fldl     (rFP,rINST,4)              # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_FLOAT.S b/vm/mterp/x86/OP_REM_FLOAT.S
new file mode 100644
index 0000000..342bf3e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float vAA, vBB, vCC */
+    movzbl   3(rPC),%ecx            # ecx<- BB
+    movzbl   2(rPC),%eax            # eax<- CC
+    flds     (rFP,%ecx,4)           # vCC to fp stack
+    flds     (rFP,%eax,4)           # vCC to fp stack
+    movzbl   rINSTbl,%ecx           # ecx<- AA
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 2 %eax
+    ADVANCE_PC 2
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..1f494bd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINSTbl,%ecx                # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    flds     (rFP,rINST,4)              # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_REM_INT.S b/vm/mterp/x86/OP_REM_INT.S
new file mode 100644
index 0000000..6e4fd04
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_2ADDR.S b/vm/mterp/x86/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..4a11617
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT16.S b/vm/mterp/x86/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..5c4afd6
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT8.S b/vm/mterp/x86/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..53e12ee
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"rIBASE","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG.S b/vm/mterp/x86/OP_REM_LONG.S
new file mode 100644
index 0000000..ad8091c
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG_2ADDR.S b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..f6e6d61
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG_2ADDR.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_RETURN.S b/vm/mterp/x86/OP_RETURN.S
new file mode 100644
index 0000000..082b82e
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    movl    rSELF,%ecx
+    GET_VREG_R %eax rINST               # eax<- vAA
+    movl    %eax,offThread_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_OBJECT.S b/vm/mterp/x86/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..1a2b83e
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_RETURN.S"
diff --git a/vm/mterp/x86/OP_RETURN_VOID.S b/vm/mterp/x86/OP_RETURN_VOID.S
new file mode 100644
index 0000000..4d4291f
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+    jmp       common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_WIDE.S b/vm/mterp/x86/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..c8e7df5
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "self"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    movl    rSELF,%ecx
+    GET_VREG_WORD %eax rINST 0       # eax<- v[AA+0]
+    GET_VREG_WORD rINST rINST 1      # rINST<- v[AA+1]
+    movl    %eax,offThread_retval(%ecx)
+    movl    rINST,4+offThread_retval(%ecx)
+    jmp     common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RSUB_INT.S b/vm/mterp/x86/OP_RSUB_INT.S
new file mode 100644
index 0000000..fa9b410
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"subl %eax,%ecx","result":"%ecx"}
diff --git a/vm/mterp/x86/OP_RSUB_INT_LIT8.S b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..81f49a7
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"subl  %eax,%ecx" , "result":"%ecx"}
diff --git a/vm/mterp/x86/OP_SGET.S b/vm/mterp/x86/OP_SGET.S
new file mode 100644
index 0000000..27d63b1
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET.S
@@ -0,0 +1,53 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SGET handler.
+     *
+     * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%eax
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    SET_VREG %eax rINST
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SGET_BOOLEAN.S b/vm/mterp/x86/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_BYTE.S b/vm/mterp/x86/OP_SGET_BYTE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_CHAR.S b/vm/mterp/x86/OP_SGET_CHAR.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT.S b/vm/mterp/x86/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_SHORT.S b/vm/mterp/x86/OP_SGET_SHORT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_VOLATILE.S b/vm/mterp/x86/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_WIDE.S b/vm/mterp/x86/OP_SGET_WIDE.S
new file mode 100644
index 0000000..be97017
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_WIDE.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * 64-bit SGET handler.
+     *
+     */
+    /* sget-wide vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    movl      offStaticField_value(%eax),%ecx    # ecx<- lsw
+    movl      4+offStaticField_value(%eax),%eax  # eax<- msw
+    SET_VREG_WORD %ecx rINST 0
+    FETCH_INST_OPCODE 2 %ecx
+    SET_VREG_WORD %eax rINST 1
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SHL_INT.S b/vm/mterp/x86/OP_SHL_INT.S
new file mode 100644
index 0000000..a72a272
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sall    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_2ADDR.S b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c8e1dfe
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sall    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_LIT8.S b/vm/mterp/x86/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..8c7f007
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sall  %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_LONG.S b/vm/mterp/x86/OP_SHL_LONG.S
new file mode 100644
index 0000000..0750f80
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shl-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rINST */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # ecx<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- %eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHL_LONG_2ADDR.S b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..b8bbce9
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $$0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $$4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R  %ecx %ecx              # ecx<- vBB
+    shldl     %eax,rIBASE
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,rIBASE
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHR_INT.S b/vm/mterp/x86/OP_SHR_INT.S
new file mode 100644
index 0000000..febc429
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_2ADDR.S b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..89c6625
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_LIT8.S b/vm/mterp/x86/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..77ddd23
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sarl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_LONG.S b/vm/mterp/x86/OP_SHR_LONG.S
new file mode 100644
index 0000000..d79d624
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R   %ecx %ecx              # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $$31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1        # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0          # v[AA+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SHR_LONG_2ADDR.S b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..d380e12
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx         # ecx<- BA
+    andb      $$0xf,rINSTbl        # rINST<- A
+    GET_VREG_WORD %eax rINST 0     # eax<- v[AA+0]
+    sarl      $$4,%ecx             # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1   # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx           # ecx<- vBB
+    shrdl     rIBASE,%eax
+    sarl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    sarl      $$31,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1   # v[AA+1]<- rIBASE
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG_WORD %eax rINST 0    # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SPARSE_SWITCH.S b/vm/mterp/x86/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..4cedd40
--- /dev/null
+++ b/vm/mterp/x86/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/x86/OP_SPUT.S b/vm/mterp/x86/OP_SPUT.S
new file mode 100644
index 0000000..57a1543
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT.S
@@ -0,0 +1,53 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    GET_VREG_R  rINST rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    movl      rINST,offStaticField_value(%eax)
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx        # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
\ No newline at end of file
diff --git a/vm/mterp/x86/OP_SPUT_BOOLEAN.S b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_BYTE.S b/vm/mterp/x86/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_CHAR.S b/vm/mterp/x86/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT.S b/vm/mterp/x86/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..95a0a34
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT.S
@@ -0,0 +1,57 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:                              # field ptr in eax
+    movzbl    rINSTbl,%ecx                       # ecx<- AA
+    GET_VREG_R  %ecx %ecx
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    je        1f                                 # skip card mark if null
+    movl      rSELF,%ecx
+    movl      offField_clazz(%eax),%eax          # eax<- method->clazz
+    movl      offThread_cardTable(%ecx),%ecx       # get card table base
+    shrl      $$GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..afc6668
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_SPUT_SHORT.S b/vm/mterp/x86/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_WIDE.S b/vm/mterp/x86/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..d5825f6
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_WIDE.S
@@ -0,0 +1,55 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * General 32-bit SPUT handler.
+     *
+     * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+     */
+    /* op vAA, field@BBBB */
+    movl      rSELF,%ecx
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offThread_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+#if defined(WITH_JIT)
+    movl      %ecx, TMP_SPILL1(%ebp)
+    lea       (%ecx,%eax,4),%ecx
+    movl      %ecx, TMP_SPILL2(%ebp)
+    movl      TMP_SPILL1(%ebp), %ecx
+#endif
+    movl      (%ecx,%eax,4),%eax                 # eax<- resolved StaticField ptr
+    testl     %eax,%eax                          # resolved entry null?
+    je        .L${opcode}_resolve                # if not, make it so
+.L${opcode}_finish:     # field ptr in eax
+    GET_VREG_WORD %ecx rINST 0                  # rINST<- lsw
+    GET_VREG_WORD rINST rINST 1                 # ecx<- msw
+    movl      %ecx,offStaticField_value(%eax)
+    FETCH_INST_OPCODE 2 %ecx
+    movl      rINST,4+offStaticField_value(%eax)
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offThread_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC                                   # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    SPILL(rIBASE)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rIBASE)
+    testl    %eax,%eax
+    je      common_exceptionThrown             # no, handle exception
+#if defined(WITH_JIT)
+    movl      TMP_SPILL2(%ebp), %ecx
+    SPILL(rIBASE)
+    call     common_verifyField
+    UNSPILL(rIBASE)
+#endif
+    jmp      .L${opcode}_finish                 # success, continue
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE.S b/vm/mterp/x86/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..cf84a65
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE.S
@@ -0,0 +1,14 @@
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+    movzbl   2(rPC),%eax                # eax<- BB
+    movzbl   3(rPC),%ecx                # ecx<- CC
+    # TODO: movsd?
+    movq        (rFP, %eax, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %ecx, 4), %xmm1   # %xmm1<- vCC
+    FETCH_INST_OPCODE 2 %ecx
+    subsd       %xmm1, %xmm0
+    ADVANCE_PC 2
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- vBB - vCC
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..05dce86
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,15 @@
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+    movzx       rINSTbl,%ecx            # ecx<- A+
+    andb        $$0xf,%cl               # ecx<- A
+    sarl        $$4,rINST               # rINST<- B
+    # TODO: movsd?
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    FETCH_INST_OPCODE 1 %eax
+    subsd       %xmm1, %xmm0            # %xmm0<- vA op vB
+    ADVANCE_PC 1
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/OP_SUB_FLOAT.S b/vm/mterp/x86/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..99d11c6
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..013334a
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_INT.S b/vm/mterp/x86/OP_SUB_INT.S
new file mode 100644
index 0000000..04fcf21
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"subl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_INT_2ADDR.S b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..0f63b86
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"subl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_SUB_LONG.S b/vm/mterp/x86/OP_SUB_LONG.S
new file mode 100644
index 0000000..485818c
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"subl (rFP,%ecx,4),rIBASE", "instr2":"sbbl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_LONG_2ADDR.S b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..f2a94ea
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"subl %eax,(rFP,rINST,4)","instr2":"sbbl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/OP_THROW.S b/vm/mterp/x86/OP_THROW.S
new file mode 100644
index 0000000..6559a29
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW.S
@@ -0,0 +1,13 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    EXPORT_PC
+    GET_VREG_R %eax rINST              # eax<- exception object
+    movl     rSELF,%ecx                # ecx<- self
+    testl    %eax,%eax                 # null object?
+    je       common_errNullObject
+    movl     %eax,offThread_exception(%ecx) # thread->exception<- obj
+    jmp      common_exceptionThrown
diff --git a/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..c934bdb
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,16 @@
+%verify executed
+    /*
+     * Handle a throw-verification-error instruction.  This throws an
+     * exception for an error discovered during verification.  The
+     * exception is indicated by AA, with some detail provided by BBBB.
+     */
+    /* op AA, ref@BBBB */
+    movl     rSELF,%ecx
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offThread_method(%ecx),%ecx       # ecx<- self->method
+    EXPORT_PC
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST,OUT_ARG1(%esp)            # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    jmp      common_exceptionThrown          # handle exception
diff --git a/vm/mterp/x86/OP_UNUSED_3E.S b/vm/mterp/x86/OP_UNUSED_3E.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_3F.S b/vm/mterp/x86/OP_UNUSED_3F.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_40.S b/vm/mterp/x86/OP_UNUSED_40.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_41.S b/vm/mterp/x86/OP_UNUSED_41.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_42.S b/vm/mterp/x86/OP_UNUSED_42.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_43.S b/vm/mterp/x86/OP_UNUSED_43.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_73.S b/vm/mterp/x86/OP_UNUSED_73.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_79.S b/vm/mterp/x86/OP_UNUSED_79.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_7A.S b/vm/mterp/x86/OP_UNUSED_7A.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FF.S b/vm/mterp/x86/OP_UNUSED_FF.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_USHR_INT.S b/vm/mterp/x86/OP_USHR_INT.S
new file mode 100644
index 0000000..04b9210
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"shrl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_2ADDR.S b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..02a94ff
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"shrl    %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_LIT8.S b/vm/mterp/x86/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..24fd087
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"shrl     %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_LONG.S b/vm/mterp/x86/OP_USHR_LONG.S
new file mode 100644
index 0000000..4f3647e
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+    /*
+     * Long integer shift.  This is different from the generic 32/64-bit
+     * binary operations because vAA/vBB are 64-bit but vCC (the shift
+     * distance) is 32-bit.  Also, Dalvik requires us to mask off the low
+     * 6 bits of the shift distance.  x86 shifts automatically mask off
+     * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+     * case specially.
+     */
+    /* shr-long vAA, vBB, vCC */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE %eax 1         # rIBASE<- v[BB+1]
+    GET_VREG_R  %ecx %ecx               # ecx<- vCC
+    GET_VREG_WORD %eax %eax 0           # eax<- v[BB+0]
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1          # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[BB+0]<- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_USHR_LONG_2ADDR.S b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..d4396b4
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,29 @@
+%verify "executed"
+    /*
+     * Long integer shift, 2addr version.  vA is 64-bit value/result, vB is
+     * 32-bit shift distance.
+     */
+    /* shl-long/2addr vA, vB */
+    /* ecx gets shift count */
+    /* Need to spill rIBASE */
+    /* rINSTw gets AA */
+    movzbl    rINSTbl,%ecx             # ecx<- BA
+    andb      $$0xf,rINSTbl            # rINST<- A
+    GET_VREG_WORD %eax rINST 0         # eax<- v[AA+0]
+    sarl      $$4,%ecx                 # ecx<- B
+    SPILL(rIBASE)
+    GET_VREG_WORD rIBASE rINST 1       # rIBASE<- v[AA+1]
+    GET_VREG_R %ecx %ecx               # ecx<- vBB
+    shrdl     rIBASE,%eax
+    shrl      %cl,rIBASE
+    testb     $$32,%cl
+    je        2f
+    movl      rIBASE,%eax
+    xorl      rIBASE,rIBASE
+2:
+    SET_VREG_WORD rIBASE rINST 1       # v[AA+1]<- rIBASE
+    FETCH_INST_OPCODE 1 %ecx
+    UNSPILL(rIBASE)
+    SET_VREG_WORD %eax rINST 0         # v[AA+0]<- eax
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/OP_XOR_INT.S b/vm/mterp/x86/OP_XOR_INT.S
new file mode 100644
index 0000000..71e4013
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"xorl   (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_2ADDR.S b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..50880cf
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"xorl     %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT16.S b/vm/mterp/x86/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..e62e4df
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"xor    %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT8.S b/vm/mterp/x86/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..9ccca3f
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"xor    %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG.S b/vm/mterp/x86/OP_XOR_LONG.S
new file mode 100644
index 0000000..3cd5ffb
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"xorl (rFP,%ecx,4),rIBASE", "instr2":"xorl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG_2ADDR.S b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..6d72124
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"xorl %eax,(rFP,rINST,4)","instr2":"xorl %ecx,4(rFP,rINST,4)"}
diff --git a/vm/mterp/x86/alt_stub.S b/vm/mterp/x86/alt_stub.S
new file mode 100644
index 0000000..9807ad1
--- /dev/null
+++ b/vm/mterp/x86/alt_stub.S
@@ -0,0 +1,22 @@
+/*
+ * Inter-instruction transfer stub.  Call out to dvmCheckBefore to handle
+ * any interesting requests and then jump to the real instruction
+ * handler.  Unlike the Arm handler, we can't do this as a tail call
+ * because rIBASE is caller save and we need to reload it.
+ *
+ * Note that unlike in the Arm implementation, we should never arrive
+ * here with a zero breakFlag because we always refresh rIBASE on
+ * return.
+ */
+    EXPORT_PC
+    movl   rSELF, %eax
+    movl   rPC, OUT_ARG0(%esp)
+    cmpb   $$0,offThread_breakFlags(%eax)    # anything to do?
+    movl   rFP, OUT_ARG1(%esp)
+    je     1f                                # reload rIBASE & resume if not
+    movl   %eax, OUT_ARG2(%esp)
+    call   dvmCheckBefore                    # (dPC, dFP, self)
+    movl   rSELF, %eax
+1:
+    movl   offThread_curHandlerTable(%eax), rIBASE # reload rIBASE
+    jmp    *dvmAsmInstructionStart+(${opnum}*4)
diff --git a/vm/mterp/x86/bincmp.S b/vm/mterp/x86/bincmp.S
new file mode 100644
index 0000000..ae68dd3
--- /dev/null
+++ b/vm/mterp/x86/bincmp.S
@@ -0,0 +1,29 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic two-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+     */
+    /* if-cmp vA, vB, +CCCC */
+    movzx    rINSTbl,%ecx          # ecx <- A+
+    andb     $$0xf,%cl             # ecx <- A
+    GET_VREG_R %eax %ecx           # eax <- vA
+    sarl     $$4,rINST             # rINST<- B
+    movl     rSELF,%ecx
+    cmpl     (rFP,rINST,4),%eax    # compare (vA, vB)
+    movl     $$2,%eax              # assume not taken
+    j${revcmp}   1f
+    movswl   2(rPC),%eax           # Get signed branch offset
+1:
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv.S b/vm/mterp/x86/bindiv.S
new file mode 100644
index 0000000..cbd2f3d
--- /dev/null
+++ b/vm/mterp/x86/bindiv.S
@@ -0,0 +1,33 @@
+
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    SPILL(rIBASE)
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindiv2addr.S b/vm/mterp/x86/bindiv2addr.S
new file mode 100644
index 0000000..cc415be
--- /dev/null
+++ b/vm/mterp/x86/bindiv2addr.S
@@ -0,0 +1,33 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINSTbl,%ecx          # eax<- BA
+    SPILL(rIBASE)
+    sarl     $$4,%ecx              # ecx<- B
+    GET_VREG_R %ecx %ecx           # eax<- vBB
+    andb     $$0xf,rINSTbl         # rINST<- A
+    GET_VREG_R %eax rINST          # eax<- vBB
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div2addr
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div2addr
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div2addr:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindivLit16.S b/vm/mterp/x86/bindivLit16.S
new file mode 100644
index 0000000..c935367
--- /dev/null
+++ b/vm/mterp/x86/bindivLit16.S
@@ -0,0 +1,34 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST, ssssCCCC in ecx, vB in eax */
+    movzbl   rINSTbl,%eax         # eax<- 000000BA
+    SPILL(rIBASE)
+    sarl     $$4,%eax             # eax<- B
+    GET_VREG_R %eax %eax          # eax<- vB
+    movswl   2(rPC),%ecx          # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl        # rINST<- A
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/bindivLit8.S b/vm/mterp/x86/bindivLit8.S
new file mode 100644
index 0000000..94adef4
--- /dev/null
+++ b/vm/mterp/x86/bindivLit8.S
@@ -0,0 +1,31 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit div/rem "lit8" binary operation.  Handles special case of
+     * op0=minint & op1=-1
+     */
+    /* div/rem/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax        # eax<- BB
+    movsbl    3(rPC),%ecx        # ecx<- ssssssCC
+    SPILL(rIBASE)
+    GET_VREG_R  %eax %eax        # eax<- rBB
+    cmpl     $$0,%ecx
+    je       common_errDivideByZero
+    cmpl     $$0x80000000,%eax
+    jne      .L${opcode}_continue_div
+    cmpl     $$-1,%ecx
+    jne      .L${opcode}_continue_div
+    movl     $special,$result
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+    SET_VREG $result rINST
+    UNSPILL(rIBASE)
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binflop.S b/vm/mterp/x86/binflop.S
new file mode 100644
index 0000000..a432956
--- /dev/null
+++ b/vm/mterp/x86/binflop.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax          # eax<- CC
+    movzbl   3(rPC),%ecx          # ecx<- BB
+    $load    (rFP,%eax,4)         # vCC to fp stack
+    $instr   (rFP,%ecx,4)         # ex: faddp
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    $store   (rFP,rINST,4)         # %st to vAA
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binflop2addr.S b/vm/mterp/x86/binflop2addr.S
new file mode 100644
index 0000000..c616d8b
--- /dev/null
+++ b/vm/mterp/x86/binflop2addr.S
@@ -0,0 +1,16 @@
+    /*
+     * Generic 32-bit binary float operation.
+     *
+     * For: add-fp, sub-fp, mul-fp, div-fp
+     */
+
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx           # ecx<- A+
+    andb    $$0xf,%cl              # ecx<- A
+    $load    (rFP,%ecx,4)          # vAA to fp stack
+    sarl    $$4,rINST             # rINST<- B
+    $instr   (rFP,rINST,4)         # ex: faddp
+    FETCH_INST_OPCODE 1 %eax
+    ADVANCE_PC 1
+    $store    (rFP,%ecx,4)         # %st to vA
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/binop.S b/vm/mterp/x86/binop.S
new file mode 100644
index 0000000..af2e908
--- /dev/null
+++ b/vm/mterp/x86/binop.S
@@ -0,0 +1,19 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit binary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int, sub-int, and-int, or-int,
+     *      xor-int, shl-int, shr-int, ushr-int
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax   # eax<- BB
+    movzbl   3(rPC),%ecx   # ecx<- CC
+    GET_VREG_R %eax %eax   # eax<- vBB
+    $instr                 # ex: addl    (rFP,%ecx,4),%eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binop1.S b/vm/mterp/x86/binop1.S
new file mode 100644
index 0000000..38c2daf
--- /dev/null
+++ b/vm/mterp/x86/binop1.S
@@ -0,0 +1,15 @@
+%default {"result":"%eax","tmp":"%ecx"}
+    /*
+     * Generic 32-bit binary operation in which both operands loaded to
+     * registers (op0 in eax, op1 in ecx).
+     */
+    /* binop vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    GET_VREG_R %eax %eax            # eax<- vBB
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    $instr                          # ex: addl    %ecx,%eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binop2addr.S b/vm/mterp/x86/binop2addr.S
new file mode 100644
index 0000000..4d30880
--- /dev/null
+++ b/vm/mterp/x86/binop2addr.S
@@ -0,0 +1,20 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = r0 op r1".
+     * This could be an instruction or a function call.
+     *
+     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+     */
+    /* binop/2addr vA, vB */
+    movzx   rINSTbl,%ecx               # ecx<- A+
+    sarl    $$4,rINST                  # rINST<- B
+    GET_VREG_R %eax rINST              # eax<- vB
+    andb    $$0xf,%cl                  # ecx<- A
+    $instr                             # for ex: addl   %eax,(rFP,%ecx,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopLit16.S b/vm/mterp/x86/binopLit16.S
new file mode 100644
index 0000000..4ca6c77
--- /dev/null
+++ b/vm/mterp/x86/binopLit16.S
@@ -0,0 +1,21 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than eax, you can override "result".)
+     *
+     * For: add-int/lit16, rsub-int,
+     *      and-int/lit16, or-int/lit16, xor-int/lit16
+     */
+    /* binop/lit16 vA, vB, #+CCCC */
+    movzbl   rINSTbl,%eax               # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG_R %eax %eax                # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    andb     $$0xf,rINSTbl              # rINST<- A
+    $instr                              # for example: addl %ecx, %eax
+    SET_VREG $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopLit8.S b/vm/mterp/x86/binopLit8.S
new file mode 100644
index 0000000..08e2efd
--- /dev/null
+++ b/vm/mterp/x86/binopLit8.S
@@ -0,0 +1,20 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
+     * that specifies an instruction that performs "result = eax op ecx".
+     * This could be an x86 instruction or a function call.  (If the result
+     * comes back in a register other than r0, you can override "result".)
+     *
+     * For: add-int/lit8, rsub-int/lit8
+     *      and-int/lit8, or-int/lit8, xor-int/lit8,
+     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
+     */
+    /* binop/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    GET_VREG_R   %eax %eax             # eax<- rBB
+    $instr                             # ex: addl %ecx,%eax
+    SET_VREG   $result rINST
+    FETCH_INST_OPCODE 2 %ecx
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopWide.S b/vm/mterp/x86/binopWide.S
new file mode 100644
index 0000000..6114aff
--- /dev/null
+++ b/vm/mterp/x86/binopWide.S
@@ -0,0 +1,18 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rIBASE)                       # save rIBASE
+    GET_VREG_WORD rIBASE %eax 0         # rIBASE<- v[BB+0]
+    GET_VREG_WORD %eax %eax 1           # eax<- v[BB+1]
+    $instr1         # ex: addl   (rFP,%ecx,4),rIBASE
+    $instr2         # ex: adcl   4(rFP,%ecx,4),%eax
+    SET_VREG_WORD rIBASE rINST 0        # v[AA+0] <- rIBASE
+    FETCH_INST_OPCODE 2 %ecx
+    UNSPILL(rIBASE)                     # restore rIBASE
+    SET_VREG_WORD %eax rINST 1          # v[AA+1] <- eax
+    ADVANCE_PC 2
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/binopWide2addr.S b/vm/mterp/x86/binopWide2addr.S
new file mode 100644
index 0000000..fa42644
--- /dev/null
+++ b/vm/mterp/x86/binopWide2addr.S
@@ -0,0 +1,14 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINSTbl,%ecx              # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG_WORD %eax %ecx 0           # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1           # eax<- v[B+1]
+    andb      $$0xF,rINSTbl             # rINST<- A
+    $instr1         # example: addl   %eax,(rFP,rINST,4)
+    $instr2         # example: adcl   %ecx,4(rFP,rINST,4)
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/cvtfp_int.S b/vm/mterp/x86/cvtfp_int.S
new file mode 100644
index 0000000..7590943
--- /dev/null
+++ b/vm/mterp/x86/cvtfp_int.S
@@ -0,0 +1,58 @@
+%default {"srcdouble":"1","tgtlong":"1"}
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint.  If it is less
+ * than minint, it should be clamped to minint.  If it is a nan, the result
+ * should be zero.  Further, the rounding mode is to truncate.  This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+    /* float/double to int/long vA, vB */
+    movzbl    rINSTbl,%ecx       # ecx<- A+
+    sarl      $$4,rINST         # rINST<- B
+    .if $srcdouble
+    fldl     (rFP,rINST,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST,4)       # %st0<- vB
+    .endif
+    ftst
+    fnstcw   LOCAL0_OFFSET(%ebp)      # remember original rounding mode
+    movzwl   LOCAL0_OFFSET(%ebp),%eax
+    movb     $$0xc,%ah
+    movw     %ax,LOCAL0_OFFSET+2(%ebp)
+    fldcw    LOCAL0_OFFSET+2(%ebp)    # set "to zero" rounding mode
+    andb     $$0xf,%cl                # ecx<- A
+    .if $tgtlong
+    fistpll  (rFP,%ecx,4)             # convert and store
+    .else
+    fistpl   (rFP,%ecx,4)             # convert and store
+    .endif
+    fldcw    LOCAL0_OFFSET(%ebp)      # restore previous rounding mode
+    .if $tgtlong
+    movl     $$0x80000000,%eax
+    xorl     4(rFP,%ecx,4),%eax
+    orl      (rFP,%ecx,4),%eax
+    .else
+    cmpl     $$0x80000000,(rFP,%ecx,4)
+    .endif
+    je       .L${opcode}_special_case # fix up result
+
+.L${opcode}_finish:
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
+
+.L${opcode}_special_case:
+    fnstsw   %ax
+    sahf
+    jp       .L${opcode}_isNaN
+    adcl     $$-1,(rFP,%ecx,4)
+    .if $tgtlong
+    adcl     $$-1,4(rFP,%ecx,4)
+    .endif
+   jmp       .L${opcode}_finish
+.L${opcode}_isNaN:
+    movl      $$0,(rFP,%ecx,4)
+    .if $tgtlong
+    movl      $$0,4(rFP,%ecx,4)
+    .endif
+    jmp       .L${opcode}_finish
diff --git a/vm/mterp/x86/entry.S b/vm/mterp/x86/entry.S
new file mode 100644
index 0000000..f97d6a5
--- /dev/null
+++ b/vm/mterp/x86/entry.S
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+
+    .text
+    .global dvmMterpStdRun
+    .type   dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(Thread* self)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp                 # save caller base pointer
+    movl    %esp, %ebp           # set our %ebp
+    movl    rSELF, %ecx          # get incoming rSELF
+/*
+ * At this point we've allocated one slot on the stack
+ * via push and stack is 8-byte aligned.  Allocate space
+ * for 9 spill slots, 4 local slots, 5 arg slots to bring
+ * us to 16-byte alignment
+ */
+    subl    $$(FRAME_SIZE-4), %esp
+
+/* Spill callee save regs */
+    movl    %edi,EDI_SPILL(%ebp)
+    movl    %esi,ESI_SPILL(%ebp)
+    movl    %ebx,EBX_SPILL(%ebp)
+
+/* Set up "named" registers */
+    movl    offThread_pc(%ecx),rPC
+    movl    offThread_curFrame(%ecx),rFP
+    movl    offThread_curHandlerTable(%ecx),rIBASE
+
+    /* Remember %esp for future "longjmp" */
+    movl    %esp,offThread_bailPtr(%ecx)
+
+    /* Fetch next instruction before potential jump */
+    FETCH_INST
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    movl        $$0, offThread_inJitCodeCache(%ecx)
+    cmpl        $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+
+   /* Normal case: start executing the instruction at rPC */
+    GOTO_NEXT
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(Thread* self, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper.  The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ *  esp+4 (arg0)  Thread* self
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab self
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offThread_bailPtr(%ecx),%esp # Restore "setjmp" esp
+    movl    %esp,%ebp
+    addl    $$(FRAME_SIZE-4), %ebp       # Restore %ebp at point of setjmp
+    movl    EDI_SPILL(%ebp),%edi
+    movl    ESI_SPILL(%ebp),%esi
+    movl    EBX_SPILL(%ebp),%ebx
+    movl    %ebp, %esp                   # strip frame
+    pop     %ebp                         # restore caller's ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+#ifdef WITH_JIT
+    .global     dvmNcgInvokeInterpreter
+    .type       dvmNcgInvokeInterpreter, %function
+/* input: start of method in %eax */
+dvmNcgInvokeInterpreter:
+    movl        %eax, rPC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx                    # %edx<- opcode
+    GOTO_NEXT_R %ecx                    # start executing the instruction at rPC
+#endif
+
+/*
+ * Strings
+ */
+    .section    .rodata
+.LstrBadEntryPoint:
+    .asciz  "Bad entry point %d\n"
+
diff --git a/vm/mterp/x86/footer.S b/vm/mterp/x86/footer.S
new file mode 100644
index 0000000..054dc11
--- /dev/null
+++ b/vm/mterp/x86/footer.S
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+#if defined(WITH_JIT)
+/*
+ * JIT-related re-entries into the interpreter.  In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle.  If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
+ */
+
+    .global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction.  We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
+dvmJitToInterpPunt:
+    GET_PC
+#if defined(WITH_JIT_TUNING)
+    movl   rPC, OUT_ARG0(%esp)
+    call   dvmBumpPunt
+#endif
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    movl        $$0, offThread_inJitCodeCache(%ecx)
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+    .global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ *   0(%esp)          <= native return address within trace
+ *   rPC              <= Dalvik PC of this instruction
+ *   OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
+dvmJitToInterpSingleStep:
+/* TODO */
+    call     dvmAbort
+#if 0
+    pop    %eax
+    movl   rSELF, %ecx
+    movl   OUT_ARG0(%esp), %edx
+    movl   %eax,offThread_jitResumeNPC(%ecx)
+    movl   %edx,offThread_jitResumeDPC(%ecx)
+    movl   $$kInterpEntryInstr,offThread_entryPoint(%ecx)
+    movl   $$1,rINST     # changeInterp <= true
+    jmp    common_gotoBail
+#endif
+
+    .global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation.  Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+    SPILL_TMP1(%eax)
+    call   dvmBumpNoChain
+    UNSPILL_TMP1(%eax)
+#endif
+    movl   %eax, rPC
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx                # ecx <- self
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmpl   $$0, %eax
+    jz     1f
+    jmp    *%eax                     # exec translation if we've got one
+    # won't return
+1:
+    EXPORT_PC
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx
+    GOTO_NEXT_R %ecx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation from the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+    movl   %edx, OUT_ARG0(%esp)
+    call   dvmBumpNoChain
+#endif
+    movl   %ebx, rPC
+    lea    4(%esp), %esp #to recover the esp update due to function call
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    movl   rSELF,%ecx
+    cmpl   $$0,%eax
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1f
+    jmp    *%eax              # jump to tranlation
+    # won't return
+
+/* No Translation - request one */
+1:
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmpl   $$0, %eax          # JIT enabled?
+    jnz    2f                 # Request one if so
+    movl   rSELF, %ecx
+    movl   offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST_R %ecx         # Continue interpreting if not
+    GOTO_NEXT_R %ecx
+2:
+    ## Looks like an EXPORT_PC is needed here. Now jmp to common_selectTrace2
+    movl   $$kJitTSelectRequestHot,%eax # ask for trace select
+    jmp    common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target.  Reached via a call, and
+ * (TOS)->rPC.
+ */
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    movl   0(%esp), %eax          # get return address
+    movl   %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    lea    4(%esp), %esp #to recover the esp update due to function call
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea    -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea    -4(%esp), %esp
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   %eax,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread # (pc, self)
+    lea    4(%esp), %esp
+    cmpl   $$0,%eax
+    movl   rSELF, %ecx
+    movl   %eax,offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    jz     1b                 # no - ask for one
+    movl   %eax,OUT_ARG0(%esp)
+    movl   rINST,OUT_ARG1(%esp)
+    call   dvmJitChain        # Attempt dvmJitChain(codeAddr,chainAddr)
+    cmpl   $$0,%eax           # Success?
+    jz     toInterpreter      # didn't chain - interpret
+    jmp    *%eax
+    # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+
+    .global     dvmJitToExceptionThrown
+dvmJitToExceptionThrown: //rPC in
+    movl   rSELF, %edx
+    GET_PC
+    movl   $$0, offThread_inJitCodeCache(%edx)
+    jmp common_exceptionThrown
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+/* one input: the target rPC value */
+    movl        0(%esp), %eax          # get return address
+    movl        %ebx, rPC              # get first argument (target rPC)
+
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+
+    ## An additional 5B instruction "jump 0" was added for a thread-safe
+    ## chaining cell update in JIT code cache. So the offset is now -17=-12-5.
+    lea         -17(%eax), %ebx #$$JIT_OFFSET_CHAIN_START(%eax), %ebx
+    lea         4(%esp), %esp
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    #lea         4(%esp), %esp
+    cmp         $$0, %eax
+    je          toInterpreter
+    #lea         -8(%esp), %esp
+    movl        %ebx, OUT_ARG1(%esp)    # %ebx live thorugh dvmJitGetTraceAddrThread
+    movl        %eax, OUT_ARG0(%esp)    # first argument
+    call        dvmJitChain
+    #lea         8(%esp), %esp
+    cmp         $$0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+dvmJitToInterpNoChain: #rPC in eax
+#if defined(WITH_JIT_TUNING)
+    SPILL_TMP1(%eax)
+    call   dvmBumpNoChain
+    UNSPILL_TMP1(%eax)
+#endif
+    ## TODO, need to clean up stack manipulation ... this isn't signal safe and
+    ## doesn't use the calling conventions of header.S
+    movl        %eax, rPC
+    movl        rPC, OUT_ARG0(%esp)
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    call        dvmJitGetTraceAddrThread
+    ## Here is the change from using rGLUE to rSELF for accessing the
+    ## JIT code cache flag
+    movl        rSELF, %ecx
+    movl        %eax, offThread_inJitCodeCache(%ecx)  # set inJitCodeCache flag
+    cmp         $$0, %eax
+    je          toInterpreter
+    jmp         *%eax                   #to native address
+
+toInterpreter:
+    EXPORT_PC
+    movl        rSELF, %ecx
+    movl        offThread_curHandlerTable(%ecx), rIBASE
+    FETCH_INST
+    movl        offThread_pJitProfTable(%ecx), %eax
+    #Fallthrough
+
+/* ebx holds the pointer to the jit profile table
+   edx has the opCode */
+common_testUpdateProfile:
+    cmp         $$0, %eax
+    je          4f
+/* eax holds the pointer to the jit profile table
+   edx has the opCode
+   rPC points to the next bytecode */
+
+common_updateProfile:
+    # quick & dirty hash
+    movl   rPC, %ecx
+    shrl   $$12, %ecx
+    xorl   rPC, %ecx
+    andl   $$((1<<JIT_PROF_SIZE_LOG_2)-1), %ecx
+    decb   (%ecx,%eax)
+    #jmp    1f # remove
+    jz     2f
+1:
+    GOTO_NEXT
+2:
+common_Profile:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection.  First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+    SPILL(rIBASE)
+    SPILL_TMP1(rINST)
+    movl        rSELF, rIBASE
+    GET_JIT_THRESHOLD rIBASE rINST  # leaves rSELF in %ecx
+    EXPORT_PC
+    movb   rINSTbl,(%ecx,%eax)   # reset counter
+    movl   rIBASE,rINST            # preserve rSELF
+    movl   rSELF, %eax
+    movl   rPC,OUT_ARG0(%esp)
+    movl   rIBASE,OUT_ARG1(%esp)
+    call   dvmJitGetTraceAddrThread  # (pc, self)
+    UNSPILL(rIBASE)
+    movl   %eax,offThread_inJitCodeCache(rINST)   # set the inJitCodeCache flag
+    UNSPILL_TMP1(rINST)
+    cmpl   $$0,%eax
+    #jmp    1f # remove
+    jz     1f
+    jmp   *%eax        # TODO: decide call vs/ jmp!.  No return either way
+1:
+    movl   $$kJitTSelectRequest,%eax
+    # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movzwl      offThread_subMode(%ebx), %ecx
+    and         $$(kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+    movl        %eax, offThread_jitState(%ebx)
+    movl        rSELF, %eax
+    movl       %eax, OUT_ARG0(%esp)
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   mov          EBX_SPILL(%ebp), %ebx
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+common_selectTrace2:
+    mov         %ebx, EBX_SPILL(%ebp)
+    movl        rSELF, %ebx
+    movl        %ebx, OUT_ARG0(%esp)
+    movl        %eax, offThread_jitState(%ebx)
+    movzwl      offThread_subMode(%ebx), %ecx
+    mov         EBX_SPILL(%ebp), %ebx
+    and         (kSubModeJitTraceBuild | kSubModeJitSV), %ecx
+    jne         3f                     # already doing JIT work, continue
+
+
+
+/*
+ * Call out to validate trace-building request. If successful, rIBASE will be swapped
+ * to send us into single-steppign trace building mode, so we need to refresh before
+ * we continue.
+ */
+
+   EXPORT_PC
+   SAVE_PC_FP_TO_SELF %ecx
+   call dvmJitCheckTraceRequest
+3:
+   FETCH_INST
+   movl rSELF, %ecx
+   movl offThread_curHandlerTable(%ecx), rIBASE
+4:
+   GOTO_NEXT
+
+#endif
+
+/*
+ * For the invoke codes we need to know what register holds the "this" pointer. However
+ * it seems the this pointer is assigned consistently most times it is in %ecx but other
+ * times it is in OP_INVOKE_INTERFACE, OP_INVOKE_SUPER_QUICK, or OP_INVOKE_VIRTUAL_QUICK.
+*/
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = "this"
+ *   rINSTw trashed, must reload
+ *   rIBASE trashed, must reload before resuming interpreter
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $$kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST       # rINST<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+    test        rINST, rINST
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+
+
+   /*
+    * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count,
+    * %edx=&outs (&stackSaveArea).  (very few methods have > 10 args;
+    * could unroll for common cases)
+    */
+
+.LinvokeRangeArgs:
+    movl        %ebx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- save %ebx
+    lea         (rFP, %ecx, 4), %ecx    # %ecx<- &vCCCC
+    shll        $$2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+    subl        LOCAL0_OFFSET(%ebp), %edx       # %edx<- update &outs
+    shrl        $$2, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- offset
+1:
+    movl        (%ecx), %ebx            # %ebx<- vCCCC
+    lea         4(%ecx), %ecx           # %ecx<- &vCCCC++
+    subl        $$1, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+    movl        %ebx, (%edx)            # *outs<- vCCCC
+    lea         4(%edx), %edx           # outs++
+    jne         1b                      # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+    movl        LOCAL1_OFFSET(%ebp), %ebx       # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    * rIBASE trashed, must reload before resuming interpreter
+    */
+
+common_invokeMethodNoRange:
+#if defined(WITH_JIT)
+    SPILL_TMP1(%edx)
+    SPILL_TMP2(%ebx)
+    movl        rSELF, %edx
+    movzwl      offThread_subMode(%edx), %ebx
+    and         $$kSubModeJitTraceBuild, %ebx
+    jz          6f
+    call        save_callsiteinfo
+6:
+    UNSPILL_TMP2(%ebx)
+    UNSPILL_TMP1(%edx)
+#endif
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST       # rINST<- BA
+    movl        rINST, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+    shrl        $$4, LOCAL0_OFFSET(%ebp)        # LOCAL0_OFFSET(%ebp)<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    movzwl      4(rPC), %ecx            # %ecx<- GFED
+    SAVEAREA_FROM_FP %edx               # %edx<- &StackSaveArea
+
+   /*
+    * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $$2, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 2
+    movl        %ecx, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $$4, LOCAL0_OFFSET(%ebp)        # compare LOCAL0_OFFSET(%ebp) to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $$15, rINST             # rINSTw<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, (%edx)            # *outs<- vA
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+4:
+    shr         $$12, %ecx              # %ecx<- G
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vG
+    movl        %ecx, (%edx)            # *outs<- vG
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+3:
+    and         $$0x0f00, %ecx          # %ecx<- 0F00
+    shr         $$8, %ecx               # %ecx<- F
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vF
+    movl        %ecx, (%edx)            # *outs<- vF
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+2:
+    and         $$0x00f0, %ecx          # %ecx<- 00E0
+    shr         $$4, %ecx               # %ecx<- E
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vE
+    movl        %ecx, (%edx)            # *outs<- vE
+    movl        LOCAL1_OFFSET(%ebp), %ecx       # %ecx<- GFED
+1:
+    and         $$0x000f, %ecx          # %ecx<- 000D
+    movl        (rFP, %ecx, 4), %ecx    # %ecx<- vD
+    movl        %ecx, -4(%edx)          # *--outs<- vD
+0:
+
+   /*
+    * %eax is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+    movl        %eax, LOCAL0_OFFSET(%ebp)       # LOCAL0_OFFSET<- methodToCall
+    shl         $$2, %edx               # %edx<- update offset
+    SAVEAREA_FROM_FP %eax               # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    movl        rSELF,%edx              # %edx<- pthread
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offThread_interpStackEnd(%edx), %edx # %edx<- self->interpStackEnd
+    movl        %edx, TMP_SPILL1(%ebp)  # spill self->interpStackEnd
+    shl         $$2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         TMP_SPILL1(%ebp), %eax  # compare interpStackEnd and bottom
+    movl        LOCAL0_OFFSET(%ebp), %eax       # %eax<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rSELF,%ecx              # %ecx<- pthread
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+#if defined(WITH_JIT)
+    movl        $$0, offStackSaveArea_returnAddr(%edx)
+#endif
+
+    /* Any special actions to take? */
+    cmpw        $$0, offThread_subMode(%ecx)
+    jne         2f                     # Yes - handle them
+1:
+    testl       $$ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+    movl        %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "self" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offThread_method(%ecx) # self->method<- methodToCall
+    movl        %edx, offThread_methodClassDex(%ecx) # self->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        $$1, offThread_debugIsMethodEntry(%ecx)
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # curFrame<-newFP
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    FETCH_INST
+#if defined(WITH_JIT)
+    /* rPC is already updated */
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+2:
+    /*
+     * On entry, preserve all:
+     *  %eax: method
+     *  %ecx: self
+     *  %edx: new save area
+     */
+    SPILL_TMP1(%eax)                   # preserve methodToCall
+    SPILL_TMP2(%edx)                   # preserve newSaveArea
+    movl        rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl        %ecx, OUT_ARG0(%esp)
+    movl        %eax, OUT_ARG1(%esp)
+    call        dvmReportInvoke        # (self, method)
+    UNSPILL_TMP1(%eax)
+    UNSPILL_TMP2(%edx)
+    movl        rSELF,%ecx             # restore rSELF
+    jmp         1b
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea, %ecx=self
+    */
+
+.LinvokeNative:
+    movl        offThread_jniLocal_topCookie(%ecx), rINST # rINST<- self->localRef->...
+    movl        rINST, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, LOCAL2_OFFSET(%ebp)  # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), rINST # rINST<- newFP
+    movl        rINST, offThread_curFrame(%ecx)  # curFrame<- newFP
+    cmpw        $$0, offThread_subMode(%ecx)  # Anything special going on?
+    jne         11f                     # yes - handle it
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)    # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+7:
+    movl        LOCAL2_OFFSET(%ebp), %ecx    # %ecx<- newSaveArea
+    movl        rSELF, %eax             # %eax<- self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $$0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    jne         common_exceptionThrown  # handle exception
+    movl        offThread_curHandlerTable(%eax),rIBASE
+    FETCH_INST_OPCODE 3 %ecx
+    ADVANCE_PC 3
+    GOTO_NEXT_R %ecx                    # jump to next instruction
+
+11:
+    /*
+     * Handle any special subMode actions
+     * %eax=methodToCall, rINST=newFP, %ecx=self
+     */
+    SPILL_TMP1(%eax)                    # save methodTocall
+    movl        rPC, offThread_pc(%ecx)
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPreNativeInvoke # (methodToCall, self, fp)
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF,%ecx              # restore self
+
+    /* Do the native call */
+    movl        %ecx, OUT_ARG3(%esp)    # push parameter self
+    lea         offThread_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %eax, OUT_ARG2(%esp)    # push parameter methodToCall
+    movl        %ecx, OUT_ARG1(%esp)    # push parameter &retval
+    movl        rINST, OUT_ARG0(%esp)   # push parameter newFP
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+
+    UNSPILL_TMP1(%eax)                  # restore methodToCall
+    movl        rSELF, %ecx
+    movl        %ecx, OUT_ARG1(%esp)
+    movl        %eax, OUT_ARG0(%esp)
+    movl        rFP, OUT_ARG2(%esp)
+    call        dvmReportPostNativeInvoke # (methodToCall, self, fp)
+    jmp         7b                      # rejoin
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        rSELF,%eax              # %eax<- self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    movl    rSELF, %ecx
+    SAVEAREA_FROM_FP %eax                       # %eax<- saveArea(old)
+    cmpw    $$0, offThread_subMode(%ecx)          # special action needed?
+    jne     19f                                   # go if so
+14:
+
+    movl        offStackSaveArea_prevFrame(%eax), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), rINST # rINST<- method we are returning to
+    cmpl        $$0, rINST               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offThread_curHandlerTable(%ecx),rIBASE
+    movl        offStackSaveArea_savedPc(%eax), rPC # rPC<- saveAreaOld->savedPc
+#if defined(WITH_JIT)
+    movl        offStackSaveArea_returnAddr(%eax), %ecx
+#endif
+    movl        rSELF, %eax
+    movl        rINST, offThread_method(%eax) # glue->method<- newSave->method
+    movl        offMethod_clazz(rINST), rINST # rINST<- method->clazz
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+#if defined(WITH_JIT)
+    //update self->offThread_inJitCodeCache
+    movl        %ecx, offThread_inJitCodeCache(%eax)
+#endif
+    movl        offClassObject_pDvmDex(rINST), rINST # rINST<- method->clazz->pDvmDex
+    movl        rINST, offThread_methodClassDex(%eax) # glue->pDvmDex<- method->clazz->pDvmDex
+#if defined(WITH_JIT)
+    cmp         $$0, %ecx
+    je          .returnToBC
+    movl        %ecx, %eax
+    jmp         *%eax
+#endif
+
+.returnToBC:
+
+#if defined(WITH_JIT)
+    FETCH_INST_OPCODE  3, %ecx                 # %eax<- next instruction hi; fetch, advance
+    // %ecx has the opcode
+    addl         $$6, rPC               # 3*2 = 6
+    SPILL_TMP1   (%ecx)
+    movl         rSELF, %ecx
+    FETCH_INST
+    UNSPILL_TMP1   (%ecx)
+    movzbl      1(rPC), rINST
+    jmp     *(rIBASE,%ecx,4)
+#else
+    FETCH_INST_WORD 3
+    ADVANCE_PC 3
+    GOTO_NEXT
+#endif
+
+19:
+    /*
+     * Handle special subMode actions
+     * On entry, rFP: prevFP, %ecx: self, %eax: saveArea
+     */
+    SPILL_TMP1(%ebx)
+    movl     offStackSaveArea_prevFrame(%eax), %ebx # %ebx<- saveArea->PrevFrame
+    movl     rPC, offThread_pc(%ecx)          # update interpSave.pc
+    movl     %ebx, offThread_curFrame(%ecx)    # update interpSave.curFrame
+    movl     %ecx, OUT_ARG0(%esp)             # parameter self
+    call     dvmReportReturn                  # (self)
+    UNSPILL_TMP1(%ebx)
+    movl     rSELF, %ecx                      # restore self
+    SAVEAREA_FROM_FP %eax                     # restore saveArea
+    jmp      14b
+
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST holds changeInterp
+ *    ecx holds self pointer
+ *
+ * expected profile: dvmMterpStdBail(Thread *self, bool changeInterp)
+ */
+common_gotoBail:
+    movl   rPC,offThread_pc(%ecx)     # export state to self
+    movl   rFP,offThread_curFrame(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)      # self in arg0
+    movl   rINST,OUT_ARG1(%esp)     # changeInterp in arg1
+    call   dvmMterpStdBail          # bail out....
+
+/*
+ * The JIT's invoke method needs to remember the callsite class and
+ * target pair.  Save them here so that they are available to
+ * dvmCheckJit following the interpretation of this invoke.
+ *
+ * eax = Method* methodToCall
+ * ecx = "this"
+ * edx = rSELF
+ * ebx = free to use
+ */
+#if defined(WITH_JIT)
+save_callsiteinfo:
+    cmp     $$0, %ecx
+    je      2f
+    movl    offObject_clazz(%ecx), %ecx
+2:
+    movl    rSELF, %ebx
+    movl    %eax, offThread_methodToCall(%ebx)
+    movl    %ecx, offThread_callsiteClass(%ebx)
+    ret
+#endif
+
+#if defined(WITH_JIT)
+
+    /*
+     * If the JIT is actively building a trace we need to make sure
+     * that the field is fully resolved before including the current
+     * instruction.
+     *
+     * On entry:
+     *     %ecx: &dvmDex->pResFields[field]
+     *     %eax:  field pointer (must preserve)
+     */
+common_verifyField:
+    movl    %ebx, TMP_SPILL1(%ebp)
+    movl     rSELF, %ebx
+    movzwl   offThread_subMode(%ebx), %ebx
+    andl     $$kSubModeJitTraceBuild, %ebx
+    movl    TMP_SPILL1(%ebp), %ebx
+    jne      1f
+    ret
+1:
+    movl    (%ecx), %ecx
+    cmp     $$0, %ecx
+    je      1f
+    ret
+1:
+    SPILL_TMP1(%eax)
+    SPILL_TMP2(%edx)
+    movl     rSELF, %ecx
+    # Because we call into this helper from a bytecode, we have
+    # to be careful not to write over the return address when using
+    # the OUT_ARG macros
+    lea      -8(%esp), %esp
+    movl     %ecx, OUT_ARG0(%esp)
+    movl     rPC, OUT_ARG1(%esp)
+    call     dvmJitEndTraceSelect
+    lea      8(%esp), %esp
+    UNSPILL_TMP2(%edx)
+    UNSPILL_TMP1(%eax)
+    ret
+#endif
+
+/*
+ * After returning from a "selfd" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+     movl  rSELF, %eax
+     movl  offThread_pc(%eax),rPC
+     movl  offThread_curFrame(%eax),rFP
+     movl  offThread_curHandlerTable(%eax),rIBASE
+     FETCH_INST
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC
+    movl    $$.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowArithmeticException
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, len in eax
+ */
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)                  # arg0<- len
+    call    dvmThrowNegativeArraySizeException   # (len)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry, method name in eax
+ */
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNoSuchMethodError
+    jmp     common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one.  Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+    EXPORT_PC
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmThrowNullPointerException
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ * On entry:
+ *    eax <- array object
+ *    ecx <- index
+ */
+common_errArrayIndex:
+    EXPORT_PC
+    movl    offArrayObject_length(%eax), %eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmThrowArrayIndexOutOfBoundsException   # args (length, index)
+    jmp     common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception.  Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * NOTE: special subMode handling done in dvmMterp_exceptionThrown
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+.LexceptionNew:
+
+    EXPORT_PC
+    movl       rSELF, %ecx
+    movl       %ecx, OUT_ARG0(%esp)
+    call       dvmCheckSuspendPending
+
+    movl       rSELF, %ecx
+    movl       offThread_exception(%ecx), %edx   # %edx <- self->exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmAddTrackedAlloc      # don't let the exception be GCed
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+    movl       offThread_subMode(%ecx), %eax    # get subMode flags
+    movl       $$0, offThread_exception(%ecx)
+
+    # Special subMode?
+    cmpl       $$0, %eax                # any special subMode handling needed?
+    je         8f                      # go if so
+
+    # Manage debugger bookkeeping
+    movl       rPC, offThread_pc(%ecx) # update interpSave.pc
+    movl       rFP, offThread_curFrame(%ecx) # update interpSave.curFrame
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmReportExceptionThrow # (self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rSELF, %ecx
+
+8:
+    /*
+    * set up args and a local for &fp
+    */
+    lea        20(%esp), %esp          # raise %esp
+    movl       rFP, (%esp)               # save fp
+    movl       %esp, %eax              # %eax = &fp
+    lea        -20(%esp), %esp         # reset %esp
+    movl       %eax, OUT_ARG4(%esp)    # Arg 4 = &fp
+    movl       $$0, OUT_ARG3(%esp)      # Arg 3 = false
+    movl       %edx, OUT_ARG2(%esp)    # Arg 2 = exception
+    movl       %ecx, OUT_ARG0(%esp)    # Arg 0 = self
+
+    movl       offThread_method(%ecx), %eax # %eax = self->method
+    movl       offMethod_insns(%eax), %eax  # %eax = self->method->insn
+    movl       rPC, %ecx
+    subl       %eax, %ecx              # %ecx = pc - self->method->insn
+    sar        $$1, %ecx                # adjust %ecx for code offset
+    movl       %ecx, OUT_ARG1(%esp)    # Arg 1 = %ecx
+
+    /* call, %eax gets catchRelPc (a code-unit offset) */
+    SPILL_TMP1(%edx)                   # save exception
+    call       dvmFindCatchBlock       # call(self, relPc, exc, scan?, &fp)
+    UNSPILL_TMP1(%edx)                 # restore exception
+
+    /* fix earlier stack overflow if necessary; may trash rFP */
+    movl       rSELF, %ecx
+    cmpl       $$0, offThread_stackOverflowed(%ecx) # did we overflow?
+    je         1f                         # no, skip ahead
+    movl       %eax, rFP                  # save relPc result in rFP
+    movl       %ecx, OUT_ARG0(%esp)       # Arg 0 = self
+    movl       %edx, OUT_ARG1(%esp)       # Arg 1 = exception
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow    # call(self, exception)
+    UNSPILL_TMP1(%edx)
+    movl       rFP, %eax                  # restore result
+    movl       rSELF, %ecx
+1:
+
+    /* update frame pointer and check result from dvmFindCatchBlock */
+    movl       20(%esp), rFP              # retrieve the updated rFP
+    cmpl       $$0, %eax                  # is catchRelPc < 0?
+    jl         .LnotCaughtLocally
+
+    /* adjust locals to match self->interpSave.curFrame and updated PC */
+    SAVEAREA_FROM_FP rINST             # rINST<- new save area
+    movl       offStackSaveArea_method(rINST), rINST # rINST<- new method
+    movl       rINST, offThread_method(%ecx)         # self->method = new method
+    movl       offMethod_clazz(rINST), %ecx          # %ecx = method->clazz
+    movl       offMethod_insns(rINST), rINST         # rINST = method->insn
+    movl       offClassObject_pDvmDex(%ecx), %ecx    # %ecx = method->clazz->pDvmDex
+    lea        (rINST, %eax, 2), rPC      # rPC<- method->insns + catchRelPc
+    movl       rSELF, rINST
+    movl       %ecx, offThread_methodClassDex(rINST) # self->pDvmDex = method->clazz->pDvmDex
+
+    /* release the tracked alloc on the exception */
+    movl       %edx, OUT_ARG0(%esp)       # Arg 0 = exception
+    movl       rINST, OUT_ARG1(%esp)      # Arg 1 = self
+    SPILL_TMP1(%edx)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    UNSPILL_TMP1(%edx)
+
+    /* restore the exception if the handler wants it */
+    movl       rSELF, %ecx
+    FETCH_INST
+    movzbl     rINSTbl, %eax
+    cmpl       $$OP_MOVE_EXCEPTION, %eax   # is it "move-exception"?
+    jne        1f
+    movl       %edx, offThread_exception(%ecx) # restore exception
+1:
+    movl       offThread_curHandlerTable(%ecx), rIBASE # refresh rIBASE
+    GOTO_NEXT
+
+.LnotCaughtLocally: # %edx = exception
+    /* fix stack overflow if necessary */
+    movl       rSELF, %ecx
+    movl       offThread_stackOverflowed(%ecx), %eax
+    cmpl       $$0, %eax                   # did we overflow earlier?
+    je         1f
+    movl       %ecx, OUT_ARG0(%esp)
+    movl       %edx, OUT_ARG1(%esp)
+    SPILL_TMP1(%edx)
+    call       dvmCleanupStackOverflow
+    UNSPILL_TMP1(%edx)
+
+1:
+    movl       rSELF, %ecx
+    movl       %edx, offThread_exception(%ecx) #restore exception
+    movl       %edx, OUT_ARG0(%esp)
+    movl       %ecx, OUT_ARG1(%esp)
+    call       dvmReleaseTrackedAlloc     # release the exception
+    movl       rSELF, %ecx
+    jmp        common_gotoBail            # bail out
+
+common_abort:
+    movl    $$0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImplA:
+    .asciz  "filled-new-array only implemented for 'int'"
diff --git a/vm/mterp/x86/fpcvt.S b/vm/mterp/x86/fpcvt.S
new file mode 100644
index 0000000..983b9eb
--- /dev/null
+++ b/vm/mterp/x86/fpcvt.S
@@ -0,0 +1,14 @@
+%default {"instr":"","load":"","store":""}
+    /*
+     * Generic 32-bit FP conversion operation.
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx       # ecx<- A+
+    sarl     $$4,rINST         # rINST<- B
+    $load    (rFP,rINST,4)      # %st0<- vB
+    andb     $$0xf,%cl          # ecx<- A
+    $instr
+    $store  (rFP,%ecx,4)        # vA<- %st0
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/header.S b/vm/mterp/x86/header.S
new file mode 100644
index 0000000..16f3d98
--- /dev/null
+++ b/vm/mterp/x86/header.S
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+   eax, edx, ecx, st(0)-st(7)
+Callee save set:
+   ebx, esi, edi, ebp
+Return regs:
+   32-bit in eax
+   64-bit in edx:eax (low-order 32 in eax)
+   fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left.  On entry to target, first
+parm is at 4(%esp).  Traditional entry code is:
+
+functEntry:
+    push    %ebp             # save old frame pointer
+    mov     %ebp,%esp        # establish new frame pointer
+    sub     FrameSize,%esp   # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Stack must be 16-byte aligned to support SSE in native code.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers.  Note that each
+will also have an associated spill location (mostly useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      esi   interpreted program counter, used for fetching instructions
+  rFP      edi   interpreted frame pointer, used for accessing locals and args
+  rINSTw   bx    first 16-bit code of current instruction
+  rINSTbl  bl    opcode portion of instruction word
+  rINSTbh  bh    high byte of inst word, usually contains src/tgt reg names
+  rIBASE   edx   base of instruction handler table
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
+   o eax and ecx are scratch, rINSTw/ebx sometimes scratch
+
+*/
+
+#define rSELF    8(%ebp)
+#define rPC      %esi
+#define rFP      %edi
+#define rINST    %ebx
+#define rINSTw   %bx
+#define rINSTbh  %bh
+#define rINSTbl  %bl
+#define rIBASE   %edx
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0)
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EBX_SPILL      (-12)
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rINST_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define TMP_SPILL1     (-32)
+#define TMP_SPILL2     (-36)
+#define TMP_SPILL3     (-20)
+#define LOCAL0_OFFSET  (-44)
+#define LOCAL1_OFFSET  (-48)
+#define LOCAL2_OFFSET  (-52)
+/* Out Arg offsets, relative to %esp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+#if defined(WITH_JIT)
+/* for spill region: increase size by 48 (to keep 16-byte alignment) */
+/* 76 + 48 = 124 */
+#define JIT_SPILL      (-56)
+#define FRAME_SIZE     124
+#else
+#define FRAME_SIZE     76
+#endif
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP1(reg) movl reg,TMP_SPILL1(%ebp)
+#define UNSPILL_TMP1(reg) movl TMP_SPILL1(%ebp),reg
+#define SPILL_TMP2(reg) movl reg,TMP_SPILL2(%ebp)
+#define UNSPILL_TMP2(reg) movl TMP_SPILL2(%ebp),reg
+#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
+#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _self _reg
+    movl    offThread_pJitProfTable(\_self),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _self _reg
+    movl    offThread_jitThreshold(\_self),\_reg
+.endm
+#endif
+
+/* save/restore the PC and/or FP from the self struct */
+.macro SAVE_PC_FP_TO_SELF _reg
+    movl     rSELF,\_reg
+    movl     rPC,offThread_pc(\_reg)
+    movl     rFP,offThread_curFrame(\_reg)
+.endm
+
+.macro LOAD_PC_FP_FROM_SELF
+    movl    rSELF,rFP
+    movl    offThread_pc(rFP),rPC
+    movl    offThread_curFrame(rFP),rFP
+.endm
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects.  Must be done *before* something throws.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+.macro EXPORT_PC
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+.endm
+
+.macro GET_PC
+    movl     (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP), rPC
+.endm
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+.macro SAVEAREA_FROM_FP _reg
+    leal    -sizeofStackSaveArea(rFP), \_reg
+.endm
+
+/*
+ * Fetch the next instruction from rPC into rINSTw.  Does not advance rPC.
+ */
+.macro FETCH_INST
+    movzwl    (rPC),rINST
+.endm
+
+/*
+ * Fetch the opcode byte and zero-extend it into _reg.  Must be used
+ * in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_R _reg
+    movzbl    (rPC),\_reg
+.endm
+
+/*
+ * Fetch the opcode byte at _count words offset from rPC and zero-extend
+ * it into _reg.  Must be used in conjunction with GOTO_NEXT_R
+ */
+.macro FETCH_INST_OPCODE _count _reg
+    movzbl  \_count*2(rPC),\_reg
+.endm
+
+/*
+ * Fetch the nth instruction word from rPC into rINSTw.  Does not advance
+ * rPC, and _count is in words
+ */
+.macro FETCH_INST_WORD _count
+    movzwl  \_count*2(rPC),rINST
+.endm
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+.macro FETCH_INST_INDEXED _reg
+    movzwl  (rPC,\_reg,2),rINST
+.endm
+
+/*
+ * Advance rPC by instruction count
+ */
+.macro ADVANCE_PC _count
+    leal  2*\_count(rPC),rPC
+.endm
+
+/*
+ * Advance rPC by branch offset in register
+ */
+.macro ADVANCE_PC_INDEXED _reg
+    leal (rPC,\_reg,2),rPC
+.endm
+
+.macro GOTO_NEXT
+     movzx   rINSTbl,%eax
+     movzbl  rINSTbh,rINST
+     jmp     *(rIBASE,%eax,4)
+.endm
+
+   /*
+    * Version of GOTO_NEXT that assumes _reg preloaded with opcode.
+    * Should be paired with FETCH_INST_R
+    */
+.macro GOTO_NEXT_R _reg
+     movzbl  1(rPC),rINST
+     jmp     *(rIBASE,\_reg,4)
+.endm
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+.macro GET_VREG_R _reg _vreg
+    movl     (rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG _reg _vreg
+    movl     \_reg,(rFP,\_vreg,4)
+.endm
+
+.macro GET_VREG_WORD _reg _vreg _offset
+    movl     4*(\_offset)(rFP,\_vreg,4),\_reg
+.endm
+
+.macro SET_VREG_WORD _reg _vreg _offset
+    movl     \_reg,4*(\_offset)(rFP,\_vreg,4)
+.endm
+
+#define sReg0 LOCAL0_OFFSET(%ebp)
+#define sReg1 LOCAL1_OFFSET(%ebp)
+#define sReg2 LOCAL2_OFFSET(%ebp)
+
+   /*
+    * x86 JIT Helpers
+    */
+
+    .macro dumpSwitch _regData _regScratch1 _regScratch2
+    .endm
+
+   /*
+    * Hard coded helper values.
+    */
+
+.balign 16
+
+.LdoubNeg:
+    .quad       0x8000000000000000
+
+.L64bits:
+    .quad       0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+    .quad       0x0000000000000000
+.LshiftMask:
+    .quad       0x000000000000003F
+
+.Lvalue64:
+    .quad       0x0000000000000040
+
+.LvaluePosInfLong:
+    .quad       0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+    .quad       0x8000000000000000
+
+.LvalueNanLong:
+    .quad       0x0000000000000000
+
+.LintMin:
+.long   0x80000000
+
+.LintMax:
+.long   0x7FFFFFFF
+
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/x86/shop2addr.S b/vm/mterp/x86/shop2addr.S
new file mode 100644
index 0000000..c891259
--- /dev/null
+++ b/vm/mterp/x86/shop2addr.S
@@ -0,0 +1,15 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINSTbl,%ecx           # eax<- BA
+    sarl     $$4,%ecx               # ecx<- B
+    GET_VREG_R %ecx %ecx            # eax<- vBB
+    andb     $$0xf,rINSTbl          # rINST<- A
+    GET_VREG_R %eax rINST           # eax<- vAA
+    $instr                          # ex: sarl %cl,%eax
+    FETCH_INST_OPCODE 1 %ecx
+    SET_VREG $result rINST
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/stub.S b/vm/mterp/x86/stub.S
new file mode 100644
index 0000000..fb5c977
--- /dev/null
+++ b/vm/mterp/x86/stub.S
@@ -0,0 +1,9 @@
+    /* (stub) */
+    SAVE_PC_FP_TO_SELF %ecx          # leaves rSELF in %ecx
+    movl %ecx,OUT_ARG0(%esp)         # self is first arg to function
+    call      dvmMterp_${opcode}     # do the real work
+    movl      rSELF,%ecx
+    LOAD_PC_FP_FROM_SELF             # retrieve updated values
+    movl      offThread_curHandlerTable(%ecx),rIBASE  # set up rIBASE
+    FETCH_INST
+    GOTO_NEXT
diff --git a/vm/mterp/x86/unop.S b/vm/mterp/x86/unop.S
new file mode 100644
index 0000000..ad9c79b
--- /dev/null
+++ b/vm/mterp/x86/unop.S
@@ -0,0 +1,17 @@
+%default {"pre0":"","pre1":""}
+    /*
+     * Generic 32-bit unary operation.  Provide an "instr" line that
+     * specifies an instruction that performs "result = op eax".
+     */
+    /* unop vA, vB */
+    movzbl   rINSTbl,%ecx           # ecx<- A+
+    sarl     $$4,rINST             # rINST<- B
+    GET_VREG_R %eax rINST           # eax<- vB
+    andb     $$0xf,%cl              # ecx<- A
+    $pre0
+    $pre1
+    $instr
+    SET_VREG %eax %ecx
+    FETCH_INST_OPCODE 1 %ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %ecx
diff --git a/vm/mterp/x86/unopWide.S b/vm/mterp/x86/unopWide.S
new file mode 100644
index 0000000..41b5d10
--- /dev/null
+++ b/vm/mterp/x86/unopWide.S
@@ -0,0 +1,21 @@
+%default {"instr1":"","instr2":"","instr3":""}
+    /*
+     * Generic 64-bit unary operation.
+     * Operand in %ecx:%eax
+     *
+     * For: neg-long, not-long
+     */
+    /* unop vA, vB */
+    movzbl    rINSTbl,%ecx            # ecx<- BA
+    sarl      $$4,%ecx                # ecx<- B
+    andb      $$0xf,rINSTbl           # rINST<- A
+    GET_VREG_WORD %eax %ecx 0         # eax<- v[B+0]
+    GET_VREG_WORD %ecx %ecx 1         # ecx<- v[B+1]
+    $instr1   # ex: negl %eax
+    $instr2   # ex: adcl $$0,%ecx
+    $instr3   # ex: negl %ecx
+    SET_VREG_WORD %eax rINST 0        # v[A+0] <- eax
+    GET_INST_OPCODE 1 %eax
+    SET_VREG_WORD %ecx rINST 1        # v[A+1] <- ecx
+    ADVANCE_PC 1
+    GOTO_NEXT_R %eax
diff --git a/vm/mterp/x86/unused.S b/vm/mterp/x86/unused.S
new file mode 100644
index 0000000..f0f117c
--- /dev/null
+++ b/vm/mterp/x86/unused.S
@@ -0,0 +1 @@
+    jmp     common_abort
diff --git a/vm/mterp/x86/zcmp.S b/vm/mterp/x86/zcmp.S
new file mode 100644
index 0000000..e9fc7d4
--- /dev/null
+++ b/vm/mterp/x86/zcmp.S
@@ -0,0 +1,25 @@
+%verify "branch taken"
+%verify "branch not taken"
+    /*
+     * Generic one-operand compare-and-branch operation.  Provide a "revcmp"
+     * fragment that specifies the *reverse* comparison to perform, e.g.
+     * for "if-le" you would use "gt".
+     *
+     * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+     */
+    /* if-cmp vAA, +BBBB */
+    cmpl     $$0,(rFP,rINST,4)     # compare (vA, 0)
+    movl     rSELF,%ecx
+    movl     $$2,%eax              # assume branch not taken
+    j${revcmp}   1f
+    movswl   2(rPC),%eax           # fetch signed displacement
+    movl     offThread_curHandlerTable(%ecx),rIBASE
+1:
+    FETCH_INST_INDEXED %eax
+    ADVANCE_PC_INDEXED %eax
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE %ecx %eax
+    cmp         $$0, %eax
+    jne         common_updateProfile # set up %ebx & %edx & rPC
+#endif
+    GOTO_NEXT
diff --git a/vm/native/InternalNative.cpp b/vm/native/InternalNative.cpp
new file mode 100644
index 0000000..97a2737
--- /dev/null
+++ b/vm/native/InternalNative.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Internal-native initialization and some common utility functions.
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Set of classes for which we provide methods.
+ *
+ * The last field, classNameHash, is filled in at startup.
+ */
+static DalvikNativeClass gDvmNativeMethodSet[] = {
+    { "Ljava/lang/Object;",               dvm_java_lang_Object, 0 },
+    { "Ljava/lang/Class;",                dvm_java_lang_Class, 0 },
+    { "Ljava/lang/Double;",               dvm_java_lang_Double, 0 },
+    { "Ljava/lang/Float;",                dvm_java_lang_Float, 0 },
+    { "Ljava/lang/Math;",                 dvm_java_lang_Math, 0 },
+    { "Ljava/lang/Runtime;",              dvm_java_lang_Runtime, 0 },
+    { "Ljava/lang/String;",               dvm_java_lang_String, 0 },
+    { "Ljava/lang/System;",               dvm_java_lang_System, 0 },
+    { "Ljava/lang/Throwable;",            dvm_java_lang_Throwable, 0 },
+    { "Ljava/lang/VMClassLoader;",        dvm_java_lang_VMClassLoader, 0 },
+    { "Ljava/lang/VMThread;",             dvm_java_lang_VMThread, 0 },
+    { "Ljava/lang/reflect/AccessibleObject;",
+            dvm_java_lang_reflect_AccessibleObject, 0 },
+    { "Ljava/lang/reflect/Array;",        dvm_java_lang_reflect_Array, 0 },
+    { "Ljava/lang/reflect/Constructor;",
+            dvm_java_lang_reflect_Constructor, 0 },
+    { "Ljava/lang/reflect/Field;",        dvm_java_lang_reflect_Field, 0 },
+    { "Ljava/lang/reflect/Method;",       dvm_java_lang_reflect_Method, 0 },
+    { "Ljava/lang/reflect/Proxy;",        dvm_java_lang_reflect_Proxy, 0 },
+    { "Ljava/util/concurrent/atomic/AtomicLong;",
+            dvm_java_util_concurrent_atomic_AtomicLong, 0 },
+    { "Ldalvik/bytecode/OpcodeInfo;",     dvm_dalvik_bytecode_OpcodeInfo, 0 },
+    { "Ldalvik/system/VMDebug;",          dvm_dalvik_system_VMDebug, 0 },
+    { "Ldalvik/system/DexFile;",          dvm_dalvik_system_DexFile, 0 },
+    { "Ldalvik/system/VMRuntime;",        dvm_dalvik_system_VMRuntime, 0 },
+    { "Ldalvik/system/ZygoteHooks;",      dvm_dalvik_system_ZygoteHooks, 0 },
+    { "Ldalvik/system/VMStack;",          dvm_dalvik_system_VMStack, 0 },
+    { "Lorg/apache/harmony/dalvik/ddmc/DdmServer;",
+            dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0 },
+    { "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",
+            dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 },
+    { "Lorg/apache/harmony/dalvik/NativeTestTarget;",
+            dvm_org_apache_harmony_dalvik_NativeTestTarget, 0 },
+    { "Lsun/misc/Unsafe;",                dvm_sun_misc_Unsafe, 0 },
+    { NULL, NULL, 0 },
+};
+
+
+/*
+ * Set up hash values on the class names.
+ */
+bool dvmInternalNativeStartup()
+{
+    DalvikNativeClass* classPtr = gDvmNativeMethodSet;
+
+    while (classPtr->classDescriptor != NULL) {
+        classPtr->classDescriptorHash =
+            dvmComputeUtf8Hash(classPtr->classDescriptor);
+        classPtr++;
+    }
+
+    gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
+    if (gDvm.userDexFiles == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmInternalNativeShutdown()
+{
+    dvmHashTableFree(gDvm.userDexFiles);
+}
+
+/*
+ * Search the internal native set for a match.
+ */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method)
+{
+    const char* classDescriptor = method->clazz->descriptor;
+    const DalvikNativeClass* pClass;
+    u4 hash;
+
+    hash = dvmComputeUtf8Hash(classDescriptor);
+    pClass = gDvmNativeMethodSet;
+    while (true) {
+        if (pClass->classDescriptor == NULL)
+            break;
+        if (pClass->classDescriptorHash == hash &&
+            strcmp(pClass->classDescriptor, classDescriptor) == 0)
+        {
+            const DalvikNativeMethod* pMeth = pClass->methodInfo;
+            while (true) {
+                if (pMeth->name == NULL)
+                    break;
+
+                if (dvmCompareNameDescriptorAndMethod(pMeth->name,
+                    pMeth->signature, method) == 0)
+                {
+                    /* match */
+                    //ALOGV("+++  match on %s.%s %s at %p",
+                    //    className, methodName, methodSignature, pMeth->fnPtr);
+                    return pMeth->fnPtr;
+                }
+
+                pMeth++;
+            }
+        }
+
+        pClass++;
+    }
+
+    return NULL;
+}
+
+
+/*
+ * Magic "internal native" code stub, inserted into abstract method
+ * definitions when a class is first loaded.  This throws the expected
+ * exception so we don't have to explicitly check for it in the interpreter.
+ */
+void dvmAbstractMethodStub(const u4* args, JValue* pResult)
+{
+    ALOGD("--- called into dvmAbstractMethodStub");
+    dvmThrowAbstractMethodError("abstract method not implemented");
+}
+
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ * Used to implement reflection on fields and methods.
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) {
+    ClassObject* exceptionClass = NULL;
+    if (obj == NULL) {
+        exceptionClass = gDvm.exNullPointerException;
+    } else if (!dvmInstanceof(obj->clazz, clazz)) {
+        exceptionClass = gDvm.exIllegalArgumentException;
+    }
+
+    if (exceptionClass == NULL) {
+        return true;
+    }
+
+    std::string expectedClassName(dvmHumanReadableDescriptor(clazz->descriptor));
+    std::string actualClassName(dvmHumanReadableType(obj));
+    dvmThrowExceptionFmt(exceptionClass, "expected receiver of type %s, but got %s",
+            expectedClassName.c_str(), actualClassName.c_str());
+    return false;
+}
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+    bool doInit)
+{
+    ClassObject* clazz = NULL;
+    char* name = NULL;
+    char* descriptor = NULL;
+
+    if (nameObj == NULL) {
+        dvmThrowNullPointerException("name == null");
+        goto bail;
+    }
+    name = dvmCreateCstrFromString(nameObj);
+
+    /*
+     * We need to validate and convert the name (from x.y.z to x/y/z).  This
+     * is especially handy for array types, since we want to avoid
+     * auto-generating bogus array classes.
+     */
+    if (!dexIsValidClassName(name, true)) {
+        ALOGW("dvmFindClassByName rejecting '%s'", name);
+        dvmThrowClassNotFoundException(name);
+        goto bail;
+    }
+
+    descriptor = dvmDotToDescriptor(name);
+    if (descriptor == NULL) {
+        goto bail;
+    }
+
+    if (doInit)
+        clazz = dvmFindClass(descriptor, loader);
+    else
+        clazz = dvmFindClassNoInit(descriptor, loader);
+
+    if (clazz == NULL) {
+        LOGVV("FAIL: load %s (%d)", descriptor, doInit);
+        Thread* self = dvmThreadSelf();
+        Object* oldExcep = dvmGetException(self);
+        dvmAddTrackedAlloc(oldExcep, self);     /* don't let this be GCed */
+        dvmClearException(self);
+        dvmThrowChainedClassNotFoundException(name, oldExcep);
+        dvmReleaseTrackedAlloc(oldExcep, self);
+    } else {
+        LOGVV("GOOD: load %s (%d) --> %p ldr=%p",
+            descriptor, doInit, clazz, clazz->classLoader);
+    }
+
+bail:
+    free(name);
+    free(descriptor);
+    return clazz;
+}
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call.  This results in
+ * "native abstract" methods, which can't exist.  If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags)
+{
+    if ((flags & ACC_ABSTRACT) != 0) {
+        flags &= ~ACC_NATIVE;
+    }
+
+    flags &= ~ACC_SYNCHRONIZED;
+
+    if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+        flags |= ACC_SYNCHRONIZED;
+    }
+
+    return flags & JAVA_FLAGS_MASK;
+}
diff --git a/vm/native/InternalNative.h b/vm/native/InternalNative.h
new file mode 100644
index 0000000..006fc1e
--- /dev/null
+++ b/vm/native/InternalNative.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef DALVIK_NATIVE_INTERNALNATIVE_H_
+#define DALVIK_NATIVE_INTERNALNATIVE_H_
+
+/*
+ * Some setup for internal native functions.
+ */
+bool dvmInternalNativeStartup(void);
+void dvmInternalNativeShutdown(void);
+
+/* search the internal native set for a match */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method);
+
+/* exception-throwing stub for abstract methods (DalvikNativeFunc) */
+extern "C" void dvmAbstractMethodStub(const u4* args, JValue* pResult);
+
+#endif  // DALVIK_NATIVE_INTERNALNATIVE_H_
diff --git a/vm/native/InternalNativePriv.h b/vm/native/InternalNativePriv.h
new file mode 100644
index 0000000..c05223b
--- /dev/null
+++ b/vm/native/InternalNativePriv.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Declarations and definitions common to internal native code.
+ */
+#ifndef DALVIK_NATIVE_INTERNALNATIVEPRIV_H_
+#define DALVIK_NATIVE_INTERNALNATIVEPRIV_H_
+
+/*
+ * Return macros.  Note we use "->i" instead of "->z" for boolean; this
+ * is because the interpreter expects everything to be a 32-bit value.
+ */
+#ifdef NDEBUG
+# define RETURN_VOID()           do { (void)(pResult); return; } while(0)
+#else
+# define RETURN_VOID()           do { pResult->i = 0xfefeabab; return; }while(0)
+#endif
+#define RETURN_BOOLEAN(_val)    do { pResult->i = (_val); return; } while(0)
+#define RETURN_INT(_val)        do { pResult->i = (_val); return; } while(0)
+#define RETURN_LONG(_val)       do { pResult->j = (_val); return; } while(0)
+#define RETURN_FLOAT(_val)      do { pResult->f = (_val); return; } while(0)
+#define RETURN_DOUBLE(_val)     do { pResult->d = (_val); return; } while(0)
+#define RETURN_PTR(_val)        do { pResult->l = (Object*)(_val); return; } while(0)
+
+/*
+ * Normally a method that has an "inline native" will be invoked using
+ * execute-inline. If the method is invoked via reflection, JNI, or by
+ * virtual dispatch (in the case of String.equals, which we may arrive
+ * at via Object.equals), we need a non-"inline native" implementation.
+ *
+ * This macro is used to implement the native methods that bridge this gap.
+ */
+#define MAKE_INTRINSIC_TRAMPOLINE(INTRINSIC_FN) \
+    extern bool INTRINSIC_FN(u4 arg0, u4 arg1, u4 arg2, u4 arg3, \
+            JValue* pResult); \
+    INTRINSIC_FN(args[0], args[1], args[2], args[3], pResult);
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz);
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+    bool doInit);
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call.  This results in
+ * "native abstract" methods, which can't exist.  If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags);
+
+/*
+ * dvmHashTableFree callback for some DexFile operations.
+ */
+void dvmFreeDexOrJar(void* vptr);
+
+/*
+ * Tables of methods.
+ */
+extern const DalvikNativeMethod dvm_java_lang_Object[];
+extern const DalvikNativeMethod dvm_java_lang_Class[];
+extern const DalvikNativeMethod dvm_java_lang_Double[];
+extern const DalvikNativeMethod dvm_java_lang_Float[];
+extern const DalvikNativeMethod dvm_java_lang_Math[];
+extern const DalvikNativeMethod dvm_java_lang_Runtime[];
+extern const DalvikNativeMethod dvm_java_lang_String[];
+extern const DalvikNativeMethod dvm_java_lang_System[];
+extern const DalvikNativeMethod dvm_java_lang_Throwable[];
+extern const DalvikNativeMethod dvm_java_lang_VMClassLoader[];
+extern const DalvikNativeMethod dvm_java_lang_VMThread[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Array[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Constructor[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Field[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Method[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Proxy[];
+extern const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[];
+extern const DalvikNativeMethod dvm_dalvik_bytecode_OpcodeInfo[];
+extern const DalvikNativeMethod dvm_dalvik_system_SamplingProfiler[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMDebug[];
+extern const DalvikNativeMethod dvm_dalvik_system_DexFile[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMRuntime[];
+extern const DalvikNativeMethod dvm_dalvik_system_ZygoteHooks[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMStack[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[];
+extern const DalvikNativeMethod dvm_sun_misc_Unsafe[];
+
+#endif  // DALVIK_NATIVE_INTERNALNATIVEPRIV_H_
diff --git a/vm/native/README.txt b/vm/native/README.txt
new file mode 100644
index 0000000..bc8912f
--- /dev/null
+++ b/vm/native/README.txt
@@ -0,0 +1,23 @@
+Internal native functions.
+
+All of the functions defined here make direct use of VM functions or data
+structures, so they can't be written with JNI and shouldn't really be in
+a separate shared library.  Do not add additional functions here unless
+they need to access VM internals directly.
+
+All functions here either complete quickly or are used to enter a wait
+state, so we don't set the thread status to THREAD_NATIVE when executing
+these methods.  This means that the GC will wait for these functions
+to finish.  DO NOT perform long operations or blocking I/O in here.
+These methods should not be declared "synchronized", because we don't
+check for that flag when issuing the call.
+
+We use "late" binding on these, rather than explicit registration,
+because it's easier to handle the core system classes that way.
+
+The functions here use the DalvikNativeFunc prototype, but we can
+also treat them as DalvikBridgeFunc, which takes two extra arguments.
+The former represents the API that we're most likely to expose should
+JNI performance be deemed insufficient.  The Bridge version is used as
+an optimization for a few high-volume Object calls, and should generally
+not be used as we may drop support for it at some point.
diff --git a/vm/native/dalvik_bytecode_OpcodeInfo.cpp b/vm/native/dalvik_bytecode_OpcodeInfo.cpp
new file mode 100644
index 0000000..3861fef
--- /dev/null
+++ b/vm/native/dalvik_bytecode_OpcodeInfo.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+/*
+ * dalvik.bytecode.OpcodeInfo
+ *
+ * This file mostly exists in its current form so that we don't have
+ * to have duplicate definitions for things both in libcore and in
+ * libdex.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * public static native boolean isInvoke(int opcode);
+ */
+static void Dalvik_dalvik_bytecode_OpcodeInfo_isInvoke(const u4* args,
+    JValue* pResult)
+{
+    Opcode opcode = static_cast<Opcode>(args[0]);
+    int flags = dexGetFlagsFromOpcode(opcode);
+    bool result = (flags & kInstrInvoke) != 0;
+    RETURN_BOOLEAN(result);
+}
+
+const DalvikNativeMethod dvm_dalvik_bytecode_OpcodeInfo[] = {
+    { "isInvoke", "(I)Z", Dalvik_dalvik_bytecode_OpcodeInfo_isInvoke },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_DexFile.cpp b/vm/native/dalvik_system_DexFile.cpp
new file mode 100644
index 0000000..020c52e
--- /dev/null
+++ b/vm/native/dalvik_system_DexFile.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * dalvik.system.DexFile
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Return true if the given name ends with ".dex".
+ */
+static bool hasDexExtension(const char* name) {
+    size_t len = strlen(name);
+
+    return (len >= 5)
+        && (name[len - 5] != '/')
+        && (strcmp(&name[len - 4], ".dex") == 0);
+}
+
+/*
+ * Internal struct for managing DexFile.
+ */
+struct DexOrJar {
+    char*       fileName;
+    bool        isDex;
+    bool        okayToFree;
+    RawDexFile* pRawDexFile;
+    JarFile*    pJarFile;
+    u1*         pDexMemory; // malloc()ed memory, if any
+};
+
+/*
+ * (This is a dvmHashTableFree callback.)
+ */
+void dvmFreeDexOrJar(void* vptr)
+{
+    DexOrJar* pDexOrJar = (DexOrJar*) vptr;
+
+    ALOGV("Freeing DexOrJar '%s'", pDexOrJar->fileName);
+
+    if (pDexOrJar->isDex)
+        dvmRawDexFileFree(pDexOrJar->pRawDexFile);
+    else
+        dvmJarFileFree(pDexOrJar->pJarFile);
+    free(pDexOrJar->fileName);
+    free(pDexOrJar->pDexMemory);
+    free(pDexOrJar);
+}
+
+/*
+ * (This is a dvmHashTableLookup compare func.)
+ *
+ * Args are DexOrJar*.
+ */
+static int hashcmpDexOrJar(const void* tableVal, const void* newVal)
+{
+    return (int) newVal - (int) tableVal;
+}
+
+/*
+ * Verify that the "cookie" is a DEX file we opened.
+ *
+ * Expects that the hash table will be *unlocked* here.
+ *
+ * If the cookie is invalid, we throw an exception and return "false".
+ */
+static bool validateCookie(int cookie)
+{
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+    LOGVV("+++ dex verifying cookie %p", pDexOrJar);
+
+    if (pDexOrJar == NULL)
+        return false;
+
+    u4 hash = cookie;
+    dvmHashTableLock(gDvm.userDexFiles);
+    void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+                hashcmpDexOrJar, false);
+    dvmHashTableUnlock(gDvm.userDexFiles);
+    if (result == NULL) {
+        dvmThrowRuntimeException("invalid DexFile cookie");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Add given DexOrJar to the hash table of user-loaded dex files.
+ */
+static void addToDexFileTable(DexOrJar* pDexOrJar) {
+    /*
+     * Later on, we will receive this pointer as an argument and need
+     * to find it in the hash table without knowing if it's valid or
+     * not, which means we can't compute a hash value from anything
+     * inside DexOrJar. We don't share DexOrJar structs when the same
+     * file is opened multiple times, so we can just use the low 32
+     * bits of the pointer as the hash.
+     */
+    u4 hash = (u4) pDexOrJar;
+    void* result;
+
+    dvmHashTableLock(gDvm.userDexFiles);
+    result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+            hashcmpDexOrJar, true);
+    dvmHashTableUnlock(gDvm.userDexFiles);
+
+    if (result != pDexOrJar) {
+        ALOGE("Pointer has already been added?");
+        dvmAbort();
+    }
+
+    pDexOrJar->okayToFree = true;
+}
+
+/*
+ * private static int openDexFileNative(String sourceName, String outputName,
+ *     int flags) throws IOException
+ *
+ * Open a DEX file, returning a pointer to our internal data structure.
+ *
+ * "sourceName" should point to the "source" jar or DEX file.
+ *
+ * If "outputName" is NULL, the DEX code will automatically find the
+ * "optimized" version in the cache directory, creating it if necessary.
+ * If it's non-NULL, the specified file will be used instead.
+ *
+ * TODO: at present we will happily open the same file more than once.
+ * To optimize this away we could search for existing entries in the hash
+ * table and refCount them.  Requires atomic ops or adding "synchronized"
+ * to the non-native code that calls here.
+ */
+static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
+    JValue* pResult)
+{
+    StringObject* sourceNameObj = (StringObject*) args[0];
+    StringObject* outputNameObj = (StringObject*) args[1];
+    DexOrJar* pDexOrJar = NULL;
+    JarFile* pJarFile;
+    RawDexFile* pRawDexFile;
+    char* sourceName;
+    char* outputName;
+
+    if (sourceNameObj == NULL) {
+        dvmThrowNullPointerException("sourceName == null");
+        RETURN_VOID();
+    }
+
+    sourceName = dvmCreateCstrFromString(sourceNameObj);
+    if (outputNameObj != NULL)
+        outputName = dvmCreateCstrFromString(outputNameObj);
+    else
+        outputName = NULL;
+
+    /*
+     * We have to deal with the possibility that somebody might try to
+     * open one of our bootstrap class DEX files.  The set of dependencies
+     * will be different, and hence the results of optimization might be
+     * different, which means we'd actually need to have two versions of
+     * the optimized DEX: one that only knows about part of the boot class
+     * path, and one that knows about everything in it.  The latter might
+     * optimize field/method accesses based on a class that appeared later
+     * in the class path.
+     *
+     * We can't let the user-defined class loader open it and start using
+     * the classes, since the optimized form of the code skips some of
+     * the method and field resolution that we would ordinarily do, and
+     * we'd have the wrong semantics.
+     *
+     * We have to reject attempts to manually open a DEX file from the boot
+     * class path.  The easiest way to do this is by filename, which works
+     * out because variations in name (e.g. "/system/framework/./ext.jar")
+     * result in us hitting a different dalvik-cache entry.  It's also fine
+     * if the caller specifies their own output file.
+     */
+    if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
+        ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
+        dvmThrowIOException(
+            "Re-opening BOOTCLASSPATH DEX files is not allowed");
+        free(sourceName);
+        free(outputName);
+        RETURN_VOID();
+    }
+
+    /*
+     * Try to open it directly as a DEX if the name ends with ".dex".
+     * If that fails (or isn't tried in the first place), try it as a
+     * Zip with a "classes.dex" inside.
+     */
+    if (hasDexExtension(sourceName)
+            && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
+        ALOGV("Opening DEX file '%s' (DEX)", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = true;
+        pDexOrJar->pRawDexFile = pRawDexFile;
+        pDexOrJar->pDexMemory = NULL;
+    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
+        ALOGV("Opening DEX file '%s' (Jar)", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = false;
+        pDexOrJar->pJarFile = pJarFile;
+        pDexOrJar->pDexMemory = NULL;
+    } else {
+        ALOGV("Unable to open DEX file '%s'", sourceName);
+        dvmThrowIOException("unable to open DEX file");
+    }
+
+    if (pDexOrJar != NULL) {
+        pDexOrJar->fileName = sourceName;
+        addToDexFileTable(pDexOrJar);
+    } else {
+        free(sourceName);
+    }
+
+    free(outputName);
+    RETURN_LONG((uintptr_t) pDexOrJar);
+}
+
+/*
+ * private static int openDexFile(byte[] fileContents) throws IOException
+ *
+ * Open a DEX file represented in a byte[], returning a pointer to our
+ * internal data structure.
+ *
+ * The system will only perform "essential" optimizations on the given file.
+ *
+ * TODO: should be using "long" for a pointer.
+ */
+static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* fileContentsObj = (ArrayObject*) args[0];
+    u4 length;
+    u1* pBytes;
+    RawDexFile* pRawDexFile;
+    DexOrJar* pDexOrJar = NULL;
+
+    if (fileContentsObj == NULL) {
+        dvmThrowNullPointerException("fileContents == null");
+        RETURN_VOID();
+    }
+
+    /* TODO: Avoid making a copy of the array. (note array *is* modified) */
+    length = fileContentsObj->length;
+    pBytes = (u1*) malloc(length);
+
+    if (pBytes == NULL) {
+        dvmThrowRuntimeException("unable to allocate DEX memory");
+        RETURN_VOID();
+    }
+
+    memcpy(pBytes, fileContentsObj->contents, length);
+
+    if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
+        ALOGV("Unable to open in-memory DEX file");
+        free(pBytes);
+        dvmThrowRuntimeException("unable to open in-memory DEX file");
+        RETURN_VOID();
+    }
+
+    ALOGV("Opening in-memory DEX");
+    pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+    pDexOrJar->isDex = true;
+    pDexOrJar->pRawDexFile = pRawDexFile;
+    pDexOrJar->pDexMemory = pBytes;
+    pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
+    addToDexFileTable(pDexOrJar);
+
+    RETURN_LONG((uintptr_t) pDexOrJar);
+}
+
+/*
+ * private static void closeDexFile(int cookie)
+ *
+ * Release resources associated with a user-loaded DEX file.
+ */
+static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args,
+    JValue* pResult)
+{
+    int cookie = dvmGetArgLong(args, 0);
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+    if (pDexOrJar == NULL)
+        RETURN_VOID();
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    ALOGV("Closing DEX file %p (%s)", pDexOrJar, pDexOrJar->fileName);
+
+    /*
+     * We can't just free arbitrary DEX files because they have bits and
+     * pieces of loaded classes.  The only exception to this rule is if
+     * they were never used to load classes.
+     *
+     * If we can't free them here, dvmInternalNativeShutdown() will free
+     * them when the VM shuts down.
+     */
+    if (pDexOrJar->okayToFree) {
+        u4 hash = (u4) pDexOrJar;
+        dvmHashTableLock(gDvm.userDexFiles);
+        if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
+            ALOGW("WARNING: could not remove '%s' from DEX hash table",
+                pDexOrJar->fileName);
+        }
+        dvmHashTableUnlock(gDvm.userDexFiles);
+        ALOGV("+++ freeing DexFile '%s' resources", pDexOrJar->fileName);
+        dvmFreeDexOrJar(pDexOrJar);
+    } else {
+        ALOGV("+++ NOT freeing DexFile '%s' resources", pDexOrJar->fileName);
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * private static Class defineClassNative(String name, ClassLoader loader,
+ *      int cookie)
+ *
+ * Load a class from a DEX file.  This is roughly equivalent to defineClass()
+ * in a regular VM -- it's invoked by the class loader to cause the
+ * creation of a specific class.  The difference is that the search for and
+ * reading of the bytes is done within the VM.
+ *
+ * The class name is a "binary name", e.g. "java.lang.String".
+ *
+ * Returns a null pointer with no exception if the class was not found.
+ * Throws an exception on other failures.
+ */
+static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    Object* loader = (Object*) args[1];
+    int cookie = dvmGetArgLong(args, 2);
+    ClassObject* clazz = NULL;
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    DvmDex* pDvmDex;
+    char* name;
+    char* descriptor;
+
+    name = dvmCreateCstrFromString(nameObj);
+    descriptor = dvmDotToDescriptor(name);
+    ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
+        descriptor, loader, cookie);
+    free(name);
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    if (pDexOrJar->isDex)
+        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+    else
+        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+
+    /* once we load something, we can't unmap the storage */
+    pDexOrJar->okayToFree = false;
+
+    clazz = dvmDefineClass(pDvmDex, descriptor, loader);
+    Thread* self = dvmThreadSelf();
+    if (dvmCheckException(self)) {
+        /*
+         * If we threw a "class not found" exception, stifle it, since the
+         * contract in the higher method says we simply return null if
+         * the class is not found.
+         */
+        Object* excep = dvmGetException(self);
+        if (strcmp(excep->clazz->descriptor,
+                   "Ljava/lang/ClassNotFoundException;") == 0 ||
+            strcmp(excep->clazz->descriptor,
+                   "Ljava/lang/NoClassDefFoundError;") == 0)
+        {
+            dvmClearException(self);
+        }
+        clazz = NULL;
+    }
+
+    free(descriptor);
+    RETURN_PTR(clazz);
+}
+
+/*
+ * private static String[] getClassNameList(int cookie)
+ *
+ * Returns a String array that holds the names of all classes in the
+ * specified DEX file.
+ */
+static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args,
+    JValue* pResult)
+{
+    int cookie = dvmGetArgLong(args, 0);
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    Thread* self = dvmThreadSelf();
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    DvmDex* pDvmDex;
+    if (pDexOrJar->isDex)
+        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+    else
+        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+    assert(pDvmDex != NULL);
+    DexFile* pDexFile = pDvmDex->pDexFile;
+
+    int count = pDexFile->pHeader->classDefsSize;
+    ClassObject* arrayClass =
+        dvmFindArrayClassForElement(gDvm.classJavaLangString);
+    ArrayObject* stringArray =
+        dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        /* probably OOM */
+        ALOGD("Failed allocating array of %d strings", count);
+        assert(dvmCheckException(self));
+        RETURN_VOID();
+    }
+
+    int i;
+    for (i = 0; i < count; i++) {
+        const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i);
+        const char* descriptor =
+            dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        char* className = dvmDescriptorToDot(descriptor);
+        StringObject* str = dvmCreateStringFromCstr(className);
+        dvmSetObjectArrayElement(stringArray, i, (Object *)str);
+        dvmReleaseTrackedAlloc((Object *)str, self);
+        free(className);
+    }
+
+    dvmReleaseTrackedAlloc((Object*)stringArray, self);
+    RETURN_PTR(stringArray);
+}
+
+/*
+ * public static boolean isDexOptNeeded(String fileName)
+ *         throws FileNotFoundException, IOException
+ *
+ * Returns true if the VM believes that the apk/jar file is out of date
+ * and should be passed through "dexopt" again.
+ *
+ * @param fileName the absolute path to the apk/jar file to examine.
+ * @return true if dexopt should be called on the file, false otherwise.
+ * @throws java.io.FileNotFoundException if fileName is not readable,
+ *         not a file, or not present.
+ * @throws java.io.IOException if fileName is not a valid apk/jar file or
+ *         if problems occur while parsing it.
+ * @throws java.lang.NullPointerException if fileName is null.
+ * @throws dalvik.system.StaleDexCacheError if the optimized dex file
+ *         is stale but exists on a read-only partition.
+ */
+static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    char* name;
+    DexCacheStatus status;
+    int result;
+
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL) {
+        dvmThrowNullPointerException("fileName == null");
+        RETURN_VOID();
+    }
+    if (access(name, R_OK) != 0) {
+        dvmThrowFileNotFoundException(name);
+        free(name);
+        RETURN_VOID();
+    }
+    status = dvmDexCacheStatus(name);
+    ALOGV("dvmDexCacheStatus(%s) returned %d", name, status);
+
+    result = true;
+    switch (status) {
+    default: //FALLTHROUGH
+    case DEX_CACHE_BAD_ARCHIVE:
+        dvmThrowIOException(name);
+        result = -1;
+        break;
+    case DEX_CACHE_OK:
+        result = false;
+        break;
+    case DEX_CACHE_STALE:
+        result = true;
+        break;
+    case DEX_CACHE_STALE_ODEX:
+        dvmThrowStaleDexCacheError(name);
+        result = -1;
+        break;
+    }
+    free(name);
+
+    if (result >= 0) {
+        RETURN_BOOLEAN(result);
+    } else {
+        RETURN_VOID();
+    }
+}
+
+static void Dalvik_dalvik_system_DexFile_isDexOptNeededInternal(const u4* args,
+    JValue* pResult) {
+    return Dalvik_dalvik_system_DexFile_isDexOptNeeded(args, pResult);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
+    { "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)J",
+        Dalvik_dalvik_system_DexFile_openDexFileNative },
+    { "openDexFile",        "([B)J",
+        Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
+    { "closeDexFile",       "(J)V",
+        Dalvik_dalvik_system_DexFile_closeDexFile },
+    { "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;",
+        Dalvik_dalvik_system_DexFile_defineClassNative },
+    { "getClassNameList",   "(J)[Ljava/lang/String;",
+        Dalvik_dalvik_system_DexFile_getClassNameList },
+    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
+        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
+    { "isDexOptNeededInternal",     "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Z",
+        Dalvik_dalvik_system_DexFile_isDexOptNeededInternal },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMDebug.cpp b/vm/native/dalvik_system_VMDebug.cpp
new file mode 100644
index 0000000..227d1c1
--- /dev/null
+++ b/vm/native/dalvik_system_VMDebug.cpp
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * dalvik.system.VMDebug
+ */
+#include "Dalvik.h"
+#include "alloc/HeapSource.h"
+#include "native/InternalNativePriv.h"
+#include "hprof/Hprof.h"
+
+#include <string.h>
+#include <unistd.h>
+
+
+/*
+ * Extracts the fd from a FileDescriptor object.
+ *
+ * If an error is encountered, or the extracted descriptor is numerically
+ * invalid, this returns -1 with an exception raised.
+ */
+static int getFileDescriptor(Object* obj)
+{
+    assert(obj != NULL);
+    assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0);
+
+    int fd = dvmGetFieldInt(obj, gDvm.offJavaIoFileDescriptor_descriptor);
+    if (fd < 0) {
+        dvmThrowRuntimeException("Invalid file descriptor");
+        return -1;
+    }
+
+    return fd;
+}
+
+/*
+ * static String[] getVmFeatureList()
+ *
+ * Return a set of strings describing available VM features (this is chiefly
+ * of interest to DDMS).
+ */
+static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args, JValue* pResult) {
+    std::vector<std::string> features;
+    features.push_back("method-trace-profiling");
+    features.push_back("method-trace-profiling-streaming");
+    features.push_back("method-sample-profiling");
+    features.push_back("hprof-heap-dump");
+    features.push_back("hprof-heap-dump-streaming");
+
+    ArrayObject* result = dvmCreateStringArray(features);
+    dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+/* These must match the values in dalvik.system.VMDebug.
+ */
+enum {
+    KIND_ALLOCATED_OBJECTS      = 1<<0,
+    KIND_ALLOCATED_BYTES        = 1<<1,
+    KIND_FREED_OBJECTS          = 1<<2,
+    KIND_FREED_BYTES            = 1<<3,
+    KIND_GC_INVOCATIONS         = 1<<4,
+    KIND_CLASS_INIT_COUNT       = 1<<5,
+    KIND_CLASS_INIT_TIME        = 1<<6,
+
+    /* These values exist for backward compatibility. */
+    KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
+    KIND_EXT_ALLOCATED_BYTES   = 1<<13,
+    KIND_EXT_FREED_OBJECTS     = 1<<14,
+    KIND_EXT_FREED_BYTES       = 1<<15,
+
+    KIND_GLOBAL_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS,
+    KIND_GLOBAL_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES,
+    KIND_GLOBAL_FREED_OBJECTS       = KIND_FREED_OBJECTS,
+    KIND_GLOBAL_FREED_BYTES         = KIND_FREED_BYTES,
+    KIND_GLOBAL_GC_INVOCATIONS      = KIND_GC_INVOCATIONS,
+    KIND_GLOBAL_CLASS_INIT_COUNT    = KIND_CLASS_INIT_COUNT,
+    KIND_GLOBAL_CLASS_INIT_TIME     = KIND_CLASS_INIT_TIME,
+
+    KIND_THREAD_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS << 16,
+    KIND_THREAD_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES << 16,
+    KIND_THREAD_FREED_OBJECTS       = KIND_FREED_OBJECTS << 16,
+    KIND_THREAD_FREED_BYTES         = KIND_FREED_BYTES << 16,
+
+    KIND_THREAD_GC_INVOCATIONS      = KIND_GC_INVOCATIONS << 16,
+
+    // TODO: failedAllocCount, failedAllocSize
+};
+
+#define KIND_ALL_COUNTS 0xffffffff
+
+/*
+ * Zero out the specified fields.
+ */
+static void clearAllocProfStateFields(AllocProfState *allocProf,
+    unsigned int kinds)
+{
+    if (kinds & KIND_ALLOCATED_OBJECTS) {
+        allocProf->allocCount = 0;
+    }
+    if (kinds & KIND_ALLOCATED_BYTES) {
+        allocProf->allocSize = 0;
+    }
+    if (kinds & KIND_FREED_OBJECTS) {
+        allocProf->freeCount = 0;
+    }
+    if (kinds & KIND_FREED_BYTES) {
+        allocProf->freeSize = 0;
+    }
+    if (kinds & KIND_GC_INVOCATIONS) {
+        allocProf->gcCount = 0;
+    }
+    if (kinds & KIND_CLASS_INIT_COUNT) {
+        allocProf->classInitCount = 0;
+    }
+    if (kinds & KIND_CLASS_INIT_TIME) {
+        allocProf->classInitTime = 0;
+    }
+}
+
+/*
+ * static void startAllocCounting()
+ *
+ * Reset the counters and enable counting.
+ *
+ * TODO: this currently only resets the per-thread counters for the current
+ * thread.  If we actually start using the per-thread counters we'll
+ * probably want to fix this.
+ */
+static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS);
+    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS);
+    dvmStartAllocCounting();
+    RETURN_VOID();
+}
+
+/*
+ * public static void stopAllocCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmStopAllocCounting();
+    RETURN_VOID();
+}
+
+/*
+ * private static int getAllocCount(int kind)
+ */
+static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args,
+    JValue* pResult)
+{
+    AllocProfState *allocProf;
+    unsigned int kind = args[0];
+    if (kind < (1<<16)) {
+        allocProf = &gDvm.allocProf;
+    } else {
+        allocProf = &dvmThreadSelf()->allocProf;
+        kind >>= 16;
+    }
+    switch (kind) {
+    case KIND_ALLOCATED_OBJECTS:
+        pResult->i = allocProf->allocCount;
+        break;
+    case KIND_ALLOCATED_BYTES:
+        pResult->i = allocProf->allocSize;
+        break;
+    case KIND_FREED_OBJECTS:
+        pResult->i = allocProf->freeCount;
+        break;
+    case KIND_FREED_BYTES:
+        pResult->i = allocProf->freeSize;
+        break;
+    case KIND_GC_INVOCATIONS:
+        pResult->i = allocProf->gcCount;
+        break;
+    case KIND_CLASS_INIT_COUNT:
+        pResult->i = allocProf->classInitCount;
+        break;
+    case KIND_CLASS_INIT_TIME:
+        /* convert nsec to usec, reduce to 32 bits */
+        pResult->i = (int) (allocProf->classInitTime / 1000);
+        break;
+    case KIND_EXT_ALLOCATED_OBJECTS:
+    case KIND_EXT_ALLOCATED_BYTES:
+    case KIND_EXT_FREED_OBJECTS:
+    case KIND_EXT_FREED_BYTES:
+        pResult->i = 0;  /* backward compatibility */
+        break;
+    default:
+        assert(false);
+        pResult->i = -1;
+    }
+}
+
+/*
+ * public static void resetAllocCount(int kinds)
+ */
+static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args,
+    JValue* pResult)
+{
+    unsigned int kinds = args[0];
+    clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff);
+    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16);
+    RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingDdmsImpl(int bufferSize, int flags,
+ *     boolean samplingEnabled, int intervalUs)
+ *
+ * Start method trace profiling, sending results directly to DDMS.
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingDdmsImpl(const u4* args,
+    JValue* pResult)
+{
+    int bufferSize = args[0];
+    int flags = args[1];
+    bool samplingEnabled = args[2];
+    int intervalUs = args[3];
+    dvmMethodTraceStart("[DDMS]", -1, bufferSize, flags, true, samplingEnabled,
+        intervalUs);
+    RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingFd(String traceFileName, FileDescriptor fd,
+ *     int bufferSize, int flags, boolean samplingEnabled, int intervalUs)
+ *
+ * Start method trace profiling, sending results to a file descriptor.
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingFd(const u4* args,
+    JValue* pResult)
+{
+    StringObject* traceFileStr = (StringObject*) args[0];
+    Object* traceFd = (Object*) args[1];
+    int bufferSize = args[2];
+    int flags = args[3];
+    bool samplingEnabled = args[4];
+    int intervalUs = args[5];
+
+    int origFd = getFileDescriptor(traceFd);
+    if (origFd < 0)
+        RETURN_VOID();
+
+    int fd = dup(origFd);
+    if (fd < 0) {
+        dvmThrowExceptionFmt(gDvm.exRuntimeException,
+            "dup(%d) failed: %s", origFd, strerror(errno));
+        RETURN_VOID();
+    }
+
+    char* traceFileName = dvmCreateCstrFromString(traceFileStr);
+    if (traceFileName == NULL) {
+        RETURN_VOID();
+    }
+
+    dvmMethodTraceStart(traceFileName, fd, bufferSize, flags, false,
+        samplingEnabled, intervalUs);
+    free(traceFileName);
+    RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingFilename(String traceFileName, int bufferSize,
+ *     int flags, boolean samplingEnabled, int intervalUs)
+ *
+ * Start method trace profiling, sending results to a file.
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingFilename(const u4* args,
+    JValue* pResult)
+{
+    StringObject* traceFileStr = (StringObject*) args[0];
+    int bufferSize = args[1];
+    int flags = args[2];
+    bool samplingEnabled = args[3];
+    int intervalUs = args[4];
+
+    char* traceFileName = dvmCreateCstrFromString(traceFileStr);
+    if (traceFileName == NULL) {
+        RETURN_VOID();
+    }
+
+    dvmMethodTraceStart(traceFileName, -1, bufferSize, flags, false,
+        samplingEnabled, intervalUs);
+    free(traceFileName);
+    RETURN_VOID();
+}
+
+/*
+ * static int getMethodTracingMode()
+ *
+ * Determine whether method tracing is currently active and what type is active.
+ */
+static void Dalvik_dalvik_system_VMDebug_getMethodTracingMode(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_INT(dvmGetMethodTracingMode());
+}
+
+/*
+ * static void stopMethodTracing()
+ *
+ * Stop method tracing.
+ */
+static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmMethodTraceStop();
+    RETURN_VOID();
+}
+
+/*
+ * static void startEmulatorTracing()
+ *
+ * Start sending method trace info to the emulator.
+ */
+static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmEmulatorTraceStart();
+    RETURN_VOID();
+}
+
+/*
+ * static void stopEmulatorTracing()
+ *
+ * Start sending method trace info to the emulator.
+ */
+static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmEmulatorTraceStop();
+    RETURN_VOID();
+}
+
+/*
+ * static boolean isDebuggerConnected()
+ *
+ * Returns "true" if a debugger is attached.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_BOOLEAN(dvmDbgIsDebuggerConnected());
+}
+
+/*
+ * static boolean isDebuggingEnabled()
+ *
+ * Returns "true" if debugging is enabled.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_BOOLEAN(gDvm.jdwpConfigured);
+}
+
+/*
+ * static long lastDebuggerActivity()
+ *
+ * Returns the time, in msec, since we last had an interaction with the
+ * debugger (send or receive).
+ */
+static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_LONG(dvmDbgLastDebuggerActivity());
+}
+
+/*
+ * static void startInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args,
+    JValue* pResult)
+{
+    dvmStartInstructionCounting();
+    RETURN_VOID();
+}
+
+/*
+ * static void stopInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args,
+    JValue* pResult)
+{
+    dvmStopInstructionCounting();
+    RETURN_VOID();
+}
+
+/*
+ * static boolean getInstructionCount(int[] counts)
+ *
+ * Grab a copy of the global instruction count array.
+ *
+ * Since the instruction counts aren't synchronized, we use sched_yield
+ * to improve our chances of finishing without contention.  (Only makes
+ * sense on a uniprocessor.)
+ */
+static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* countArray = (ArrayObject*) args[0];
+
+    if (countArray != NULL) {
+        int* storage = (int*)(void*)countArray->contents;
+        u4 length = countArray->length;
+
+        /*
+         * Ensure that we copy at most kNumPackedOpcodes
+         * elements, but no more than the length of the given array.
+         */
+        if (length > kNumPackedOpcodes) {
+            length = kNumPackedOpcodes;
+        }
+
+        sched_yield();
+        memcpy(storage, gDvm.executedInstrCounts, length * sizeof(int));
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static boolean resetInstructionCount()
+ *
+ * Reset the instruction count array.
+ */
+static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args,
+    JValue* pResult)
+{
+    sched_yield();
+    memset(gDvm.executedInstrCounts, 0, kNumPackedOpcodes * sizeof(int));
+    RETURN_VOID();
+}
+
+/*
+ * static void printLoadedClasses(int flags)
+ *
+ * Dump the list of loaded classes.
+ */
+static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args,
+    JValue* pResult)
+{
+    int flags = args[0];
+
+    dvmDumpAllClasses(flags);
+
+    RETURN_VOID();
+}
+
+/*
+ * static int getLoadedClassCount()
+ *
+ * Return the number of loaded classes
+ */
+static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args,
+    JValue* pResult)
+{
+    int count;
+
+    UNUSED_PARAMETER(args);
+
+    count = dvmGetNumLoadedClasses();
+
+    RETURN_INT(count);
+}
+
+/*
+ * Returns the thread-specific CPU-time clock value for the current thread,
+ * or -1 if the feature isn't supported.
+ */
+static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args,
+    JValue* pResult)
+{
+    jlong result;
+
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+    result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec);
+#else
+    result = (jlong) -1;
+#endif
+
+    RETURN_LONG(result);
+}
+
+/*
+ * static void dumpHprofData(String fileName, FileDescriptor fd)
+ *
+ * Cause "hprof" data to be dumped.  We can throw an IOException if an
+ * error occurs during file handling.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args,
+    JValue* pResult)
+{
+    StringObject* fileNameStr = (StringObject*) args[0];
+    Object* fileDescriptor = (Object*) args[1];
+    char* fileName;
+    int result;
+
+    /*
+     * Only one of these may be NULL.
+     */
+    if (fileNameStr == NULL && fileDescriptor == NULL) {
+        dvmThrowNullPointerException("fileName == null && fd == null");
+        RETURN_VOID();
+    }
+
+    if (fileNameStr != NULL) {
+        fileName = dvmCreateCstrFromString(fileNameStr);
+        if (fileName == NULL) {
+            /* unexpected -- malloc failure? */
+            dvmThrowRuntimeException("malloc failure?");
+            RETURN_VOID();
+        }
+    } else {
+        fileName = strdup("[fd]");
+    }
+
+    int fd = -1;
+    if (fileDescriptor != NULL) {
+        fd = getFileDescriptor(fileDescriptor);
+        if (fd < 0) {
+            free(fileName);
+            RETURN_VOID();
+        }
+    }
+
+    result = hprofDumpHeap(fileName, fd, false);
+    free(fileName);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowRuntimeException(
+            "Failure during heap dump; check log output for details");
+        RETURN_VOID();
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static void dumpHprofDataDdms()
+ *
+ * Cause "hprof" data to be computed and sent directly to DDMS.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args,
+    JValue* pResult)
+{
+    int result;
+
+    result = hprofDumpHeap("[DDMS]", -1, true);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowRuntimeException(
+            "Failure during heap dump; check log output for details");
+        RETURN_VOID();
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static boolean cacheRegisterMap(String classAndMethodDescr)
+ *
+ * If the specified class is loaded, and the named method exists, ensure
+ * that the method's register map is ready for use.  If the class/method
+ * cannot be found, nothing happens.
+ *
+ * This can improve the zygote's sharing of compressed register maps.  Do
+ * this after class preloading.
+ *
+ * Returns true if the register map is cached and ready, either as a result
+ * of this call or earlier activity.  Returns false if the class isn't loaded,
+ * if the method couldn't be found, or if the method has no register map.
+ *
+ * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.)
+ */
+static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args,
+    JValue* pResult)
+{
+    StringObject* classAndMethodDescStr = (StringObject*) args[0];
+    ClassObject* clazz;
+    bool result = false;
+
+    if (classAndMethodDescStr == NULL) {
+        dvmThrowNullPointerException("classAndMethodDesc == null");
+        RETURN_VOID();
+    }
+
+    char* classAndMethodDesc = NULL;
+
+    /*
+     * Pick the string apart.  We have a local copy, so just modify it
+     * in place.
+     */
+    classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr);
+
+    char* methodName = strchr(classAndMethodDesc, '.');
+    if (methodName == NULL) {
+        dvmThrowRuntimeException("method name not found in string");
+        RETURN_VOID();
+    }
+    *methodName++ = '\0';
+
+    char* methodDescr = strchr(methodName, ':');
+    if (methodDescr == NULL) {
+        dvmThrowRuntimeException("method descriptor not found in string");
+        RETURN_VOID();
+    }
+    *methodDescr++ = '\0';
+
+    //ALOGD("GOT: %s %s %s", classAndMethodDesc, methodName, methodDescr);
+
+    /*
+     * Find the class, but only if it's already loaded.
+     */
+    clazz = dvmLookupClass(classAndMethodDesc, NULL, false);
+    if (clazz == NULL) {
+        ALOGD("Class %s not found in bootstrap loader", classAndMethodDesc);
+        goto bail;
+    }
+
+    Method* method;
+
+    /*
+     * Find the method, which could be virtual or direct, defined directly
+     * or inherited.
+     */
+    if (methodName[0] == '<') {
+        /*
+         * Constructor or class initializer.  Only need to examine the
+         * "direct" list, and don't need to search up the class hierarchy.
+         */
+        method = dvmFindDirectMethodByDescriptor(clazz, methodName,
+                    methodDescr);
+    } else {
+        /*
+         * Try both lists, and scan up the tree.
+         */
+        method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName,
+                    methodDescr);
+        if (method == NULL) {
+            method = dvmFindDirectMethodHierByDescriptor(clazz, methodName,
+                        methodDescr);
+        }
+    }
+
+    if (method != NULL) {
+        /*
+         * Got it.  See if there's a register map here.
+         */
+        const RegisterMap* pMap;
+        pMap = dvmGetExpandedRegisterMap(method);
+        if (pMap == NULL) {
+            ALOGV("No map for %s.%s %s",
+                classAndMethodDesc, methodName, methodDescr);
+        } else {
+            ALOGV("Found map %s.%s %s",
+                classAndMethodDesc, methodName, methodDescr);
+            result = true;
+        }
+    } else {
+        ALOGV("Unable to find %s.%s %s",
+            classAndMethodDesc, methodName, methodDescr);
+    }
+
+bail:
+    free(classAndMethodDesc);
+    RETURN_BOOLEAN(result);
+}
+
+/*
+ * static void dumpReferenceTables()
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    UNUSED_PARAMETER(pResult);
+
+    ALOGI("--- reference table dump ---");
+    dvmDumpJniReferenceTables();
+    // could dump thread's internalLocalRefTable, probably not useful
+    // ditto for thread's jniMonitorRefTable
+    ALOGI("---");
+    RETURN_VOID();
+}
+
+/*
+ * static void crash()
+ *
+ * Dump the current thread's interpreted stack and abort the VM.  Useful
+ * for seeing both interpreted and native stack traces.
+ *
+ * (Might want to restrict this to debuggable processes as a security
+ * measure, or check SecurityManager.checkExit().)
+ */
+static void Dalvik_dalvik_system_VMDebug_crash(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    UNUSED_PARAMETER(pResult);
+
+    ALOGW("Crashing VM on request");
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+}
+
+/*
+ * static void infopoint(int id)
+ *
+ * Provide a hook for gdb to hang to so that the VM can be stopped when
+ * user-tagged source locations are being executed.
+ */
+static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args,
+    JValue* pResult)
+{
+    gDvm.nativeDebuggerActive = true;
+
+    ALOGD("VMDebug infopoint %d hit", args[0]);
+
+    gDvm.nativeDebuggerActive = false;
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*)args[0];
+    bool countAssignable = args[1];
+    if (clazz == NULL) {
+        RETURN_LONG(0);
+    }
+    if (countAssignable) {
+        size_t count = dvmCountAssignableInstancesOfClass(clazz);
+        RETURN_LONG((long long)count);
+    } else {
+        size_t count = dvmCountInstancesOfClass(clazz);
+        RETURN_LONG((long long)count);
+    }
+}
+
+/*
+ * public static native void getHeapSpaceStats(long[] data)
+ */
+static void Dalvik_dalvik_system_VMDebug_getHeapSpaceStats(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* dataArray = (ArrayObject*) args[0];
+
+    if (dataArray == NULL || dataArray->length < 6) {
+      RETURN_VOID();
+    }
+
+    jlong* arr = (jlong*)(void*)dataArray->contents;
+
+    int j = 0;
+    size_t per_heap_allocated[2];
+    size_t per_heap_size[2];
+    memset(per_heap_allocated, 0, sizeof(per_heap_allocated));
+    memset(per_heap_size, 0, sizeof(per_heap_size));
+    dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, (size_t*) &per_heap_allocated, 2);
+    dvmHeapSourceGetValue(HS_FOOTPRINT, (size_t*) &per_heap_size, 2);
+    jlong heapSize = per_heap_size[0];
+    jlong heapUsed = per_heap_allocated[0];
+    jlong heapFree = heapSize - heapUsed;
+    jlong zygoteSize = per_heap_size[1];
+    jlong zygoteUsed = per_heap_allocated[1];
+    jlong zygoteFree = zygoteSize - zygoteUsed;
+    arr[j++] = heapSize;
+    arr[j++] = heapUsed;
+    arr[j++] = heapFree;
+    arr[j++] = zygoteSize;
+    arr[j++] = zygoteUsed;
+    arr[j++] = zygoteFree;
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = {
+    { "getVmFeatureList",           "()[Ljava/lang/String;",
+        Dalvik_dalvik_system_VMDebug_getVmFeatureList },
+    { "getAllocCount",              "(I)I",
+        Dalvik_dalvik_system_VMDebug_getAllocCount },
+    { "getHeapSpaceStats",          "([J)V",
+        Dalvik_dalvik_system_VMDebug_getHeapSpaceStats },
+    { "resetAllocCount",            "(I)V",
+        Dalvik_dalvik_system_VMDebug_resetAllocCount },
+    { "startAllocCounting",         "()V",
+        Dalvik_dalvik_system_VMDebug_startAllocCounting },
+    { "stopAllocCounting",          "()V",
+        Dalvik_dalvik_system_VMDebug_stopAllocCounting },
+    { "startMethodTracingDdmsImpl", "(IIZI)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingDdmsImpl },
+    { "startMethodTracingFd",       "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZI)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingFd },
+    { "startMethodTracingFilename", "(Ljava/lang/String;IIZI)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingFilename },
+    { "getMethodTracingMode",       "()I",
+        Dalvik_dalvik_system_VMDebug_getMethodTracingMode },
+    { "stopMethodTracing",          "()V",
+        Dalvik_dalvik_system_VMDebug_stopMethodTracing },
+    { "startEmulatorTracing",       "()V",
+        Dalvik_dalvik_system_VMDebug_startEmulatorTracing },
+    { "stopEmulatorTracing",        "()V",
+        Dalvik_dalvik_system_VMDebug_stopEmulatorTracing },
+    { "startInstructionCounting",   "()V",
+        Dalvik_dalvik_system_VMDebug_startInstructionCounting },
+    { "stopInstructionCounting",    "()V",
+        Dalvik_dalvik_system_VMDebug_stopInstructionCounting },
+    { "resetInstructionCount",      "()V",
+        Dalvik_dalvik_system_VMDebug_resetInstructionCount },
+    { "getInstructionCount",        "([I)V",
+        Dalvik_dalvik_system_VMDebug_getInstructionCount },
+    { "isDebuggerConnected",        "()Z",
+        Dalvik_dalvik_system_VMDebug_isDebuggerConnected },
+    { "isDebuggingEnabled",         "()Z",
+        Dalvik_dalvik_system_VMDebug_isDebuggingEnabled },
+    { "lastDebuggerActivity",       "()J",
+        Dalvik_dalvik_system_VMDebug_lastDebuggerActivity },
+    { "printLoadedClasses",         "(I)V",
+        Dalvik_dalvik_system_VMDebug_printLoadedClasses },
+    { "getLoadedClassCount",        "()I",
+        Dalvik_dalvik_system_VMDebug_getLoadedClassCount },
+    { "threadCpuTimeNanos",         "()J",
+        Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos },
+    { "dumpHprofData",              "(Ljava/lang/String;Ljava/io/FileDescriptor;)V",
+        Dalvik_dalvik_system_VMDebug_dumpHprofData },
+    { "dumpHprofDataDdms",          "()V",
+        Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms },
+    { "cacheRegisterMap",           "(Ljava/lang/String;)Z",
+        Dalvik_dalvik_system_VMDebug_cacheRegisterMap },
+    { "dumpReferenceTables",        "()V",
+        Dalvik_dalvik_system_VMDebug_dumpReferenceTables },
+    { "crash",                      "()V",
+        Dalvik_dalvik_system_VMDebug_crash },
+    { "infopoint",                 "(I)V",
+        Dalvik_dalvik_system_VMDebug_infopoint },
+    { "countInstancesOfClass",     "(Ljava/lang/Class;Z)J",
+        Dalvik_dalvik_system_VMDebug_countInstancesOfClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMRuntime.cpp b/vm/native/dalvik_system_VMRuntime.cpp
new file mode 100644
index 0000000..739dd1b
--- /dev/null
+++ b/vm/native/dalvik_system_VMRuntime.cpp
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * dalvik.system.VMRuntime
+ */
+#include "Dalvik.h"
+#include "ScopedPthreadMutexLock.h"
+#include "UniquePtr.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+#include "libdex/DexClass.h"
+#include "native/InternalNativePriv.h"
+
+#include <limits.h>
+
+#include <map>
+
+/*
+ * public native float getTargetHeapUtilization()
+ *
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+static void Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_FLOAT(dvmGetTargetHeapUtilization());
+}
+
+/*
+ * native float nativeSetTargetHeapUtilization()
+ *
+ * Sets the current ideal heap utilization, represented as a number
+ * between zero and one.  Returns the old utilization.
+ *
+ * Note that this is NOT static.
+ */
+static void Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization(
+    const u4* args, JValue* pResult)
+{
+    dvmSetTargetHeapUtilization(dvmU4ToFloat(args[1]));
+
+    RETURN_VOID();
+}
+
+/*
+ * public native void startJitCompilation()
+ *
+ * Callback function from the framework to indicate that an app has gone
+ * through the startup phase and it is time to enable the JIT compiler.
+ */
+static void Dalvik_dalvik_system_VMRuntime_startJitCompilation(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_JIT)
+    if (gDvm.executionMode == kExecutionModeJit && gDvmJit.disableJit == false) {
+        ScopedPthreadMutexLock lock(&gDvmJit.compilerLock);
+        gDvmJit.alreadyEnabledViaFramework = true;
+        pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+    }
+#endif
+    RETURN_VOID();
+}
+
+/*
+ * public native void disableJitCompilation()
+ *
+ * Callback function from the framework to indicate that a VM instance wants to
+ * permanently disable the JIT compiler. Currently only the system server uses
+ * this interface when it detects system-wide safe mode is enabled.
+ */
+static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_JIT)
+    if (gDvm.executionMode == kExecutionModeJit) {
+        gDvmJit.disableJit = true;
+    }
+#endif
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_newNonMovableArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[1];
+    int length = args[2];
+
+    if (elementClass == NULL) {
+        dvmThrowNullPointerException("elementClass == null");
+        RETURN_PTR(NULL);
+    }
+    if (length < 0) {
+        dvmThrowNegativeArraySizeException(length);
+        RETURN_PTR(NULL);
+    }
+
+    // TODO: right now, we don't have a copying collector, so there's no need
+    // to do anything special here, but we ought to pass the non-movability
+    // through to the allocator.
+    ClassObject* arrayClass = dvmFindArrayClassForElement(elementClass);
+    ArrayObject* newArray = dvmAllocArrayByClass(arrayClass,
+                                                 length,
+                                                 ALLOC_NON_MOVING);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_PTR(NULL);
+    }
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+
+    RETURN_PTR(newArray);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_addressOf(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* array = (ArrayObject*) args[1];
+    if (!dvmIsArray(array)) {
+        dvmThrowIllegalArgumentException(NULL);
+        RETURN_VOID();
+    }
+    // TODO: we should also check that this is a non-movable array.
+    s8 result = (uintptr_t) array->contents;
+    RETURN_LONG(result);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_clearGrowthLimit(const u4* args,
+    JValue* pResult)
+{
+    dvmClearGrowthLimit();
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_isDebuggerActive(
+    const u4* args, JValue* pResult)
+{
+    RETURN_BOOLEAN(gDvm.debuggerActive || gDvm.nativeDebuggerActive);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_properties(const u4* args,
+    JValue* pResult)
+{
+    ArrayObject* result = dvmCreateStringArray(*gDvm.properties);
+    dvmReleaseTrackedAlloc((Object*) result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+static void returnCString(JValue* pResult, const char* s)
+{
+    Object* result = (Object*) dvmCreateStringFromCstr(s);
+    dvmReleaseTrackedAlloc(result, dvmThreadSelf());
+    RETURN_PTR(result);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_bootClassPath(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, gDvm.bootClassPathStr);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_classPath(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, gDvm.classPathStr);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_vmVersion(const u4* args,
+    JValue* pResult)
+{
+    char buf[64];
+    sprintf(buf, "%d.%d.%d",
+            DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    returnCString(pResult, buf);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_vmLibrary(const u4* args,
+    JValue* pResult)
+{
+    returnCString(pResult, "libdvm.so");
+}
+
+static void Dalvik_dalvik_system_VMRuntime_isCheckJniEnabled(const u4* args, JValue* pResult)
+{
+    RETURN_BOOLEAN(gDvmJni.useCheckJni);
+}
+
+static void Dalvik_dalvik_system_VMRuntime_setTargetSdkVersionNative(
+    const u4* args,
+    JValue* pResult)
+{
+    // This is the target SDK version of the app we're about to run.
+    // Note that this value may be CUR_DEVELOPMENT (10000).
+    // Note that this value may be 0, meaning "current".
+    int targetSdkVersion = args[1];
+    if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) {
+        if (gDvmJni.useCheckJni) {
+            ALOGI("CheckJNI enabled: not enabling JNI app bug workarounds.");
+        } else {
+            ALOGI("Enabling JNI app bug workarounds for target SDK version %i...",
+                  targetSdkVersion);
+            gDvmJni.workAroundAppJniBugs = true;
+        }
+    }
+    RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_registerNativeAllocation(const u4* args,
+                                                                    JValue* pResult)
+{
+  int bytes = args[1];
+  if (bytes < 0) {
+    dvmThrowRuntimeException("allocation size negative");
+  } else {
+    dvmHeapSourceRegisterNativeAllocation(bytes);
+  }
+  RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_registerNativeFree(const u4* args,
+                                                              JValue* pResult)
+{
+  int bytes = args[1];
+  if (bytes < 0) {
+    dvmThrowRuntimeException("allocation size negative");
+  } else {
+    dvmHeapSourceRegisterNativeFree(bytes);
+  }
+  RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMRuntime_updateProcessState(const u4* args,
+                                                              JValue* pResult)
+{
+  RETURN_VOID();
+}
+
+
+static DvmDex* getDvmDexFromClassPathEntry(ClassPathEntry* cpe) {
+    if (cpe->kind == kCpeDex) {
+        return ((RawDexFile*) cpe->ptr)->pDvmDex;
+    }
+    if (cpe->kind == kCpeJar) {
+        return ((JarFile*) cpe->ptr)->pDvmDex;
+    }
+    LOG_ALWAYS_FATAL("Unknown cpe->kind=%d", cpe->kind);
+}
+
+typedef std::map<std::string, StringObject*> StringTable;
+
+static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) {
+    StringTable& table = *(StringTable*) arg;
+    StringObject* strObj = *(StringObject**) addr;
+    LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string");
+    char* newStr = dvmCreateCstrFromString(strObj);
+    // ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr);
+    table[newStr] = strObj;
+    free(newStr);
+}
+
+// Based on dvmResolveString.
+static void preloadDexCachesResolveString(DvmDex* pDvmDex,
+                                          uint32_t stringIdx,
+                                          StringTable& strings) {
+    StringObject* string = dvmDexGetResolvedString(pDvmDex, stringIdx);
+    if (string != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    uint32_t utf16Size;
+    const char* utf8 = dexStringAndSizeById(pDexFile, stringIdx, &utf16Size);
+    string = strings[utf8];
+    if (string == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found string=%s", utf8);
+    dvmDexSetResolvedString(pDvmDex, stringIdx, string);
+}
+
+// Based on dvmResolveClass.
+static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) {
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx);
+    if (clazz != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const char* className = dexStringByTypeIdx(pDexFile, typeIdx);
+    if (className[0] != '\0' && className[1] == '\0') {
+        /* primitive type */
+        clazz = dvmFindPrimitiveClass(className[0]);
+    } else {
+        clazz = dvmLookupClass(className, NULL, true);
+    }
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip uninitialized classes because filled cache entry implies it is initialized.
+    if (!dvmIsClassInitialized(clazz)) {
+        // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className);
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className);
+    dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz);
+}
+
+// Based on dvmResolveInstField/dvmResolveStaticField.
+static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) {
+    Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx);
+    if (field != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static fields for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if (!instance && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx);
+    const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    if (instance) {
+        field = dvmFindInstanceFieldHier(clazz, fieldName, signature);
+    } else {
+        field = dvmFindStaticFieldHier(clazz, fieldName, signature);
+    }
+    if (field == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s",
+    //       signature, clazz->descriptor, fieldName);
+    dvmDexSetResolvedField(pDvmDex, fieldIdx, field);
+}
+
+// Based on dvmResolveMethod.
+static void preloadDexCachesResolveMethod(DvmDex* pDvmDex,
+                                          uint32_t methodIdx,
+                                          MethodType methodType) {
+    Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+    if (method != NULL) {
+        return;
+    }
+    const DexFile* pDexFile = pDvmDex->pDexFile;
+    const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx);
+    if (clazz == NULL) {
+        return;
+    }
+    // Skip static methods for uninitialized classes because a filled
+    // cache entry implies the class is initialized.
+    if ((methodType == METHOD_STATIC) && !dvmIsClassInitialized(clazz)) {
+        return;
+    }
+    const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+    if (methodType == METHOD_DIRECT) {
+        method = dvmFindDirectMethod(clazz, methodName, &proto);
+    } else if (methodType == METHOD_STATIC) {
+        method = dvmFindDirectMethodHier(clazz, methodName, &proto);
+    } else {
+        method = dvmFindVirtualMethodHier(clazz, methodName, &proto);
+    }
+    if (method == NULL) {
+        return;
+    }
+    // ALOGI("VMRuntime.preloadDexCaches found method %s.%s",
+    //        clazz->descriptor, methodName);
+    dvmDexSetResolvedMethod(pDvmDex, methodIdx, method);
+}
+
+struct DexCacheStats {
+    uint32_t numStrings;
+    uint32_t numTypes;
+    uint32_t numFields;
+    uint32_t numMethods;
+    DexCacheStats() : numStrings(0), numTypes(0), numFields(0), numMethods(0) {};
+};
+
+static const bool kPreloadDexCachesEnabled = true;
+
+// Disabled because it takes a long time (extra half second) but
+// gives almost no benefit in terms of saving private dirty pages.
+static const bool kPreloadDexCachesStrings = false;
+
+static const bool kPreloadDexCachesTypes = true;
+static const bool kPreloadDexCachesFieldsAndMethods = true;
+
+static const bool kPreloadDexCachesCollectStats = false;
+
+static void preloadDexCachesStatsTotal(DexCacheStats* total) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        total->numStrings += pHeader->stringIdsSize;
+        total->numFields += pHeader->fieldIdsSize;
+        total->numMethods += pHeader->methodIdsSize;
+        total->numTypes += pHeader->typeIdsSize;
+    }
+}
+
+static void preloadDexCachesStatsFilled(DexCacheStats* filled) {
+    if (!kPreloadDexCachesCollectStats) {
+        return;
+    }
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
+            StringObject* string = dvmDexGetResolvedString(pDvmDex, i);
+            if (string != NULL) {
+                filled->numStrings++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
+            ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, i);
+            if (clazz != NULL) {
+                filled->numTypes++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->fieldIdsSize; i++) {
+            Field* field = dvmDexGetResolvedField(pDvmDex, i);
+            if (field != NULL) {
+                filled->numFields++;
+            }
+        }
+        for (size_t i = 0; i < pHeader->methodIdsSize; i++) {
+            Method* method = dvmDexGetResolvedMethod(pDvmDex, i);
+            if (method != NULL) {
+                filled->numMethods++;
+            }
+        }
+    }
+}
+
+static void Dalvik_dalvik_system_VMRuntime_preloadDexCaches(const u4* args, JValue* pResult)
+{
+    if (!kPreloadDexCachesEnabled) {
+        return;
+    }
+
+    DexCacheStats total;
+    DexCacheStats before;
+    if (kPreloadDexCachesCollectStats) {
+        ALOGI("VMRuntime.preloadDexCaches starting");
+        preloadDexCachesStatsTotal(&total);
+        preloadDexCachesStatsFilled(&before);
+    }
+
+    // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
+    StringTable strings;
+    if (kPreloadDexCachesStrings) {
+        dvmLockMutex(&gDvm.internLock);
+        dvmHashTableLock(gDvm.literalStrings);
+        for (int i = 0; i < gDvm.literalStrings->tableSize; ++i) {
+            HashEntry *entry = &gDvm.literalStrings->pEntries[i];
+            if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+                preloadDexCachesStringsVisitor(&entry->data, 0, ROOT_INTERNED_STRING, &strings);
+            }
+        }
+        dvmHashTableUnlock(gDvm.literalStrings);
+        dvmUnlockMutex(&gDvm.internLock);
+    }
+
+    for (ClassPathEntry* cpe = gDvm.bootClassPath; cpe->kind != kCpeLastEntry; cpe++) {
+        DvmDex* pDvmDex = getDvmDexFromClassPathEntry(cpe);
+        const DexHeader* pHeader = pDvmDex->pHeader;
+        const DexFile* pDexFile = pDvmDex->pDexFile;
+
+        if (kPreloadDexCachesStrings) {
+            for (size_t i = 0; i < pHeader->stringIdsSize; i++) {
+                preloadDexCachesResolveString(pDvmDex, i, strings);
+            }
+        }
+
+        if (kPreloadDexCachesTypes) {
+            for (size_t i = 0; i < pHeader->typeIdsSize; i++) {
+                preloadDexCachesResolveType(pDvmDex, i);
+            }
+        }
+
+        if (kPreloadDexCachesFieldsAndMethods) {
+            for (size_t classDefIndex = 0;
+                 classDefIndex < pHeader->classDefsSize;
+                 classDefIndex++) {
+                const DexClassDef* pClassDef = dexGetClassDef(pDexFile, classDefIndex);
+                const u1* pEncodedData = dexGetClassData(pDexFile, pClassDef);
+                UniquePtr<DexClassData> pClassData(dexReadAndVerifyClassData(&pEncodedData, NULL));
+                if (pClassData.get() == NULL) {
+                    continue;
+                }
+                for (uint32_t fieldIndex = 0;
+                     fieldIndex < pClassData->header.staticFieldsSize;
+                     fieldIndex++) {
+                    const DexField* pField = &pClassData->staticFields[fieldIndex];
+                    preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, false);
+                }
+                for (uint32_t fieldIndex = 0;
+                     fieldIndex < pClassData->header.instanceFieldsSize;
+                     fieldIndex++) {
+                    const DexField* pField = &pClassData->instanceFields[fieldIndex];
+                    preloadDexCachesResolveField(pDvmDex, pField->fieldIdx, true);
+                }
+                for (uint32_t methodIndex = 0;
+                     methodIndex < pClassData->header.directMethodsSize;
+                     methodIndex++) {
+                    const DexMethod* pDexMethod = &pClassData->directMethods[methodIndex];
+                    MethodType methodType = (((pDexMethod->accessFlags & ACC_STATIC) != 0) ?
+                                             METHOD_STATIC :
+                                             METHOD_DIRECT);
+                    preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, methodType);
+                }
+                for (uint32_t methodIndex = 0;
+                     methodIndex < pClassData->header.virtualMethodsSize;
+                     methodIndex++) {
+                    const DexMethod* pDexMethod = &pClassData->virtualMethods[methodIndex];
+                    preloadDexCachesResolveMethod(pDvmDex, pDexMethod->methodIdx, METHOD_VIRTUAL);
+                }
+            }
+        }
+    }
+
+    if (kPreloadDexCachesCollectStats) {
+        DexCacheStats after;
+        preloadDexCachesStatsFilled(&after);
+        ALOGI("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d",
+              total.numStrings, before.numStrings, after.numStrings);
+        ALOGI("VMRuntime.preloadDexCaches types total=%d before=%d after=%d",
+              total.numTypes, before.numTypes, after.numTypes);
+        ALOGI("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d",
+              total.numFields, before.numFields, after.numFields);
+        ALOGI("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d",
+              total.numMethods, before.numMethods, after.numMethods);
+        ALOGI("VMRuntime.preloadDexCaches finished");
+    }
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
+    { "addressOf", "(Ljava/lang/Object;)J",
+        Dalvik_dalvik_system_VMRuntime_addressOf },
+    { "bootClassPath", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_bootClassPath },
+    { "classPath", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_classPath },
+    { "clearGrowthLimit", "()V",
+        Dalvik_dalvik_system_VMRuntime_clearGrowthLimit },
+    { "disableJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
+    { "isDebuggerActive", "()Z",
+        Dalvik_dalvik_system_VMRuntime_isDebuggerActive },
+    { "getTargetHeapUtilization", "()F",
+        Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
+    { "nativeSetTargetHeapUtilization", "(F)V",
+        Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization },
+    { "newNonMovableArray", "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_dalvik_system_VMRuntime_newNonMovableArray },
+    { "properties", "()[Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_properties },
+    { "setTargetSdkVersionNative", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_setTargetSdkVersionNative },
+    { "startJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_startJitCompilation },
+    { "vmVersion", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_vmVersion },
+    { "vmLibrary", "()Ljava/lang/String;",
+        Dalvik_dalvik_system_VMRuntime_vmLibrary },
+    { "isCheckJniEnabled", "()Z",
+        Dalvik_dalvik_system_VMRuntime_isCheckJniEnabled },
+    { "registerNativeAllocation", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_registerNativeAllocation },
+    { "registerNativeFree", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_registerNativeFree },
+    { "updateProcessState", "(I)V",
+        Dalvik_dalvik_system_VMRuntime_updateProcessState },
+    { "preloadDexCaches", "()V",
+        Dalvik_dalvik_system_VMRuntime_preloadDexCaches },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMStack.cpp b/vm/native/dalvik_system_VMStack.cpp
new file mode 100644
index 0000000..db627ef
--- /dev/null
+++ b/vm/native/dalvik_system_VMStack.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * dalvik.system.VMStack
+ */
+#include "Dalvik.h"
+#include "UniquePtr.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * public static ClassLoader getCallingClassLoader()
+ *
+ * Return the defining class loader of the caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getCallingClassLoader(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz =
+        dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    if (clazz == NULL)
+        RETURN_PTR(NULL);
+    RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public static Class<?> getStackClass2()
+ *
+ * Returns the class of the caller's caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getStackClass2(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz =
+        dvmGetCaller3Class(dvmThreadSelf()->interpSave.curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    RETURN_PTR(clazz);
+}
+
+/*
+ * public static Class<?>[] getClasses(int maxDepth)
+ *
+ * Create an array of classes for the methods on the stack, skipping the
+ * first two and all reflection methods.  If "stopAtPrivileged" is set,
+ * stop shortly after we encounter a privileged class.
+ */
+static void Dalvik_dalvik_system_VMStack_getClasses(const u4* args,
+    JValue* pResult)
+{
+    /* note "maxSize" is unsigned, so -1 turns into a very large value */
+    size_t maxSize = args[0];
+    size_t size = 0;
+    const size_t kSkip = 2;
+
+    /*
+     * Get an array with the stack trace in it.
+     */
+    void *fp = dvmThreadSelf()->interpSave.curFrame;
+    size_t depth = dvmComputeExactFrameDepth(fp);
+    UniquePtr<const Method*[]> methods(new const Method*[depth]);
+    dvmFillStackTraceArray(fp, methods.get(), depth);
+
+    /*
+     * Run through the array and count up how many elements there are.
+     */
+    for (size_t i = kSkip; i < depth && size < maxSize; ++i) {
+        const Method* meth = methods[i];
+
+        if (dvmIsReflectionMethod(meth))
+            continue;
+
+        size++;
+    }
+
+    /*
+     * Create an array object to hold the classes.
+     * TODO: can use gDvm.classJavaLangClassArray here?
+     */
+    ClassObject* classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;",
+                                                     NULL);
+    if (classArrayClass == NULL) {
+        ALOGW("Unable to find java.lang.Class array class");
+        return;
+    }
+    ArrayObject* classes = dvmAllocArrayByClass(classArrayClass,
+                                                size,
+                                                ALLOC_DEFAULT);
+    if (classes == NULL) {
+        ALOGW("Unable to allocate class array of %zd elements", size);
+        return;
+    }
+
+    /*
+     * Fill in the array.
+     */
+    size_t objCount = 0;
+    for (size_t i = kSkip; i < depth; ++i) {
+        if (dvmIsReflectionMethod(methods[i])) {
+            continue;
+        }
+        Object* klass = (Object *)methods[i]->clazz;
+        dvmSetObjectArrayElement(classes, objCount, klass);
+        objCount++;
+    }
+    assert(objCount == classes->length);
+
+    dvmReleaseTrackedAlloc((Object*)classes, NULL);
+    RETURN_PTR(classes);
+}
+
+/*
+ * Return a trace buffer for the specified thread or NULL if the
+ * thread is not still alive. *depth is set to the length of a
+ * non-NULL trace buffer. Caller is responsible for freeing the trace
+ * buffer.
+ */
+static int* getTraceBuf(Object* targetThreadObj, size_t* pStackDepth)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int* traceBuf;
+
+    assert(targetThreadObj != NULL);
+
+    dvmLockThreadList(self);
+
+    /*
+     * Make sure the thread is still alive and in the list.
+     */
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        if (thread->threadObj == targetThreadObj)
+            break;
+    }
+    if (thread == NULL) {
+        ALOGI("VMStack.getTraceBuf: threadObj %p not active",
+            targetThreadObj);
+        dvmUnlockThreadList();
+        return NULL;
+    }
+
+    /*
+     * Suspend the thread, pull out the stack trace, then resume the thread
+     * and release the thread list lock.  If we're being asked to examine
+     * our own stack trace, skip the suspend/resume.
+     */
+    if (thread != self)
+        dvmSuspendThread(thread);
+    traceBuf = dvmFillInStackTraceRaw(thread, pStackDepth);
+    if (thread != self)
+        dvmResumeThread(thread);
+    dvmUnlockThreadList();
+
+    return traceBuf;
+}
+
+/*
+ * public static StackTraceElement[] getThreadStackTrace(Thread t)
+ *
+ * Retrieve the stack trace of the specified thread and return it as an
+ * array of StackTraceElement.  Returns NULL on failure.
+ */
+static void Dalvik_dalvik_system_VMStack_getThreadStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* targetThreadObj = (Object*) args[0];
+    size_t stackDepth;
+    int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
+
+    if (traceBuf == NULL)
+        RETURN_PTR(NULL);
+
+    /*
+     * Convert the raw buffer into an array of StackTraceElement.
+     */
+    ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+    free(traceBuf);
+    RETURN_PTR(trace);
+}
+
+/*
+ * public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements)
+ *
+ * Retrieve a partial stack trace of the specified thread and return
+ * the number of frames filled.  Returns 0 on failure.
+ */
+static void Dalvik_dalvik_system_VMStack_fillStackTraceElements(const u4* args,
+    JValue* pResult)
+{
+    Object* targetThreadObj = (Object*) args[0];
+    ArrayObject* steArray = (ArrayObject*) args[1];
+    size_t stackDepth;
+    int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
+
+    if (traceBuf == NULL)
+        RETURN_PTR(NULL);
+
+    /*
+     * Set the raw buffer into an array of StackTraceElement.
+     */
+    if (stackDepth > steArray->length) {
+        stackDepth = steArray->length;
+    }
+    dvmFillStackTraceElements(traceBuf, stackDepth, steArray);
+    free(traceBuf);
+    RETURN_INT(stackDepth);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
+    { "getCallingClassLoader",  "()Ljava/lang/ClassLoader;",
+        Dalvik_dalvik_system_VMStack_getCallingClassLoader },
+    { "getStackClass2",         "()Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getStackClass2 },
+    { "getClasses",             "(I)[Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getClasses },
+    { "getThreadStackTrace",    "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
+        Dalvik_dalvik_system_VMStack_getThreadStackTrace },
+    { "fillStackTraceElements", "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I",
+        Dalvik_dalvik_system_VMStack_fillStackTraceElements },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_ZygoteHooks.cpp b/vm/native/dalvik_system_ZygoteHooks.cpp
new file mode 100644
index 0000000..717803d
--- /dev/null
+++ b/vm/native/dalvik_system_ZygoteHooks.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * dalvik.system.Zygote
+ */
+#include "Dalvik.h"
+#include "Thread.h"
+#include "native/InternalNativePriv.h"
+
+#include <sys/resource.h>
+
+#if defined(HAVE_PRCTL)
+# include <sys/prctl.h>
+#endif
+
+#define ZYGOTE_LOG_TAG "Zygote"
+
+/* must match values in com.android.internal.os.Zygote */
+enum {
+    DEBUG_ENABLE_DEBUGGER           = 1,
+    DEBUG_ENABLE_CHECKJNI           = 1 << 1,
+    DEBUG_ENABLE_ASSERT             = 1 << 2,
+    DEBUG_ENABLE_SAFEMODE           = 1 << 3,
+    DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
+};
+
+/*
+ * Enable/disable debug features requested by the caller.
+ *
+ * debugger
+ *   If set, enable debugging; if not set, disable debugging.  This is
+ *   easy to handle, because the JDWP thread isn't started until we call
+ *   dvmInitAfterZygote().
+ * checkjni
+ *   If set, make sure "check JNI" is enabled.
+ * assert
+ *   If set, make sure assertions are enabled.  This gets fairly weird,
+ *   because it affects the result of a method called by class initializers,
+ *   and hence can't affect pre-loaded/initialized classes.
+ * safemode
+ *   If set, operates the VM in the safe mode. The definition of "safe mode" is
+ *   implementation dependent and currently only the JIT compiler is disabled.
+ *   This is easy to handle because the compiler thread and associated resources
+ *   are not requested until we call dvmInitAfterZygote().
+ */
+static void enableDebugFeatures(u4 debugFlags)
+{
+    gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
+
+    if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
+        /* turn it on if it's not already enabled */
+        dvmLateEnableCheckedJni();
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_JNI_LOGGING) != 0) {
+        gDvmJni.logThirdPartyJni = true;
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
+        /* turn it on if it's not already enabled */
+        dvmLateEnableAssertions();
+    }
+
+    if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
+#if defined(WITH_JIT)
+        /* turn off the jit if it is explicitly requested by the app */
+        if (gDvm.executionMode == kExecutionModeJit)
+            gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+    }
+
+#ifdef HAVE_ANDROID_OS
+    if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
+        /* To let a non-privileged gdbserver attach to this
+         * process, we must set its dumpable bit flag. However
+         * we are not interested in generating a coredump in
+         * case of a crash, so also set the coredump size to 0
+         * to disable that
+         */
+        if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+            ALOGE("could not set dumpable bit flag for pid %d: %s",
+                 getpid(), strerror(errno));
+        } else {
+            struct rlimit rl;
+            rl.rlim_cur = 0;
+            rl.rlim_max = RLIM_INFINITY;
+            if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+                ALOGE("could not disable core file generation for pid %d: %s",
+                    getpid(), strerror(errno));
+            }
+        }
+    }
+#endif
+}
+
+/*
+ * native public static long nativePreFork()
+ */
+static void Dalvik_dalvik_system_ZygoteHooks_preFork(const u4* args,
+    JValue* pResult)
+{
+    dvmDumpLoaderStats("zygote");
+
+    if (!gDvm.zygote) {
+        dvmThrowIllegalStateException(
+            "VM instance not started with -Xzygote");
+
+        RETURN_LONG(-1L);
+    }
+
+    if (!dvmGcPreZygoteFork()) {
+        ALOGE("pre-fork heap failed");
+        dvmAbort();
+    }
+
+    RETURN_LONG(0L);
+}
+
+/*
+ * native public static int nativePostForkChild(long token, int debug_flags),
+ */
+static void Dalvik_dalvik_system_ZygoteHooks_postForkChild(
+        const u4* args, JValue* pResult)
+{
+    /*
+     * Our system thread ID has changed.  Get the new one.
+     */
+    Thread* thread = dvmThreadSelf();
+    thread->systemTid = dvmGetSysThreadId();
+
+    /* configure additional debug options */
+    enableDebugFeatures(args[2]);
+
+    gDvm.zygote = false;
+    if (!dvmInitAfterZygote()) {
+        ALOGE("error in post-zygote initialization");
+        dvmAbort();
+    }
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_ZygoteHooks[] = {
+    { "nativePreFork", "()J",
+      Dalvik_dalvik_system_ZygoteHooks_preFork },
+    { "nativePostForkChild", "(JI)V",
+      Dalvik_dalvik_system_ZygoteHooks_postForkChild },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Class.cpp b/vm/native/java_lang_Class.cpp
new file mode 100644
index 0000000..f8a3a92
--- /dev/null
+++ b/vm/native/java_lang_Class.cpp
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.Class
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+#include "ScopedPthreadMutexLock.h"
+
+/*
+ * native public boolean desiredAssertionStatus()
+ *
+ * Determine the class-init-time assertion status of a class.  This is
+ * called from <clinit> in javac-generated classes that use the Java
+ * programming language "assert" keyword.
+ */
+static void Dalvik_java_lang_Class_desiredAssertionStatus(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+    char* className = dvmDescriptorToName(thisPtr->descriptor);
+    int i;
+    bool enable = false;
+
+    /*
+     * Run through the list of arguments specified on the command line.  The
+     * last matching argument takes precedence.
+     */
+    for (i = 0; i < gDvm.assertionCtrlCount; i++) {
+        const AssertionControl* pCtrl = &gDvm.assertionCtrl[i];
+
+        if (pCtrl->isPackage) {
+            /*
+             * Given "dalvik/system/Debug" or "MyStuff", compute the
+             * length of the package portion of the class name string.
+             *
+             * Unlike most package operations, we allow matching on
+             * "sub-packages", so "dalvik..." will match "dalvik.Foo"
+             * and "dalvik.system.Foo".
+             *
+             * The pkgOrClass string looks like "dalvik/system/", i.e. it still
+             * has the terminating slash, so we can be sure we're comparing
+             * against full package component names.
+             */
+            const char* lastSlash;
+            int pkgLen;
+
+            lastSlash = strrchr(className, '/');
+            if (lastSlash == NULL) {
+                pkgLen = 0;
+            } else {
+                pkgLen = lastSlash - className +1;
+            }
+
+            if (pCtrl->pkgOrClassLen > pkgLen ||
+                memcmp(pCtrl->pkgOrClass, className, pCtrl->pkgOrClassLen) != 0)
+            {
+                ALOGV("ASRT: pkg no match: '%s'(%d) vs '%s'",
+                    className, pkgLen, pCtrl->pkgOrClass);
+            } else {
+                ALOGV("ASRT: pkg match: '%s'(%d) vs '%s' --> %d",
+                    className, pkgLen, pCtrl->pkgOrClass, pCtrl->enable);
+                enable = pCtrl->enable;
+            }
+        } else {
+            /*
+             * "pkgOrClass" holds a fully-qualified class name, converted from
+             * dot-form to slash-form.  An empty string means all classes.
+             */
+            if (pCtrl->pkgOrClass == NULL) {
+                /* -esa/-dsa; see if class is a "system" class */
+                if (strncmp(className, "java/", 5) != 0) {
+                    ALOGV("ASRT: sys no match: '%s'", className);
+                } else {
+                    ALOGV("ASRT: sys match: '%s' --> %d",
+                        className, pCtrl->enable);
+                    enable = pCtrl->enable;
+                }
+            } else if (*pCtrl->pkgOrClass == '\0') {
+                ALOGV("ASRT: class all: '%s' --> %d",
+                    className, pCtrl->enable);
+                enable = pCtrl->enable;
+            } else {
+                if (strcmp(pCtrl->pkgOrClass, className) != 0) {
+                    ALOGV("ASRT: cls no match: '%s' vs '%s'",
+                        className, pCtrl->pkgOrClass);
+                } else {
+                    ALOGV("ASRT: cls match: '%s' vs '%s' --> %d",
+                        className, pCtrl->pkgOrClass, pCtrl->enable);
+                    enable = pCtrl->enable;
+                }
+            }
+        }
+    }
+
+    free(className);
+    RETURN_INT(enable);
+}
+
+/*
+ * static public Class<?> classForName(String name, boolean initialize,
+ *     ClassLoader loader)
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    bool initialize = (args[1] != 0);
+    Object* loader = (Object*) args[2];
+
+    RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize));
+}
+
+/*
+ * static private ClassLoader getClassLoader(Class clazz)
+ *
+ * Return the class' defining class loader.
+ */
+static void Dalvik_java_lang_Class_getClassLoader(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public Class<?> getComponentType()
+ *
+ * If this is an array type, return the class of the elements; otherwise
+ * return NULL.
+ */
+static void Dalvik_java_lang_Class_getComponentType(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    if (!dvmIsArrayClass(thisPtr))
+        RETURN_PTR(NULL);
+
+    /*
+     * We can't just return thisPtr->elementClass, because that gives
+     * us the base type (e.g. X[][][] returns X).  If this is a multi-
+     * dimensional array, we have to do the lookup by name.
+     */
+    if (thisPtr->descriptor[1] == '[')
+        RETURN_PTR(dvmFindArrayClass(&thisPtr->descriptor[1],
+                   thisPtr->classLoader));
+    else
+        RETURN_PTR(thisPtr->elementClass);
+}
+
+/*
+ * private static Class<?>[] getDeclaredClasses(Class<?> clazz,
+ *     boolean publicOnly)
+ *
+ * Return an array with the classes that are declared by the specified class.
+ * If "publicOnly" is set, we strip out any classes that don't have "public"
+ * access.
+ */
+static void Dalvik_java_lang_Class_getDeclaredClasses(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* classes;
+
+    classes = dvmGetDeclaredClasses(clazz);
+    if (classes == NULL) {
+        if (!dvmCheckException(dvmThreadSelf())) {
+            /* empty list, so create a zero-length array */
+            classes = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                        0, ALLOC_DEFAULT);
+        }
+    } else if (publicOnly) {
+        u4 count, newIdx, publicCount = 0;
+        ClassObject** pSource = (ClassObject**)(void*)classes->contents;
+        u4 length = classes->length;
+
+        /* count up public classes */
+        for (count = 0; count < length; count++) {
+            if (dvmIsPublicClass(pSource[count]))
+                publicCount++;
+        }
+
+        /* create a new array to hold them */
+        ArrayObject* newClasses;
+        newClasses = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                        publicCount, ALLOC_DEFAULT);
+
+        /* copy them over */
+        for (count = newIdx = 0; count < length; count++) {
+            if (dvmIsPublicClass(pSource[count])) {
+                dvmSetObjectArrayElement(newClasses, newIdx,
+                                         (Object *)pSource[count]);
+                newIdx++;
+            }
+        }
+        assert(newIdx == publicCount);
+        dvmReleaseTrackedAlloc((Object*) classes, NULL);
+        classes = newClasses;
+    }
+
+    dvmReleaseTrackedAlloc((Object*) classes, NULL);
+    RETURN_PTR(classes);
+}
+
+/*
+ * static Constructor[] getDeclaredConstructors(Class clazz, boolean publicOnly)
+ *     throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredConstructors(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* constructors;
+
+    constructors = dvmGetDeclaredConstructors(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) constructors, NULL);
+
+    RETURN_PTR(constructors);
+}
+
+/*
+ * static Field[] getDeclaredFields(Class klass, boolean publicOnly)
+ */
+static void Dalvik_java_lang_Class_getDeclaredFields(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* fields;
+
+    fields = dvmGetDeclaredFields(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) fields, NULL);
+
+    RETURN_PTR(fields);
+}
+
+/*
+ * static Field getDeclaredField(Class klass, String name)
+ */
+static void Dalvik_java_lang_Class_getDeclaredField(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    Object* fieldObj = dvmGetDeclaredField(clazz, nameObj);
+    dvmReleaseTrackedAlloc((Object*) fieldObj, NULL);
+    RETURN_PTR(fieldObj);
+}
+
+/*
+ * static Method[] getDeclaredMethods(Class clazz, boolean publicOnly)
+ *     throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredMethods(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool publicOnly = (args[1] != 0);
+    ArrayObject* methods;
+
+    methods = dvmGetDeclaredMethods(clazz, publicOnly);
+    dvmReleaseTrackedAlloc((Object*) methods, NULL);
+
+    RETURN_PTR(methods);
+}
+
+/*
+ * static native Member getDeclaredConstructorOrMethod(
+ *     Class clazz, String name, Class[] args);
+ */
+static void Dalvik_java_lang_Class_getDeclaredConstructorOrMethod(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    ArrayObject* methodArgs = (ArrayObject*) args[2];
+
+    Object* methodObj;
+
+    methodObj = dvmGetDeclaredConstructorOrMethod(clazz, nameObj, methodArgs);
+    dvmReleaseTrackedAlloc(methodObj, NULL);
+
+    RETURN_PTR(methodObj);
+}
+
+/*
+ * Class[] getInterfaces()
+ */
+static void Dalvik_java_lang_Class_getInterfaces(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ArrayObject* interfaces;
+
+    interfaces = dvmGetInterfaces(clazz);
+    dvmReleaseTrackedAlloc((Object*) interfaces, NULL);
+
+    RETURN_PTR(interfaces);
+}
+
+/*
+ * private static int getModifiers(Class klass, boolean
+ *     ignoreInnerClassesAttrib)
+ *
+ * Return the class' modifier flags.  If "ignoreInnerClassesAttrib" is false,
+ * and this is an inner class, we return the access flags from the inner class
+ * attribute.
+ */
+static void Dalvik_java_lang_Class_getModifiers(const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    bool ignoreInner = args[1];
+    u4 accessFlags;
+
+    accessFlags = clazz->accessFlags & JAVA_FLAGS_MASK;
+
+    if (!ignoreInner) {
+        /* see if we have an InnerClass annotation with flags in it */
+        StringObject* className = NULL;
+        int innerFlags;
+
+        if (dvmGetInnerClass(clazz, &className, &innerFlags))
+            accessFlags = innerFlags & JAVA_FLAGS_MASK;
+
+        dvmReleaseTrackedAlloc((Object*) className, NULL);
+    }
+
+    RETURN_INT(accessFlags);
+}
+
+/*
+ * private native String getNameNative()
+ *
+ * Return the class' name. The exact format is bizarre, but it's the specified
+ * behavior: keywords for primitive types, regular "[I" form for primitive
+ * arrays (so "int" but "[I"), and arrays of reference types written
+ * between "L" and ";" but with dots rather than slashes (so "java.lang.String"
+ * but "[Ljava.lang.String;"). Madness.
+ */
+static void Dalvik_java_lang_Class_getNameNative(const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    const char* descriptor = clazz->descriptor;
+    StringObject* nameObj;
+
+    if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+        /*
+         * The descriptor indicates that this is the class for
+         * a primitive type; special-case the return value.
+         */
+        const char* name;
+        switch (descriptor[0]) {
+            case 'Z': name = "boolean"; break;
+            case 'B': name = "byte";    break;
+            case 'C': name = "char";    break;
+            case 'S': name = "short";   break;
+            case 'I': name = "int";     break;
+            case 'J': name = "long";    break;
+            case 'F': name = "float";   break;
+            case 'D': name = "double";  break;
+            case 'V': name = "void";    break;
+            default: {
+                ALOGE("Unknown primitive type '%c'", descriptor[0]);
+                assert(false);
+                RETURN_PTR(NULL);
+            }
+        }
+
+        nameObj = dvmCreateStringFromCstr(name);
+    } else {
+        /*
+         * Convert the UTF-8 name to a java.lang.String. The
+         * name must use '.' to separate package components.
+         *
+         * TODO: this could be more efficient. Consider a custom
+         * conversion function here that walks the string once and
+         * avoids the allocation for the common case (name less than,
+         * say, 128 bytes).
+         */
+        char* dotName = dvmDescriptorToDot(clazz->descriptor);
+        nameObj = dvmCreateStringFromCstr(dotName);
+        free(dotName);
+    }
+
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    RETURN_PTR(nameObj);
+}
+
+/*
+ * Return the superclass for instances of this class.
+ *
+ * If the class represents a java/lang/Object, an interface, a primitive
+ * type, or void (which *is* a primitive type??), return NULL.
+ *
+ * For an array, return the java/lang/Object ClassObject.
+ */
+static void Dalvik_java_lang_Class_getSuperclass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz))
+        RETURN_PTR(NULL);
+    else
+        RETURN_PTR(clazz->super);
+}
+
+/*
+ * public boolean isAssignableFrom(Class<?> cls)
+ *
+ * Determine if this class is either the same as, or is a superclass or
+ * superinterface of, the class specified in the "cls" parameter.
+ */
+static void Dalvik_java_lang_Class_isAssignableFrom(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+    ClassObject* testClass = (ClassObject*) args[1];
+
+    if (testClass == NULL) {
+        dvmThrowNullPointerException("cls == null");
+        RETURN_INT(false);
+    }
+    RETURN_INT(dvmInstanceof(testClass, thisPtr));
+}
+
+/*
+ * public boolean isInstance(Object o)
+ *
+ * Dynamic equivalent of Java programming language "instanceof".
+ */
+static void Dalvik_java_lang_Class_isInstance(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+    Object* testObj = (Object*) args[1];
+
+    if (testObj == NULL)
+        RETURN_INT(false);
+    RETURN_INT(dvmInstanceof(testObj->clazz, thisPtr));
+}
+
+/*
+ * public boolean isInterface()
+ */
+static void Dalvik_java_lang_Class_isInterface(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    RETURN_INT(dvmIsInterfaceClass(thisPtr));
+}
+
+/*
+ * public boolean isPrimitive()
+ */
+static void Dalvik_java_lang_Class_isPrimitive(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* thisPtr = (ClassObject*) args[0];
+
+    RETURN_INT(dvmIsPrimitiveClass(thisPtr));
+}
+
+/*
+ * public T newInstance() throws InstantiationException, IllegalAccessException
+ *
+ * Create a new instance of this class.
+ */
+static void Dalvik_java_lang_Class_newInstance(const u4* args, JValue* pResult)
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clazz = (ClassObject*) args[0];
+    Method* init;
+    Object* newObj;
+
+    /* can't instantiate these */
+    if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz)
+        || dvmIsArrayClass(clazz) || dvmIsAbstractClass(clazz))
+    {
+        ALOGD("newInstance failed: p%d i%d [%d a%d",
+            dvmIsPrimitiveClass(clazz), dvmIsInterfaceClass(clazz),
+            dvmIsArrayClass(clazz), dvmIsAbstractClass(clazz));
+        dvmThrowInstantiationException(clazz, NULL);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(clazz)) {
+        if (!dvmInitClass(clazz)) {
+            ALOGW("Class init failed in newInstance call (%s)",
+                clazz->descriptor);
+            assert(dvmCheckException(self));
+            RETURN_VOID();
+        }
+    }
+
+    /* find the "nullary" constructor */
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V");
+    if (init == NULL) {
+        /* common cause: secret "this" arg on non-static inner class ctor */
+        ALOGD("newInstance failed: no <init>()");
+        dvmThrowInstantiationException(clazz, "no empty constructor");
+        RETURN_VOID();
+    }
+
+    /*
+     * Verify access from the call site.
+     *
+     * First, make sure the method invoking Class.newInstance() has permission
+     * to access the class.
+     *
+     * Second, make sure it has permission to invoke the constructor.  The
+     * constructor must be public or, if the caller is in the same package,
+     * have package scope.
+     */
+    ClassObject* callerClass = dvmGetCaller2Class(self->interpSave.curFrame);
+
+    if (!dvmCheckClassAccess(callerClass, clazz)) {
+        ALOGD("newInstance failed: %s not accessible to %s",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowIllegalAccessException("access to class not allowed");
+        RETURN_VOID();
+    }
+    if (!dvmCheckMethodAccess(callerClass, init)) {
+        ALOGD("newInstance failed: %s.<init>() not accessible to %s",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowIllegalAccessException("access to constructor not allowed");
+        RETURN_VOID();
+    }
+
+    newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    JValue unused;
+
+    /* invoke constructor; unlike reflection calls, we don't wrap exceptions */
+    dvmCallMethod(self, init, newObj, &unused);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+
+    RETURN_PTR(newObj);
+}
+
+/*
+ * public Class getEnclosingClass()
+ *
+ * Get the class that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingClass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    ClassObject* enclosing = dvmGetEnclosingClass(clazz);
+    dvmReleaseTrackedAlloc((Object*) enclosing, NULL);
+    RETURN_PTR(enclosing);
+}
+
+/*
+ * public Constructor getEnclosingConstructor()
+ *
+ * Get the constructor that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingConstructor(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    Object* enclosing = dvmGetEnclosingMethod(clazz);
+    if (enclosing != NULL) {
+        dvmReleaseTrackedAlloc(enclosing, NULL);
+        if (enclosing->clazz == gDvm.classJavaLangReflectConstructor) {
+            RETURN_PTR(enclosing);
+        }
+        assert(enclosing->clazz == gDvm.classJavaLangReflectMethod);
+    }
+    RETURN_PTR(NULL);
+}
+
+/*
+ * public Method getEnclosingMethod()
+ *
+ * Get the method that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingMethod(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    Object* enclosing = dvmGetEnclosingMethod(clazz);
+    if (enclosing != NULL) {
+        dvmReleaseTrackedAlloc(enclosing, NULL);
+        if (enclosing->clazz == gDvm.classJavaLangReflectMethod) {
+            RETURN_PTR(enclosing);
+        }
+        assert(enclosing->clazz == gDvm.classJavaLangReflectConstructor);
+    }
+    RETURN_PTR(NULL);
+}
+
+/*
+ * public boolean isAnonymousClass()
+ *
+ * Returns true if this is an "anonymous" class.
+ */
+static void Dalvik_java_lang_Class_isAnonymousClass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* className = NULL;
+    int accessFlags;
+
+    /*
+     * If this has an InnerClass annotation, pull it out.  Lack of the
+     * annotation, or an annotation with a NULL class name, indicates
+     * that this is an anonymous inner class.
+     */
+    if (!dvmGetInnerClass(clazz, &className, &accessFlags))
+        RETURN_BOOLEAN(false);
+
+    dvmReleaseTrackedAlloc((Object*) className, NULL);
+    RETURN_BOOLEAN(className == NULL);
+}
+
+/*
+ * public Class getDeclaringClass()
+ *
+ * Get the class that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getDeclaringClass(const u4* args,
+    JValue* pResult)
+{
+    JValue isAnonymousClass;
+    Dalvik_java_lang_Class_isAnonymousClass(args, &isAnonymousClass);
+    if (isAnonymousClass.z) {
+        RETURN_PTR(NULL);
+    }
+
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    ClassObject* enclosing = dvmGetDeclaringClass(clazz);
+    dvmReleaseTrackedAlloc((Object*) enclosing, NULL);
+    RETURN_PTR(enclosing);
+}
+
+/*
+ * private Annotation[] getDeclaredAnnotations()
+ *
+ * Return the annotations declared on this class.
+ */
+static void Dalvik_java_lang_Class_getDeclaredAnnotations(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    ArrayObject* annos = dvmGetClassAnnotations(clazz);
+    dvmReleaseTrackedAlloc((Object*) annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * private Annotation getDeclaredAnnotation(Class annotationClass)
+ */
+static void Dalvik_java_lang_Class_getDeclaredAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ClassObject* annotationClazz = (ClassObject*) args[1];
+
+    RETURN_PTR(dvmGetClassAnnotation(clazz, annotationClazz));
+}
+
+/*
+ * private boolean isDeclaredAnnotationPresent(Class annotationClass);
+ */
+static void Dalvik_java_lang_Class_isDeclaredAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ClassObject* annotationClazz = (ClassObject*) args[1];
+
+    RETURN_BOOLEAN(dvmIsClassAnnotationPresent(clazz, annotationClazz));
+}
+
+/*
+ * public String getInnerClassName()
+ *
+ * Returns the simple name of a member class or local class, or null otherwise.
+ */
+static void Dalvik_java_lang_Class_getInnerClassName(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    StringObject* nameObj;
+    int flags;
+
+    if (dvmGetInnerClass(clazz, &nameObj, &flags)) {
+        dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+        RETURN_PTR(nameObj);
+    } else {
+        RETURN_PTR(NULL);
+    }
+}
+
+JNIEXPORT jobject JNICALL Java_java_lang_Class_getDex(JNIEnv* env, jclass javaClass) {
+    Thread* self = dvmThreadSelf();
+    ClassObject* c = (ClassObject*) dvmDecodeIndirectRef(self, javaClass);
+
+    DvmDex* dvm_dex = c->pDvmDex;
+    if (dvm_dex == NULL) {
+        return NULL;
+    }
+    // Already cached?
+    if (dvm_dex->dex_object != NULL) {
+        return dvm_dex->dex_object;
+    }
+    jobject byte_buffer = env->NewDirectByteBuffer(dvm_dex->memMap.addr, dvm_dex->memMap.length);
+    if (byte_buffer == NULL) {
+        return NULL;
+    }
+
+    jclass com_android_dex_Dex = env->FindClass("com/android/dex/Dex");
+    if (com_android_dex_Dex == NULL) {
+        return NULL;
+    }
+
+    jmethodID com_android_dex_Dex_create =
+            env->GetStaticMethodID(com_android_dex_Dex,
+                                   "create", "(Ljava/nio/ByteBuffer;)Lcom/android/dex/Dex;");
+    if (com_android_dex_Dex_create == NULL) {
+        return NULL;
+    }
+
+    jvalue args[1];
+    args[0].l = byte_buffer;
+    jobject local_ref = env->CallStaticObjectMethodA(com_android_dex_Dex,
+                                                     com_android_dex_Dex_create,
+                                                     args);
+    if (local_ref == NULL) {
+        return NULL;
+    }
+
+    // Check another thread didn't cache an object, if we've won install the object.
+    ScopedPthreadMutexLock lock(&dvm_dex->modLock);
+
+    if (dvm_dex->dex_object == NULL) {
+        dvm_dex->dex_object = env->NewGlobalRef(local_ref);
+    }
+    return dvm_dex->dex_object;
+}
+
+const DalvikNativeMethod dvm_java_lang_Class[] = {
+    { "desiredAssertionStatus", "()Z",
+        Dalvik_java_lang_Class_desiredAssertionStatus },
+    { "classForName",           "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
+        Dalvik_java_lang_Class_classForName },
+    { "getClassLoader",         "(Ljava/lang/Class;)Ljava/lang/ClassLoader;",
+        Dalvik_java_lang_Class_getClassLoader },
+    { "getComponentType",       "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getComponentType },
+    { "getDeclaredClasses",     "(Ljava/lang/Class;Z)[Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getDeclaredClasses },
+    { "getDeclaredConstructors", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;",
+        Dalvik_java_lang_Class_getDeclaredConstructors },
+    { "getDeclaredFields",      "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;",
+        Dalvik_java_lang_Class_getDeclaredFields },
+    { "getDeclaredMethods",     "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;",
+        Dalvik_java_lang_Class_getDeclaredMethods },
+    { "getDeclaredField",      "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;",
+        Dalvik_java_lang_Class_getDeclaredField },
+    { "getDeclaredConstructorOrMethod", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;",
+        Dalvik_java_lang_Class_getDeclaredConstructorOrMethod },
+    { "getInterfaces",          "()[Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getInterfaces },
+    { "getModifiers",           "(Ljava/lang/Class;Z)I",
+        Dalvik_java_lang_Class_getModifiers },
+    { "getNameNative",                "()Ljava/lang/String;",
+        Dalvik_java_lang_Class_getNameNative },
+    { "getSuperclass",          "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getSuperclass },
+    { "isAssignableFrom",       "(Ljava/lang/Class;)Z",
+        Dalvik_java_lang_Class_isAssignableFrom },
+    { "isInstance",             "(Ljava/lang/Object;)Z",
+        Dalvik_java_lang_Class_isInstance },
+    { "isInterface",            "()Z",
+        Dalvik_java_lang_Class_isInterface },
+    { "isPrimitive",            "()Z",
+        Dalvik_java_lang_Class_isPrimitive },
+    { "newInstanceImpl",        "()Ljava/lang/Object;",
+        Dalvik_java_lang_Class_newInstance },
+    { "getDeclaringClass",      "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getDeclaringClass },
+    { "getEnclosingClass",      "()Ljava/lang/Class;",
+        Dalvik_java_lang_Class_getEnclosingClass },
+    { "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;",
+        Dalvik_java_lang_Class_getEnclosingConstructor },
+    { "getEnclosingMethod",     "()Ljava/lang/reflect/Method;",
+        Dalvik_java_lang_Class_getEnclosingMethod },
+#if 0
+    { "getGenericInterfaces",   "()[Ljava/lang/reflect/Type;",
+        Dalvik_java_lang_Class_getGenericInterfaces },
+    { "getGenericSuperclass",   "()Ljava/lang/reflect/Type;",
+        Dalvik_java_lang_Class_getGenericSuperclass },
+    { "getTypeParameters",      "()Ljava/lang/reflect/TypeVariable;",
+        Dalvik_java_lang_Class_getTypeParameters },
+#endif
+    { "isAnonymousClass",       "()Z",
+        Dalvik_java_lang_Class_isAnonymousClass },
+    { "getDeclaredAnnotations", "()[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_Class_getDeclaredAnnotations },
+    { "getDeclaredAnnotation", "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_Class_getDeclaredAnnotation },
+    { "isDeclaredAnnotationPresent", "(Ljava/lang/Class;)Z",
+        Dalvik_java_lang_Class_isDeclaredAnnotationPresent },
+    { "getInnerClassName",       "()Ljava/lang/String;",
+        Dalvik_java_lang_Class_getInnerClassName },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Double.cpp b/vm/native/java_lang_Double.cpp
new file mode 100644
index 0000000..b019c8c
--- /dev/null
+++ b/vm/native/java_lang_Double.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Double_doubleToLongBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_doubleToLongBits);
+}
+
+static void Double_doubleToRawLongBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_doubleToRawLongBits);
+}
+
+static void Double_longBitsToDouble(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangDouble_longBitsToDouble);
+}
+
+const DalvikNativeMethod dvm_java_lang_Double[] = {
+    { "doubleToLongBits",    "(D)J", Double_doubleToLongBits },
+    { "doubleToRawLongBits", "(D)J", Double_doubleToRawLongBits },
+    { "longBitsToDouble",    "(J)D", Double_longBitsToDouble },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Float.cpp b/vm/native/java_lang_Float.cpp
new file mode 100644
index 0000000..e99e4aa
--- /dev/null
+++ b/vm/native/java_lang_Float.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Float_floatToIntBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_floatToIntBits);
+}
+
+static void Float_floatToRawIntBits(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_floatToRawIntBits);
+}
+
+static void Float_intBitsToFloat(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangFloat_intBitsToFloat);
+}
+
+const DalvikNativeMethod dvm_java_lang_Float[] = {
+    { "floatToIntBits",    "(F)I", Float_floatToIntBits },
+    { "floatToRawIntBits", "(F)I", Float_floatToRawIntBits },
+    { "intBitsToFloat",    "(I)F", Float_intBitsToFloat },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Math.cpp b/vm/native/java_lang_Math.cpp
new file mode 100644
index 0000000..7c17242
--- /dev/null
+++ b/vm/native/java_lang_Math.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void Math_absD(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_double);
+}
+
+static void Math_absF(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_float);
+}
+
+static void Math_absI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_int);
+}
+
+static void Math_absJ(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_abs_long);
+}
+
+static void Math_cos(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_cos);
+}
+
+static void Math_maxI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_max_int);
+}
+
+static void Math_minI(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_min_int);
+}
+
+static void Math_sin(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_sin);
+}
+
+static void Math_sqrt(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangMath_sqrt);
+}
+
+const DalvikNativeMethod dvm_java_lang_Math[] = {
+    { "abs",  "(D)D",  Math_absD },
+    { "abs",  "(F)F",  Math_absF },
+    { "abs",  "(I)I",  Math_absI },
+    { "abs",  "(J)J",  Math_absJ },
+    { "cos",  "(D)D",  Math_cos },
+    { "max",  "(II)I", Math_maxI },
+    { "min",  "(II)I", Math_minI },
+    { "sin",  "(D)D",  Math_sin },
+    { "sqrt", "(D)D",  Math_sqrt },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Object.cpp b/vm/native/java_lang_Object.cpp
new file mode 100644
index 0000000..12f701f
--- /dev/null
+++ b/vm/native/java_lang_Object.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.Object
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private Object internalClone()
+ *
+ * Implements most of Object.clone().
+ */
+static void Dalvik_java_lang_Object_internalClone(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Object* clone = dvmCloneObject(thisPtr, ALLOC_DONT_TRACK);
+
+    RETURN_PTR(clone);
+}
+
+/*
+ * public int hashCode()
+ */
+static void Dalvik_java_lang_Object_hashCode(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    RETURN_INT(dvmIdentityHashCode(thisPtr));
+}
+
+/*
+ * public Class getClass()
+ */
+static void Dalvik_java_lang_Object_getClass(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    RETURN_PTR(thisPtr->clazz);
+}
+
+/*
+ * public void notify()
+ *
+ * NOTE: we declare this as a full DalvikBridgeFunc, rather than a
+ * DalvikNativeFunc, because we really want to avoid the "self" lookup.
+ */
+static void Dalvik_java_lang_Object_notify(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectNotify(self, thisPtr);
+    RETURN_VOID();
+}
+
+/*
+ * public void notifyAll()
+ */
+static void Dalvik_java_lang_Object_notifyAll(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectNotifyAll(self, thisPtr);
+    RETURN_VOID();
+}
+
+/*
+ * public void wait(long ms, int ns) throws InterruptedException
+ */
+static void Dalvik_java_lang_Object_wait(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisPtr = (Object*) args[0];
+
+    dvmObjectWait(self, thisPtr, GET_ARG_LONG(args,1), (s4)args[3], true);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_Object[] = {
+    { "internalClone",  "(Ljava/lang/Cloneable;)Ljava/lang/Object;",
+        Dalvik_java_lang_Object_internalClone },
+    { "hashCode",       "()I",
+        Dalvik_java_lang_Object_hashCode },
+    { "notify",         "()V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_notify },
+    { "notifyAll",      "()V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_notifyAll },
+    { "wait",           "(JI)V",
+        (DalvikNativeFunc) Dalvik_java_lang_Object_wait },
+    { "getClass",       "()Ljava/lang/Class;",
+        Dalvik_java_lang_Object_getClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Runtime.cpp b/vm/native/java_lang_Runtime.cpp
new file mode 100644
index 0000000..2d1c4fe
--- /dev/null
+++ b/vm/native/java_lang_Runtime.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.Runtime
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <unistd.h>
+
+/*
+ * public void gc()
+ *
+ * Initiate a gc.
+ */
+static void Dalvik_java_lang_Runtime_gc(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmCollectGarbage();
+    RETURN_VOID();
+}
+
+/*
+ * private static void nativeExit(int code)
+ *
+ * Runtime.exit() calls this after doing shutdown processing.  Runtime.halt()
+ * uses this as well.
+ */
+static void Dalvik_java_lang_Runtime_nativeExit(const u4* args,
+    JValue* pResult)
+{
+    int status = args[0];
+    if (gDvm.exitHook != NULL) {
+        dvmChangeStatus(NULL, THREAD_NATIVE);
+        (*gDvm.exitHook)(status);     // not expected to return
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+        ALOGW("JNI exit hook returned");
+    }
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    dvmCompilerDumpStats();
+#endif
+    ALOGD("Calling exit(%d)", status);
+    exit(status);
+}
+
+/*
+ * static String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath)
+ *
+ * Load the specified full path as a dynamic library filled with
+ * JNI-compatible methods. Returns null on success, or a failure
+ * message on failure.
+ */
+static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args,
+    JValue* pResult)
+{
+    StringObject* fileNameObj = (StringObject*) args[0];
+    Object* classLoader = (Object*) args[1];
+    StringObject* ldLibraryPathObj = (StringObject*) args[2];
+
+    assert(fileNameObj != NULL);
+    char* fileName = dvmCreateCstrFromString(fileNameObj);
+
+    if (ldLibraryPathObj != NULL) {
+        char* ldLibraryPath = dvmCreateCstrFromString(ldLibraryPathObj);
+        void* sym = dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH");
+        if (sym != NULL) {
+            typedef void (*Fn)(const char*);
+            Fn android_update_LD_LIBRARY_PATH = reinterpret_cast<Fn>(sym);
+            (*android_update_LD_LIBRARY_PATH)(ldLibraryPath);
+        } else {
+            ALOGE("android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!");
+        }
+        free(ldLibraryPath);
+    }
+
+    StringObject* result = NULL;
+    char* reason = NULL;
+    bool success = dvmLoadNativeCode(fileName, classLoader, &reason);
+    if (!success) {
+        const char* msg = (reason != NULL) ? reason : "unknown failure";
+        result = dvmCreateStringFromCstr(msg);
+        dvmReleaseTrackedAlloc((Object*) result, NULL);
+    }
+
+    free(reason);
+    free(fileName);
+    RETURN_PTR(result);
+}
+
+/*
+ * public long maxMemory()
+ *
+ * Returns GC heap max memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult)
+{
+    RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapMaximumSize));
+}
+
+/*
+ * public long totalMemory()
+ *
+ * Returns GC heap total memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_totalMemory(const u4* args,
+    JValue* pResult)
+{
+    RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapSize));
+}
+
+/*
+ * public long freeMemory()
+ *
+ * Returns GC heap free memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_freeMemory(const u4* args,
+    JValue* pResult)
+{
+    size_t size = dvmGetHeapDebugInfo(kVirtualHeapSize);
+    size_t allocated = dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+    long long result = size - allocated;
+    if (result < 0) {
+        result = 0;
+    }
+    RETURN_LONG(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_Runtime[] = {
+    { "freeMemory",          "()J",
+        Dalvik_java_lang_Runtime_freeMemory },
+    { "gc",                 "()V",
+        Dalvik_java_lang_Runtime_gc },
+    { "maxMemory",          "()J",
+        Dalvik_java_lang_Runtime_maxMemory },
+    { "nativeExit",         "(I)V",
+        Dalvik_java_lang_Runtime_nativeExit },
+    { "nativeLoad",         "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;",
+        Dalvik_java_lang_Runtime_nativeLoad },
+    { "totalMemory",          "()J",
+        Dalvik_java_lang_Runtime_totalMemory },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_String.cpp b/vm/native/java_lang_String.cpp
new file mode 100644
index 0000000..38f9e31
--- /dev/null
+++ b/vm/native/java_lang_String.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.String
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+static void String_charAt(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_charAt);
+}
+
+static void String_compareTo(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_compareTo);
+}
+
+static void String_equals(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_equals);
+}
+
+static void String_fastIndexOf(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_fastIndexOf_II);
+}
+
+static void String_intern(const u4* args, JValue* pResult)
+{
+    StringObject* str = (StringObject*) args[0];
+    StringObject* interned = dvmLookupInternedString(str);
+    RETURN_PTR(interned);
+}
+
+static void String_isEmpty(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_isEmpty);
+}
+
+static void String_length(const u4* args, JValue* pResult)
+{
+    MAKE_INTRINSIC_TRAMPOLINE(javaLangString_length);
+}
+
+const DalvikNativeMethod dvm_java_lang_String[] = {
+    { "charAt",      "(I)C",                  String_charAt },
+    { "compareTo",   "(Ljava/lang/String;)I", String_compareTo },
+    { "equals",      "(Ljava/lang/Object;)Z", String_equals },
+    { "fastIndexOf", "(II)I",                 String_fastIndexOf },
+    { "intern",      "()Ljava/lang/String;",  String_intern },
+    { "isEmpty",     "()Z",                   String_isEmpty },
+    { "length",      "()I",                   String_length },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_System.cpp b/vm/native/java_lang_System.cpp
new file mode 100644
index 0000000..d4aecac
--- /dev/null
+++ b/vm/native/java_lang_System.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+/*
+ * The VM makes guarantees about the atomicity of accesses to primitive
+ * variables.  These guarantees also apply to elements of arrays.
+ * In particular, 8-bit, 16-bit, and 32-bit accesses must be atomic and
+ * must not cause "word tearing".  Accesses to 64-bit array elements must
+ * either be atomic or treated as two 32-bit operations.  References are
+ * always read and written atomically, regardless of the number of bits
+ * used to represent them.
+ *
+ * We can't rely on standard libc functions like memcpy() and memmove()
+ * in our implementation of System.arraycopy(), because they may copy
+ * byte-by-byte (either for the full run or for "unaligned" parts at the
+ * start or end).  We need to use functions that guarantee 16-bit or 32-bit
+ * atomicity as appropriate.
+ *
+ * System.arraycopy() is heavily used, so having an efficient implementation
+ * is important.  The bionic libc provides a platform-optimized memory move
+ * function that should be used when possible.  If it's not available,
+ * the trivial "reference implementation" versions below can be used until
+ * a proper version can be written.
+ *
+ * For these functions, The caller must guarantee that dest/src are aligned
+ * appropriately for the element type, and that n is a multiple of the
+ * element size.
+ */
+
+/*
+ * Works like memmove(), except:
+ * - if all arguments are at least 32-bit aligned, we guarantee that we
+ *   will use operations that preserve atomicity of 32-bit values
+ * - if not, we guarantee atomicity of 16-bit values
+ *
+ * If all three arguments are not at least 16-bit aligned, the behavior
+ * of this function is undefined.  (We could remove this restriction by
+ * testing for unaligned values and punting to memmove(), but that's
+ * not currently useful.)
+ *
+ * TODO: add loop for 64-bit alignment
+ * TODO: use __builtin_prefetch
+ * TODO: write an ARM-optimized version
+ */
+static void memmove_words(void* dest, const void* src, size_t n) {
+    assert((((uintptr_t) dest | (uintptr_t) src | n) & 0x01) == 0);
+
+    char* d = (char*) dest;
+    const char* s = (const char*) src;
+    size_t copyCount;
+
+    /*
+     * If the source and destination pointers are the same, this is
+     * an expensive no-op.  Testing for an empty move now allows us
+     * to skip a check later.
+     */
+    if (n == 0 || d == s)
+        return;
+
+    /*
+     * Determine if the source and destination buffers will overlap if
+     * we copy data forward (i.e. *dest++ = *src++).
+     *
+     * It's okay if the destination buffer starts before the source and
+     * there is some overlap, because the reader is always ahead of the
+     * writer.
+     */
+    if (__builtin_expect((d < s) || ((size_t)(d - s) >= n), 1)) {
+        /*
+         * Copy forward.  We prefer 32-bit loads and stores even for 16-bit
+         * data, so sort that out.
+         */
+        if ((((uintptr_t) d | (uintptr_t) s) & 0x03) != 0) {
+            /*
+             * Not 32-bit aligned.  Two possibilities:
+             * (1) Congruent, we can align to 32-bit by copying one 16-bit val
+             * (2) Non-congruent, we can do one of:
+             *   a. copy whole buffer as a series of 16-bit values
+             *   b. load/store 32 bits, using shifts to ensure alignment
+             *   c. just copy the as 32-bit values and assume the CPU
+             *      will do a reasonable job
+             *
+             * We're currently using (a), which is suboptimal.
+             */
+            if ((((uintptr_t) d ^ (uintptr_t) s) & 0x03) != 0) {
+                copyCount = n;
+            } else {
+                copyCount = 2;
+            }
+            n -= copyCount;
+            copyCount /= sizeof(uint16_t);
+
+            while (copyCount--) {
+                *(uint16_t*)d = *(uint16_t*)s;
+                d += sizeof(uint16_t);
+                s += sizeof(uint16_t);
+            }
+        }
+
+        /*
+         * Copy 32-bit aligned words.
+         */
+        copyCount = n / sizeof(uint32_t);
+        while (copyCount--) {
+            *(uint32_t*)d = *(uint32_t*)s;
+            d += sizeof(uint32_t);
+            s += sizeof(uint32_t);
+        }
+
+        /*
+         * Check for leftovers.  Either we finished exactly, or we have
+         * one remaining 16-bit chunk.
+         */
+        if ((n & 0x02) != 0) {
+            *(uint16_t*)d = *(uint16_t*)s;
+        }
+    } else {
+        /*
+         * Copy backward, starting at the end.
+         */
+        d += n;
+        s += n;
+
+        if ((((uintptr_t) d | (uintptr_t) s) & 0x03) != 0) {
+            /* try for 32-bit alignment */
+            if ((((uintptr_t) d ^ (uintptr_t) s) & 0x03) != 0) {
+                copyCount = n;
+            } else {
+                copyCount = 2;
+            }
+            n -= copyCount;
+            copyCount /= sizeof(uint16_t);
+
+            while (copyCount--) {
+                d -= sizeof(uint16_t);
+                s -= sizeof(uint16_t);
+                *(uint16_t*)d = *(uint16_t*)s;
+            }
+        }
+
+        /* copy 32-bit aligned words */
+        copyCount = n / sizeof(uint32_t);
+        while (copyCount--) {
+            d -= sizeof(uint32_t);
+            s -= sizeof(uint32_t);
+            *(uint32_t*)d = *(uint32_t*)s;
+        }
+
+        /* copy leftovers */
+        if ((n & 0x02) != 0) {
+            d -= sizeof(uint16_t);
+            s -= sizeof(uint16_t);
+            *(uint16_t*)d = *(uint16_t*)s;
+        }
+    }
+}
+
+#define move16 memmove_words
+#define move32 memmove_words
+
+/*
+ * public static void arraycopy(Object src, int srcPos, Object dest,
+ *      int destPos, int length)
+ *
+ * The description of this function is long, and describes a multitude
+ * of checks and exceptions.
+ */
+static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult)
+{
+    ArrayObject* srcArray = (ArrayObject*) args[0];
+    int srcPos = args[1];
+    ArrayObject* dstArray = (ArrayObject*) args[2];
+    int dstPos = args[3];
+    int length = args[4];
+
+    /* Check for null pointers. */
+    if (srcArray == NULL) {
+        dvmThrowNullPointerException("src == null");
+        RETURN_VOID();
+    }
+    if (dstArray == NULL) {
+        dvmThrowNullPointerException("dst == null");
+        RETURN_VOID();
+    }
+
+    /* Make sure source and destination are arrays. */
+    if (!dvmIsArray(srcArray)) {
+        dvmThrowArrayStoreExceptionNotArray(((Object*)srcArray)->clazz, "source");
+        RETURN_VOID();
+    }
+    if (!dvmIsArray(dstArray)) {
+        dvmThrowArrayStoreExceptionNotArray(((Object*)dstArray)->clazz, "destination");
+        RETURN_VOID();
+    }
+
+    /* avoid int overflow */
+    if (srcPos < 0 || dstPos < 0 || length < 0 ||
+        srcPos > (int) srcArray->length - length ||
+        dstPos > (int) dstArray->length - length)
+    {
+        dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+            "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
+            srcArray->length, srcPos, dstArray->length, dstPos, length);
+        RETURN_VOID();
+    }
+
+    ClassObject* srcClass = srcArray->clazz;
+    ClassObject* dstClass = dstArray->clazz;
+    char srcType = srcClass->descriptor[1];
+    char dstType = dstClass->descriptor[1];
+
+    /*
+     * If one of the arrays holds a primitive type, the other array must
+     * hold the same type.
+     */
+    bool srcPrim = (srcType != '[' && srcType != 'L');
+    bool dstPrim = (dstType != '[' && dstType != 'L');
+    if (srcPrim || dstPrim) {
+        if (srcPrim != dstPrim || srcType != dstType) {
+            dvmThrowArrayStoreExceptionIncompatibleArrays(srcClass, dstClass);
+            RETURN_VOID();
+        }
+
+        if (false) ALOGD("arraycopy prim[%c] dst=%p %d src=%p %d len=%d",
+            srcType, dstArray->contents, dstPos,
+            srcArray->contents, srcPos, length);
+
+        switch (srcType) {
+        case 'B':
+        case 'Z':
+            /* 1 byte per element */
+            memmove((u1*) dstArray->contents + dstPos,
+                (const u1*) srcArray->contents + srcPos,
+                length);
+            break;
+        case 'C':
+        case 'S':
+            /* 2 bytes per element */
+            move16((u1*) dstArray->contents + dstPos * 2,
+                (const u1*) srcArray->contents + srcPos * 2,
+                length * 2);
+            break;
+        case 'F':
+        case 'I':
+            /* 4 bytes per element */
+            move32((u1*) dstArray->contents + dstPos * 4,
+                (const u1*) srcArray->contents + srcPos * 4,
+                length * 4);
+            break;
+        case 'D':
+        case 'J':
+            /*
+             * 8 bytes per element.  We don't need to guarantee atomicity
+             * of the entire 64-bit word, so we can use the 32-bit copier.
+             */
+            move32((u1*) dstArray->contents + dstPos * 8,
+                (const u1*) srcArray->contents + srcPos * 8,
+                length * 8);
+            break;
+        default:        /* illegal array type */
+            ALOGE("Weird array type '%s'", srcClass->descriptor);
+            dvmAbort();
+        }
+    } else {
+        /*
+         * Neither class is primitive.  See if elements in "src" are instances
+         * of elements in "dst" (e.g. copy String to String or String to
+         * Object).
+         */
+        const int width = sizeof(Object*);
+
+        if (srcClass->arrayDim == dstClass->arrayDim &&
+            dvmInstanceof(srcClass, dstClass))
+        {
+            /*
+             * "dst" can hold "src"; copy the whole thing.
+             */
+            if (false) ALOGD("arraycopy ref dst=%p %d src=%p %d len=%d",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                length * width);
+            move32((u1*)dstArray->contents + dstPos * width,
+                (const u1*)srcArray->contents + srcPos * width,
+                length * width);
+            dvmWriteBarrierArray(dstArray, dstPos, dstPos+length);
+        } else {
+            /*
+             * The arrays are not fundamentally compatible.  However, we
+             * may still be able to do this if the destination object is
+             * compatible (e.g. copy Object[] to String[], but the Object
+             * being copied is actually a String).  We need to copy elements
+             * one by one until something goes wrong.
+             *
+             * Because of overlapping moves, what we really want to do
+             * is compare the types and count up how many we can move,
+             * then call move32() to shift the actual data.  If we just
+             * start from the front we could do a smear rather than a move.
+             */
+            Object** srcObj;
+            int copyCount;
+            ClassObject*   clazz = NULL;
+
+            srcObj = ((Object**)(void*)srcArray->contents) + srcPos;
+
+            if (length > 0 && srcObj[0] != NULL)
+            {
+                clazz = srcObj[0]->clazz;
+                if (!dvmCanPutArrayElement(clazz, dstClass))
+                    clazz = NULL;
+            }
+
+            for (copyCount = 0; copyCount < length; copyCount++)
+            {
+                if (srcObj[copyCount] != NULL &&
+                    srcObj[copyCount]->clazz != clazz &&
+                    !dvmCanPutArrayElement(srcObj[copyCount]->clazz, dstClass))
+                {
+                    /* can't put this element into the array */
+                    break;
+                }
+            }
+
+            if (false) ALOGD("arraycopy iref dst=%p %d src=%p %d count=%d of %d",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                copyCount, length);
+            move32((u1*)dstArray->contents + dstPos * width,
+                (const u1*)srcArray->contents + srcPos * width,
+                copyCount * width);
+            dvmWriteBarrierArray(dstArray, 0, copyCount);
+            if (copyCount != length) {
+                dvmThrowArrayStoreExceptionIncompatibleArrayElement(srcPos + copyCount,
+                        srcObj[copyCount]->clazz, dstClass);
+                RETURN_VOID();
+            }
+        }
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * public static void arraycopyCharUnchecked(char[] src, int srcPos, char[] dest,
+ *      int destPos, int length)
+ *
+ * This is a char[] specialized, native, unchecked version of
+ * arraycopy(). This assumes error checking has been done.
+ */
+static void Dalvik_java_lang_System_arraycopyCharUnchecked(const u4* args, JValue* pResult)
+{
+    ArrayObject* srcArray = (ArrayObject*) args[0];
+    int srcPos = args[1];
+    ArrayObject* dstArray = (ArrayObject*) args[2];
+    int dstPos = args[3];
+    int length = args[4];
+    assert(srcArray != NULL);
+    assert(dstArray != NULL);
+    assert(dvmIsArray(srcArray));
+    assert(dvmIsArray(dstArray));
+    assert(srcPos >= 0 && dstPos >= 0 && length >= 0 &&
+           srcPos + length <= (int) srcArray->length &&
+           dstPos + length <= (int) dstArray->length);
+#ifndef NDEBUG
+    ClassObject* srcClass = srcArray->clazz;
+    ClassObject* dstClass = dstArray->clazz;
+    char srcType = srcClass->descriptor[1];
+    char dstType = dstClass->descriptor[1];
+    assert(srcType == 'C' && dstType == 'C');
+#endif
+    /* 2 bytes per element */
+    move16((u1*) dstArray->contents + dstPos * 2,
+           (const u1*) srcArray->contents + srcPos * 2,
+           length * 2);
+    RETURN_VOID();
+}
+
+/*
+ * static int identityHashCode(Object x)
+ *
+ * Returns that hash code that the default hashCode()
+ * method would return for "x", even if "x"s class
+ * overrides hashCode().
+ */
+static void Dalvik_java_lang_System_identityHashCode(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    RETURN_INT(dvmIdentityHashCode(thisPtr));
+}
+
+const DalvikNativeMethod dvm_java_lang_System[] = {
+    { "arraycopy",          "(Ljava/lang/Object;ILjava/lang/Object;II)V",
+        Dalvik_java_lang_System_arraycopy },
+    { "arraycopyCharUnchecked", "([CI[CII)V",
+        Dalvik_java_lang_System_arraycopyCharUnchecked },
+    { "identityHashCode",  "(Ljava/lang/Object;)I",
+        Dalvik_java_lang_System_identityHashCode },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Throwable.cpp b/vm/native/java_lang_Throwable.cpp
new file mode 100644
index 0000000..6e3ab6c
--- /dev/null
+++ b/vm/native/java_lang_Throwable.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.Throwable
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object nativeFillInStackTrace()
+ */
+static void Dalvik_java_lang_Throwable_nativeFillInStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* stackState = NULL;
+
+    UNUSED_PARAMETER(args);
+
+    stackState = dvmFillInStackTrace(dvmThreadSelf());
+    RETURN_PTR(stackState);
+}
+
+/*
+ * private static StackTraceElement[] nativeGetStackTrace(Object stackState)
+ *
+ * The "stackState" argument must be the value returned by an earlier call to
+ * nativeFillInStackTrace().
+ */
+static void Dalvik_java_lang_Throwable_nativeGetStackTrace(const u4* args,
+    JValue* pResult)
+{
+    Object* stackState = (Object*) args[0];
+    ArrayObject* elements = NULL;
+
+    if (stackState == NULL) {
+        ALOGW("getStackTrace() called but no trace available");
+        RETURN_PTR(NULL);   /* could throw NPE; currently caller will do so */
+    }
+
+    elements = dvmGetStackTrace(stackState);
+    RETURN_PTR(elements);
+}
+
+const DalvikNativeMethod dvm_java_lang_Throwable[] = {
+    { "nativeFillInStackTrace", "()Ljava/lang/Object;",
+        Dalvik_java_lang_Throwable_nativeFillInStackTrace },
+    { "nativeGetStackTrace",    "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;",
+        Dalvik_java_lang_Throwable_nativeGetStackTrace },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMClassLoader.cpp b/vm/native/java_lang_VMClassLoader.cpp
new file mode 100644
index 0000000..eeff688
--- /dev/null
+++ b/vm/native/java_lang_VMClassLoader.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.VMClassLoader
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class defineClass(ClassLoader cl, String name,
+ *     byte[] data, int offset, int len)
+ *     throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    const u1* data = (const u1*) args[2];
+    int offset = args[3];
+    int len = args[4];
+    char* name = NULL;
+
+    name = dvmCreateCstrFromString(nameObj);
+    ALOGE("ERROR: defineClass(%p, %s, %p, %d, %d)",
+        loader, name, data, offset, len);
+    dvmThrowUnsupportedOperationException(
+        "can't load this type of class file");
+
+    free(name);
+    RETURN_VOID();
+}
+
+/*
+ * static Class defineClass(ClassLoader cl, byte[] data, int offset,
+ *     int len)
+ *     throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object. Deprecated version of
+ * previous method, lacks name parameter.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    const u1* data = (const u1*) args[1];
+    int offset = args[2];
+    int len = args[3];
+
+    ALOGE("ERROR: defineClass(%p, %p, %d, %d)",
+        loader, data, offset, len);
+    dvmThrowUnsupportedOperationException(
+        "can't load this type of class file");
+
+    RETURN_VOID();
+}
+
+/*
+ * static Class findLoadedClass(ClassLoader cl, String name)
+ */
+static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args,
+    JValue* pResult)
+{
+    Object* loader = (Object*) args[0];
+    StringObject* nameObj = (StringObject*) args[1];
+    ClassObject* clazz = NULL;
+    char* name = NULL;
+    char* descriptor = NULL;
+
+    if (nameObj == NULL) {
+        dvmThrowNullPointerException("name == null");
+        goto bail;
+    }
+
+    /*
+     * Get a UTF-8 copy of the string, and convert dots to slashes.
+     */
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL)
+        goto bail;
+
+    descriptor = dvmDotToDescriptor(name);
+    if (descriptor == NULL)
+        goto bail;
+
+    clazz = dvmLookupClass(descriptor, loader, false);
+    LOGVV("look: %s ldr=%p --> %p", descriptor, loader, clazz);
+
+bail:
+    free(name);
+    free(descriptor);
+    RETURN_PTR(clazz);
+}
+
+/*
+ * private static int getBootClassPathSize()
+ *
+ * Get the number of entries in the boot class path.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathSize(const u4* args,
+    JValue* pResult)
+{
+    int count = dvmGetBootPathSize();
+    RETURN_INT(count);
+}
+
+/*
+ * private static String getBootClassPathResource(String name, int index)
+ *
+ * Find a resource with a matching name in a boot class path entry.
+ *
+ * This mimics the previous VM interface, since we're sharing class libraries.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource(
+    const u4* args, JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    StringObject* result;
+    int idx = args[1];
+    char* name;
+
+    name = dvmCreateCstrFromString(nameObj);
+    if (name == NULL)
+        RETURN_PTR(NULL);
+
+    result = dvmGetBootPathResource(name, idx);
+    free(name);
+    dvmReleaseTrackedAlloc((Object*)result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * static final Class getPrimitiveClass(char prim_type)
+ */
+static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args,
+    JValue* pResult)
+{
+    int primType = args[0];
+
+    pResult->l = (Object*)dvmFindPrimitiveClass(primType);
+}
+
+/*
+ * static Class loadClass(String name, boolean resolve)
+ *     throws ClassNotFoundException
+ *
+ * Load class using bootstrap class loader.
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    bool resolve = (args[1] != 0);
+    ClassObject* clazz;
+
+    clazz = dvmFindClassByName(nameObj, NULL, resolve);
+    assert(clazz == NULL || dvmIsClassLinked(clazz));
+    RETURN_PTR(clazz);
+}
+
+const DalvikNativeMethod dvm_java_lang_VMClassLoader[] = {
+    { "defineClass",        "(Ljava/lang/ClassLoader;Ljava/lang/String;[BII)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_defineClass },
+    { "defineClass",        "(Ljava/lang/ClassLoader;[BII)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_defineClass2 },
+    { "findLoadedClass",    "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_findLoadedClass },
+    { "getBootClassPathSize", "()I",
+        Dalvik_java_lang_VMClassLoader_getBootClassPathSize },
+    { "getBootClassPathResource", "(Ljava/lang/String;I)Ljava/lang/String;",
+        Dalvik_java_lang_VMClassLoader_getBootClassPathResource },
+    { "getPrimitiveClass",  "(C)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_getPrimitiveClass },
+    { "loadClass",          "(Ljava/lang/String;Z)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_loadClass },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMThread.cpp b/vm/native/java_lang_VMThread.cpp
new file mode 100644
index 0000000..635b726
--- /dev/null
+++ b/vm/native/java_lang_VMThread.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.VMThread
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static void create(Thread t, long stacksize)
+ *
+ * This is eventually called as a result of Thread.start().
+ *
+ * Throws an exception on failure.
+ */
+static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
+{
+    Object* threadObj = (Object*) args[0];
+    s8 stackSize = GET_ARG_LONG(args, 1);
+
+    /* copying collector will pin threadObj for us since it was an argument */
+    dvmCreateInterpThread(threadObj, (int) stackSize);
+    RETURN_VOID();
+}
+
+/*
+ * static Thread currentThread()
+ */
+static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_PTR(dvmThreadSelf()->threadObj);
+}
+
+/*
+ * void getStatus()
+ *
+ * Gets the Thread status. Result is in VM terms, has to be mapped to
+ * Thread.State by interpreted code.
+ */
+static void Dalvik_java_lang_VMThread_getStatus(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+    int result;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        result = thread->status;
+    else
+        result = THREAD_ZOMBIE;     // assume it used to exist and is now gone
+    dvmUnlockThreadList();
+
+    RETURN_INT(result);
+}
+
+/*
+ * boolean holdsLock(Object object)
+ *
+ * Returns whether the current thread has a monitor lock on the specific
+ * object.
+ */
+static void Dalvik_java_lang_VMThread_holdsLock(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Object* object = (Object*) args[1];
+    Thread* thread;
+
+    if (object == NULL) {
+        dvmThrowNullPointerException("object == null");
+        RETURN_VOID();
+    }
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    int result = dvmHoldsLock(thread, object);
+    dvmUnlockThreadList();
+
+    RETURN_BOOLEAN(result);
+}
+
+/*
+ * void interrupt()
+ *
+ * Interrupt a thread that is waiting (or is about to wait) on a monitor.
+ */
+static void Dalvik_java_lang_VMThread_interrupt(const u4* args, JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        dvmThreadInterrupt(thread);
+    dvmUnlockThreadList();
+    RETURN_VOID();
+}
+
+/*
+ * static boolean interrupted()
+ *
+ * Determine if the current thread has been interrupted.  Clears the flag.
+ */
+static void Dalvik_java_lang_VMThread_interrupted(const u4* args,
+    JValue* pResult)
+{
+    Thread* self = dvmThreadSelf();
+    bool interrupted;
+
+    UNUSED_PARAMETER(args);
+
+    interrupted = self->interrupted;
+    self->interrupted = false;
+    RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * boolean isInterrupted()
+ *
+ * Determine if the specified thread has been interrupted.  Does not clear
+ * the flag.
+ */
+static void Dalvik_java_lang_VMThread_isInterrupted(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    Thread* thread;
+    bool interrupted;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        interrupted = thread->interrupted;
+    else
+        interrupted = false;
+    dvmUnlockThreadList();
+
+    RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * void nameChanged(String newName)
+ *
+ * The name of the target thread has changed.  We may need to alert DDMS.
+ */
+static void Dalvik_java_lang_VMThread_nameChanged(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    StringObject* nameStr = (StringObject*) args[1];
+    Thread* thread;
+    int threadId = -1;
+
+    /* get the thread's ID */
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        threadId = thread->threadId;
+    dvmUnlockThreadList();
+
+    dvmDdmSendThreadNameChange(threadId, nameStr);
+    //char* str = dvmCreateCstrFromString(nameStr);
+    //ALOGI("UPDATE: threadid=%d now '%s'", threadId, str);
+    //free(str);
+
+    RETURN_VOID();
+}
+
+/*
+ * void setPriority(int newPriority)
+ *
+ * Alter the priority of the specified thread.  "newPriority" will range
+ * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
+ * threads at Thread.NORM_PRIORITY (5).
+ */
+static void Dalvik_java_lang_VMThread_setPriority(const u4* args,
+    JValue* pResult)
+{
+    Object* thisPtr = (Object*) args[0];
+    int newPriority = args[1];
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    thread = dvmGetThreadFromThreadObject(thisPtr);
+    if (thread != NULL)
+        dvmChangeThreadPriority(thread, newPriority);
+    //dvmDumpAllThreads(false);
+    dvmUnlockThreadList();
+
+    RETURN_VOID();
+}
+
+/*
+ * static void sleep(long msec, int nsec)
+ */
+static void Dalvik_java_lang_VMThread_sleep(const u4* args, JValue* pResult)
+{
+    dvmThreadSleep(GET_ARG_LONG(args,0), args[2]);
+    RETURN_VOID();
+}
+
+/*
+ * public void yield()
+ *
+ * Causes the thread to temporarily pause and allow other threads to execute.
+ *
+ * The exact behavior is poorly defined.  Some discussion here:
+ *   http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
+ */
+static void Dalvik_java_lang_VMThread_yield(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    sched_yield();
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_VMThread[] = {
+    { "create",         "(Ljava/lang/Thread;J)V",
+        Dalvik_java_lang_VMThread_create },
+    { "currentThread",  "()Ljava/lang/Thread;",
+        Dalvik_java_lang_VMThread_currentThread },
+    { "getStatus",      "()I",
+        Dalvik_java_lang_VMThread_getStatus },
+    { "holdsLock",      "(Ljava/lang/Object;)Z",
+        Dalvik_java_lang_VMThread_holdsLock },
+    { "interrupt",      "()V",
+        Dalvik_java_lang_VMThread_interrupt },
+    { "interrupted",    "()Z",
+        Dalvik_java_lang_VMThread_interrupted },
+    { "isInterrupted",  "()Z",
+        Dalvik_java_lang_VMThread_isInterrupted },
+    { "nameChanged",    "(Ljava/lang/String;)V",
+        Dalvik_java_lang_VMThread_nameChanged },
+    { "setPriority",    "(I)V",
+        Dalvik_java_lang_VMThread_setPriority },
+    { "sleep",          "(JI)V",
+        Dalvik_java_lang_VMThread_sleep },
+    { "yield",          "()V",
+        Dalvik_java_lang_VMThread_yield },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_AccessibleObject.cpp b/vm/native/java_lang_reflect_AccessibleObject.cpp
new file mode 100644
index 0000000..46a1357
--- /dev/null
+++ b/vm/native/java_lang_reflect_AccessibleObject.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.AccessibleObject
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object[] getClassSignatureAnnotation(Class clazz)
+ *
+ * Return the Signature annotation for the specified class.  Equivalent to
+ * Class.getSignatureAnnotation(), but available to java.lang.reflect.
+ */
+static void Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz);
+
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[] = {
+    { "getClassSignatureAnnotation", "(Ljava/lang/Class;)[Ljava/lang/Object;",
+      Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Array.cpp b/vm/native/java_lang_reflect_Array.cpp
new file mode 100644
index 0000000..dbe823d
--- /dev/null
+++ b/vm/native/java_lang_reflect_Array.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.Array
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object createObjectArray(Class<?> componentType,
+ *     int length) throws NegativeArraySizeException;
+ *
+ * Create a one-dimensional array of Objects.
+ */
+static void Dalvik_java_lang_reflect_Array_createObjectArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[0];
+    int length = args[1];
+
+    assert(elementClass != NULL);       // tested by caller
+    if (length < 0) {
+        dvmThrowNegativeArraySizeException(length);
+        RETURN_VOID();
+    }
+
+    ClassObject* arrayClass =
+        dvmFindArrayClassForElement(elementClass);
+    ArrayObject* newArray =
+        dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+
+    RETURN_PTR(newArray);
+}
+
+/*
+ * private static Object createMultiArray(Class<?> componentType,
+ *     int[] dimensions) throws NegativeArraySizeException;
+ *
+ * Create a multi-dimensional array of Objects or primitive types.
+ *
+ * We have to generate the names for X[], X[][], X[][][], and so on.  The
+ * easiest way to deal with that is to create the full name once and then
+ * subtract pieces off.  Besides, we want to start with the outermost
+ * piece and work our way in.
+ */
+static void Dalvik_java_lang_reflect_Array_createMultiArray(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* elementClass = (ClassObject*) args[0];
+    ArrayObject* dimArray = (ArrayObject*) args[1];
+    ClassObject* arrayClass;
+    ArrayObject* newArray;
+    char* acDescriptor;
+    int numDim, i;
+    int* dimensions;
+
+    ALOGV("createMultiArray: '%s' [%d]",
+        elementClass->descriptor, dimArray->length);
+
+    assert(elementClass != NULL);       // verified by caller
+
+    /*
+     * Verify dimensions.
+     *
+     * The caller is responsible for verifying that "dimArray" is non-null
+     * and has a length > 0 and <= 255.
+     */
+    assert(dimArray != NULL);           // verified by caller
+    numDim = dimArray->length;
+    assert(numDim > 0 && numDim <= 255);
+
+    dimensions = (int*)(void*)dimArray->contents;
+    for (i = 0; i < numDim; i++) {
+        if (dimensions[i] < 0) {
+            dvmThrowNegativeArraySizeException(dimensions[i]);
+            RETURN_VOID();
+        }
+        LOGVV("DIM %d: %d", i, dimensions[i]);
+    }
+
+    /*
+     * Generate the full name of the array class.
+     */
+    acDescriptor =
+        (char*) malloc(strlen(elementClass->descriptor) + numDim + 1);
+    memset(acDescriptor, '[', numDim);
+
+    LOGVV("#### element name = '%s'", elementClass->descriptor);
+    if (dvmIsPrimitiveClass(elementClass)) {
+        assert(elementClass->primitiveType != PRIM_NOT);
+        acDescriptor[numDim] = dexGetPrimitiveTypeDescriptorChar(elementClass->primitiveType);
+        acDescriptor[numDim+1] = '\0';
+    } else {
+        strcpy(acDescriptor+numDim, elementClass->descriptor);
+    }
+    LOGVV("#### array name = '%s'", acDescriptor);
+
+    /*
+     * Find/generate the array class.
+     */
+    arrayClass = dvmFindArrayClass(acDescriptor, elementClass->classLoader);
+    if (arrayClass == NULL) {
+        ALOGW("Unable to find or generate array class '%s'", acDescriptor);
+        assert(dvmCheckException(dvmThreadSelf()));
+        free(acDescriptor);
+        RETURN_VOID();
+    }
+    free(acDescriptor);
+
+    /* create the array */
+    newArray = dvmAllocMultiArray(arrayClass, numDim-1, dimensions);
+    if (newArray == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+
+    dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+    RETURN_PTR(newArray);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Array[] = {
+    { "createObjectArray",  "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Array_createObjectArray },
+    { "createMultiArray",   "(Ljava/lang/Class;[I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Array_createMultiArray },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Constructor.cpp b/vm/native/java_lang_reflect_Constructor.cpp
new file mode 100644
index 0000000..166dbf5
--- /dev/null
+++ b/vm/native/java_lang_reflect_Constructor.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.Constructor
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public int constructNative(Object[] args, Class declaringClass,
+ *     Class[] parameterTypes, int slot, boolean noAccessCheck)
+ *
+ * We get here through Constructor.newInstance().  The Constructor object
+ * would not be available if the constructor weren't public (per the
+ * definition of Class.getConstructor), so we can skip the method access
+ * check.  We can also safely assume the constructor isn't associated
+ * with an interface, array, or primitive class.
+ */
+static void Dalvik_java_lang_reflect_Constructor_constructNative(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ArrayObject* argList = (ArrayObject*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ArrayObject* params = (ArrayObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Object* newObj;
+    Method* meth;
+
+    if (dvmIsAbstractClass(declaringClass)) {
+        dvmThrowInstantiationException(declaringClass, NULL);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(declaringClass)) {
+        if (!dvmInitClass(declaringClass)) {
+            ALOGW("Class init failed in Constructor.constructNative (%s)",
+                declaringClass->descriptor);
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+    }
+
+    newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT);
+    if (newObj == NULL)
+        RETURN_PTR(NULL);
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    (void) dvmInvokeMethod(newObj, meth, argList, params, NULL, noAccessCheck);
+    dvmReleaseTrackedAlloc(newObj, NULL);
+    RETURN_PTR(newObj);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Constructor[] = {
+    { "constructNative",    "([Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Constructor_constructNative },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Field.cpp b/vm/native/java_lang_reflect_Field.cpp
new file mode 100644
index 0000000..dac784b
--- /dev/null
+++ b/vm/native/java_lang_reflect_Field.cpp
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.Field
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Validate access to a field.  Returns a pointer to the Field struct.
+ *
+ * "declaringClass" is the class in which the field was declared.  For an
+ * instance field, "obj" is the object that holds the field data; for a
+ * static field its value is ignored.
+ *
+ * "If the underlying field is static, the class that declared the
+ * field is initialized if it has not already been initialized."
+ *
+ * On failure, throws an exception and returns NULL.
+ *
+ * The documentation lists exceptional conditions and the exceptions that
+ * should be thrown, but doesn't say which exception prevails when two or
+ * more exceptional conditions exist at the same time.  For example,
+ * attempting to set a protected field from an unrelated class causes an
+ * IllegalAccessException, while passing in a data type that doesn't match
+ * the field causes an IllegalArgumentException.  If code does both at the
+ * same time, we have to choose one or the other.
+ *
+ * The expected order is:
+ *  (1) Check for illegal access. Throw IllegalAccessException.
+ *  (2) Make sure the object actually has the field.  Throw
+ *      IllegalArgumentException.
+ *  (3) Make sure the field matches the expected type, e.g. if we issued
+ *      a "getInteger" call make sure the field is an integer or can be
+ *      converted to an int with a widening conversion.  Throw
+ *      IllegalArgumentException.
+ *  (4) Make sure "obj" is not null.  Throw NullPointerException.
+ *
+ * TODO: we're currently handling #3 after #4, because we don't check the
+ * widening conversion until we're actually extracting the value from the
+ * object (which won't work well if it's a null reference).
+ */
+static Field* validateFieldAccess(Object* obj, ClassObject* declaringClass,
+    int slot, bool isSetOperation, bool noAccessCheck)
+{
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    /* verify access */
+    if (!noAccessCheck) {
+        if (isSetOperation && dvmIsFinalField(field)) {
+            dvmThrowIllegalAccessException("field is marked 'final'");
+            return NULL;
+        }
+
+        ClassObject* callerClass =
+            dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
+
+        /*
+         * We need to check two things:
+         *  (1) Would an instance of the calling class have access to the field?
+         *  (2) If the field is "protected", is the object an instance of the
+         *      calling class, or is the field's declaring class in the same
+         *      package as the calling class?
+         *
+         * #1 is basic access control.  #2 ensures that, just because
+         * you're a subclass of Foo, you can't mess with protected fields
+         * in arbitrary Foo objects from other packages.
+         */
+        if (!dvmCheckFieldAccess(callerClass, field)) {
+            dvmThrowIllegalAccessException("access to field not allowed");
+            return NULL;
+        }
+        if (dvmIsProtectedField(field)) {
+            bool isInstance, samePackage;
+
+            if (obj != NULL)
+                isInstance = dvmInstanceof(obj->clazz, callerClass);
+            else
+                isInstance = false;
+            samePackage = dvmInSamePackage(declaringClass, callerClass);
+
+            if (!isInstance && !samePackage) {
+                dvmThrowIllegalAccessException(
+                    "access to protected field not allowed");
+                return NULL;
+            }
+        }
+    }
+
+    if (dvmIsStaticField(field)) {
+        /* init class if necessary, then return ptr to storage in "field" */
+        if (!dvmIsClassInitialized(declaringClass)) {
+            if (!dvmInitClass(declaringClass)) {
+                assert(dvmCheckException(dvmThreadSelf()));
+                return NULL;
+            }
+        }
+
+    } else {
+        /*
+         * Verify object is of correct type (i.e. it actually has the
+         * expected field in it), then grab a pointer to obj storage.
+         * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL.
+         */
+        if (!dvmVerifyObjectInClass(obj, declaringClass)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+    }
+
+    return field;
+}
+
+/*
+ * Extracts the value of a static field.  Provides appropriate barriers
+ * for volatile fields.
+ *
+ * Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
+ */
+static void getStaticFieldValue(const StaticField* sfield, JValue* value)
+{
+    if (!dvmIsVolatileField(sfield)) {
+        /* just copy the whole thing */
+        *value = sfield->value;
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (sfield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetStaticFieldBooleanVolatile(sfield);
+            break;
+        case 'B':
+            value->i = dvmGetStaticFieldByteVolatile(sfield);
+            break;
+        case 'S':
+            value->i = dvmGetStaticFieldShortVolatile(sfield);
+            break;
+        case 'C':
+            value->i = dvmGetStaticFieldCharVolatile(sfield);
+            break;
+        case 'I':
+            value->i = dvmGetStaticFieldIntVolatile(sfield);
+            break;
+        case 'F':
+            value->f = dvmGetStaticFieldFloatVolatile(sfield);
+            break;
+        case 'J':
+            value->j = dvmGetStaticFieldLongVolatile(sfield);
+            break;
+        case 'D':
+            value->d = dvmGetStaticFieldDoubleVolatile(sfield);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetStaticFieldObjectVolatile(sfield);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", sfield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Extracts the value of an instance field.  Provides appropriate barriers
+ * for volatile fields.
+ *
+ * Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
+ */
+static void getInstFieldValue(const InstField* ifield, Object* obj,
+    JValue* value)
+{
+    if (!dvmIsVolatileField(ifield)) {
+        /* use type-specific get; really just 32-bit vs. 64-bit */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetFieldBoolean(obj, ifield->byteOffset);
+            break;
+        case 'B':
+            value->i = dvmGetFieldByte(obj, ifield->byteOffset);
+            break;
+        case 'S':
+            value->i = dvmGetFieldShort(obj, ifield->byteOffset);
+            break;
+        case 'C':
+            value->i = dvmGetFieldChar(obj, ifield->byteOffset);
+            break;
+        case 'I':
+            value->i = dvmGetFieldInt(obj, ifield->byteOffset);
+            break;
+        case 'F':
+            value->f = dvmGetFieldFloat(obj, ifield->byteOffset);
+            break;
+        case 'J':
+            value->j = dvmGetFieldLong(obj, ifield->byteOffset);
+            break;
+        case 'D':
+            value->d = dvmGetFieldDouble(obj, ifield->byteOffset);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetFieldObject(obj, ifield->byteOffset);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            value->i = dvmGetFieldBooleanVolatile(obj, ifield->byteOffset);
+            break;
+        case 'B':
+            value->i = dvmGetFieldByteVolatile(obj, ifield->byteOffset);
+            break;
+        case 'S':
+            value->i = dvmGetFieldShortVolatile(obj, ifield->byteOffset);
+            break;
+        case 'C':
+            value->i = dvmGetFieldCharVolatile(obj, ifield->byteOffset);
+            break;
+        case 'I':
+            value->i = dvmGetFieldIntVolatile(obj, ifield->byteOffset);
+            break;
+        case 'F':
+            value->f = dvmGetFieldFloatVolatile(obj, ifield->byteOffset);
+            break;
+        case 'J':
+            value->j = dvmGetFieldLongVolatile(obj, ifield->byteOffset);
+            break;
+        case 'D':
+            value->d = dvmGetFieldDoubleVolatile(obj, ifield->byteOffset);
+            break;
+        case 'L':
+        case '[':
+            value->l = dvmGetFieldObjectVolatile(obj, ifield->byteOffset);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Copies the value of the static or instance field into "*value".
+ */
+static void getFieldValue(const Field* field, Object* obj, JValue* value)
+{
+    if (dvmIsStaticField(field)) {
+        return getStaticFieldValue((const StaticField*) field, value);
+    } else {
+        return getInstFieldValue((const InstField*) field, obj, value);
+    }
+}
+
+/*
+ * Sets the value of a static field.  Provides appropriate barriers
+ * for volatile fields.
+ */
+static void setStaticFieldValue(StaticField* sfield, const JValue* value)
+{
+    if (!dvmIsVolatileField(sfield)) {
+        switch (sfield->signature[0]) {
+        case 'L':
+        case '[':
+            dvmSetStaticFieldObject(sfield, (Object*)value->l);
+            break;
+        default:
+            /* just copy the whole thing */
+            sfield->value = *value;
+            break;
+        }
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (sfield->signature[0]) {
+        case 'Z':
+            dvmSetStaticFieldBooleanVolatile(sfield, value->z);
+            break;
+        case 'B':
+            dvmSetStaticFieldByteVolatile(sfield, value->b);
+            break;
+        case 'S':
+            dvmSetStaticFieldShortVolatile(sfield, value->s);
+            break;
+        case 'C':
+            dvmSetStaticFieldCharVolatile(sfield, value->c);
+            break;
+        case 'I':
+            dvmSetStaticFieldIntVolatile(sfield, value->i);
+            break;
+        case 'F':
+            dvmSetStaticFieldFloatVolatile(sfield, value->f);
+            break;
+        case 'J':
+            dvmSetStaticFieldLongVolatile(sfield, value->j);
+            break;
+        case 'D':
+            dvmSetStaticFieldDoubleVolatile(sfield, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetStaticFieldObjectVolatile(sfield, (Object*)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", sfield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Sets the value of an instance field.  Provides appropriate barriers
+ * for volatile fields.
+ */
+static void setInstFieldValue(InstField* ifield, Object* obj,
+    const JValue* value)
+{
+    if (!dvmIsVolatileField(ifield)) {
+        /* use type-specific set; really just 32-bit vs. 64-bit */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            dvmSetFieldBoolean(obj, ifield->byteOffset, value->z);
+            break;
+        case 'B':
+            dvmSetFieldByte(obj, ifield->byteOffset, value->b);
+            break;
+        case 'S':
+            dvmSetFieldShort(obj, ifield->byteOffset, value->s);
+            break;
+        case 'C':
+            dvmSetFieldChar(obj, ifield->byteOffset, value->c);
+            break;
+        case 'I':
+            dvmSetFieldInt(obj, ifield->byteOffset, value->i);
+            break;
+        case 'F':
+            dvmSetFieldFloat(obj, ifield->byteOffset, value->f);
+            break;
+        case 'J':
+            dvmSetFieldLong(obj, ifield->byteOffset, value->j);
+            break;
+        case 'D':
+            dvmSetFieldDouble(obj, ifield->byteOffset, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetFieldObject(obj, ifield->byteOffset, (Object *)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+#if ANDROID_SMP != 0
+        /*
+         * Special handling for final fields on SMP systems.  We need a
+         * store/store barrier here (JMM requirement).
+         */
+        if (dvmIsFinalField(ifield)) {
+            ANDROID_MEMBAR_STORE();
+        }
+#endif
+    } else {
+        /* need memory barriers and/or 64-bit atomic ops */
+        switch (ifield->signature[0]) {
+        case 'Z':
+            dvmSetFieldBooleanVolatile(obj, ifield->byteOffset, value->z);
+            break;
+        case 'B':
+            dvmSetFieldByteVolatile(obj, ifield->byteOffset, value->b);
+            break;
+        case 'S':
+            dvmSetFieldShortVolatile(obj, ifield->byteOffset, value->s);
+            break;
+        case 'C':
+            dvmSetFieldCharVolatile(obj, ifield->byteOffset, value->c);
+            break;
+        case 'I':
+            dvmSetFieldIntVolatile(obj, ifield->byteOffset, value->i);
+            break;
+        case 'F':
+            dvmSetFieldFloatVolatile(obj, ifield->byteOffset, value->f);
+            break;
+        case 'J':
+            dvmSetFieldLongVolatile(obj, ifield->byteOffset, value->j);
+            break;
+        case 'D':
+            dvmSetFieldDoubleVolatile(obj, ifield->byteOffset, value->d);
+            break;
+        case 'L':
+        case '[':
+            dvmSetFieldObjectVolatile(obj, ifield->byteOffset, (Object*)value->l);
+            break;
+        default:
+            ALOGE("Unhandled field signature '%s'", ifield->signature);
+            dvmAbort();
+        }
+    }
+}
+
+/*
+ * Copy "*value" into the static or instance field.
+ */
+static void setFieldValue(Field* field, Object* obj, const JValue* value)
+{
+    if (dvmIsStaticField(field)) {
+        return setStaticFieldValue((StaticField*) field, value);
+    } else {
+        return setInstFieldValue((InstField*) field, obj, value);
+    }
+}
+
+
+
+/*
+ * public int getFieldModifiers(Class declaringClass, int slot)
+ */
+static void Dalvik_java_lang_reflect_Field_getFieldModifiers(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK);
+}
+
+/*
+ * private Object getField(Object o, Class declaringClass, Class type,
+ *     int slot, boolean noAccessCheck)
+ *
+ * Primitive types need to be boxed.
+ */
+static void Dalvik_java_lang_reflect_Field_getField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Field* field;
+    JValue value;
+    DataObject* result;
+
+    //dvmDumpClass(obj->clazz, kDumpClassFullDetail);
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
+    if (field == NULL)
+        RETURN_VOID();
+
+    getFieldValue(field, obj, &value);
+
+    /* if it's primitive, box it up */
+    result = dvmBoxPrimitive(value, fieldType);
+    dvmReleaseTrackedAlloc((Object*) result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * private void setField(Object o, Class declaringClass, Class type,
+ *     int slot, boolean noAccessCheck, Object value)
+ *
+ * When assigning into a primitive field we will automatically extract
+ * the value from box types.
+ */
+static void Dalvik_java_lang_reflect_Field_setField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    Object* valueObj = (Object*) args[6];
+    Field* field;
+    JValue value;
+
+    /* unbox primitive, or verify object type */
+    if (!dvmUnboxPrimitive(valueObj, fieldType, &value)) {
+        dvmThrowIllegalArgumentException("invalid value for field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
+
+    if (field != NULL) {
+        setFieldValue(field, obj, &value);
+    }
+    RETURN_VOID();
+}
+
+/*
+ * Primitive field getters, e.g.:
+ * private double getIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, char descriptor)
+ */
+static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    jchar descriptor = args[6];
+    PrimitiveType targetType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
+    const Field* field;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowIllegalArgumentException("not a primitive field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
+    if (field == NULL)
+        RETURN_VOID();
+
+    getFieldValue(field, obj, &value);
+
+    /* retrieve value, performing a widening conversion if necessary */
+    if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
+        &(value.i), &(pResult->i)) < 0)
+    {
+        dvmThrowIllegalArgumentException("invalid primitive conversion");
+        RETURN_VOID();
+    }
+}
+
+/*
+ * Primitive field setters, e.g.:
+ * private void setIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, char descriptor, int value)
+ */
+static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    Object* obj = (Object*) args[1];
+    ClassObject* declaringClass = (ClassObject*) args[2];
+    ClassObject* fieldType = (ClassObject*) args[3];
+    int slot = args[4];
+    bool noAccessCheck = (args[5] != 0);
+    jchar descriptor = args[6];
+    const s4* valuePtr = (s4*) &args[7];    /* 64-bit vars spill into args[8] */
+    PrimitiveType srcType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
+    Field* field;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowIllegalArgumentException("not a primitive field");
+        RETURN_VOID();
+    }
+
+    /* convert the 32/64-bit arg to a JValue matching the field type */
+    if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
+        valuePtr, &(value.i)) < 0)
+    {
+        dvmThrowIllegalArgumentException("invalid primitive conversion");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the Field after validating access */
+    field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
+
+    if (field != NULL) {
+        setFieldValue(field, obj, &value);
+    }
+    RETURN_VOID();
+}
+
+/*
+ * private static Annotation[] getDeclaredAnnotations(
+ *         Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this field.
+ */
+static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    ArrayObject* annos = dvmGetFieldAnnotations(field);
+    dvmReleaseTrackedAlloc((Object*) annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * static Annotation getAnnotation(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Field_getAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Field* field = dvmSlotToField(clazz, slot);
+    RETURN_PTR(dvmGetFieldAnnotation(clazz, field, annotationClazz));
+}
+
+/*
+ * static boolean isAnnotationPresent(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Field_isAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Field* field = dvmSlotToField(clazz, slot);
+    RETURN_BOOLEAN(dvmIsFieldAnnotationPresent(clazz, field, annotationClazz));
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
+    JValue* pResult)
+{
+    /* ignore thisPtr in args[0] */
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
+    { "getFieldModifiers",  "(Ljava/lang/Class;I)I",
+        Dalvik_java_lang_reflect_Field_getFieldModifiers },
+    { "getField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Field_getField },
+    { "getBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)B",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)C",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)D",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)F",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)I",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)J",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)S",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)Z",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "setField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
+        Dalvik_java_lang_reflect_Field_setField },
+    { "setBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCB)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCC)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCD)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCF)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCI)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCJ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCS)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCZ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
+    { "getAnnotation", "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Field_getAnnotation },
+    { "isAnnotationPresent", "(Ljava/lang/Class;ILjava/lang/Class;)Z",
+        Dalvik_java_lang_reflect_Field_isAnnotationPresent },
+    { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Method.cpp b/vm/native/java_lang_reflect_Method.cpp
new file mode 100644
index 0000000..33d98c5
--- /dev/null
+++ b/vm/native/java_lang_reflect_Method.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.Method
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static int getMethodModifiers(Class decl_class, int slot)
+ *
+ * (Not sure why the access flags weren't stored in the class along with
+ * everything else.  Not sure why this isn't static.)
+ */
+static void Dalvik_java_lang_reflect_Method_getMethodModifiers(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    RETURN_INT(dvmFixMethodFlags(meth->accessFlags));
+}
+
+/*
+ * private Object invokeNative(Object obj, Object[] args, Class declaringClass,
+ *   Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck)
+ *
+ * Invoke a static or virtual method via reflection.
+ */
+static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args,
+    JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    Object* methObj = (Object*) args[1];        // null for static methods
+    ArrayObject* argList = (ArrayObject*) args[2];
+    ClassObject* declaringClass = (ClassObject*) args[3];
+    ArrayObject* params = (ArrayObject*) args[4];
+    ClassObject* returnType = (ClassObject*) args[5];
+    int slot = args[6];
+    bool noAccessCheck = (args[7] != 0);
+    const Method* meth;
+    Object* result;
+
+    /*
+     * "If the underlying method is static, the class that declared the
+     * method is initialized if it has not already been initialized."
+     */
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    if (dvmIsStaticMethod(meth)) {
+        if (!dvmIsClassInitialized(declaringClass)) {
+            if (!dvmInitClass(declaringClass))
+                goto init_failed;
+        }
+    } else {
+        /* looks like interfaces need this too? */
+        if (dvmIsInterfaceClass(declaringClass) &&
+            !dvmIsClassInitialized(declaringClass))
+        {
+            if (!dvmInitClass(declaringClass))
+                goto init_failed;
+        }
+
+        /* make sure the object is an instance of the expected class */
+        if (!dvmVerifyObjectInClass(methObj, declaringClass)) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+
+        /* do the virtual table lookup for the method */
+        meth = dvmGetVirtualizedMethod(methObj->clazz, meth);
+        if (meth == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            RETURN_VOID();
+        }
+    }
+
+    /*
+     * If the method has a return value, "result" will be an object or
+     * a boxed primitive.
+     */
+    result = dvmInvokeMethod(methObj, meth, argList, params, returnType,
+                noAccessCheck);
+
+    RETURN_PTR(result);
+
+init_failed:
+    /*
+     * If initialization failed, an exception will be raised.
+     */
+    ALOGD("Method.invoke() on bad class %s failed",
+        declaringClass->descriptor);
+    assert(dvmCheckException(dvmThreadSelf()));
+    RETURN_VOID();
+}
+
+/*
+ * static Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method.
+ */
+static void Dalvik_java_lang_reflect_Method_getDeclaredAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetMethodAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * static Annotation getAnnotation(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Method_getAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Method* meth = dvmSlotToMethod(clazz, slot);
+    RETURN_PTR(dvmGetMethodAnnotation(clazz, meth, annotationClazz));
+}
+
+/*
+ * static boolean isAnnotationPresent(
+ *         Class declaringClass, int slot, Class annotationType);
+ */
+static void Dalvik_java_lang_reflect_Method_isAnnotationPresent(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    int slot = args[1];
+    ClassObject* annotationClazz = (ClassObject*) args[2];
+
+    Method* meth = dvmSlotToMethod(clazz, slot);
+    RETURN_BOOLEAN(dvmIsMethodAnnotationPresent(clazz, meth, annotationClazz));
+}
+
+/*
+ * static Annotation[][] getParameterAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method's parameters.
+ */
+static void Dalvik_java_lang_reflect_Method_getParameterAnnotations(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetParameterAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * private Object getDefaultValue(Class declaringClass, int slot)
+ *
+ * Return the default value for the annotation member represented by
+ * this Method instance.  Returns NULL if none is defined.
+ */
+static void Dalvik_java_lang_reflect_Method_getDefaultValue(const u4* args,
+    JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    /* make sure this is an annotation class member */
+    if (!dvmIsAnnotationClass(declaringClass))
+        RETURN_PTR(NULL);
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    Object* def = dvmGetAnnotationDefaultValue(meth);
+    dvmReleaseTrackedAlloc(def, NULL);
+    RETURN_PTR(def);
+}
+
+/*
+ * static Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Method_getSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    ClassObject* declaringClass = (ClassObject*) args[0];
+    int slot = args[1];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth);
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Method[] = {
+    { "getMethodModifiers", "(Ljava/lang/Class;I)I",
+        Dalvik_java_lang_reflect_Method_getMethodModifiers },
+    { "invokeNative",       "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_invokeNative },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getDeclaredAnnotations },
+    { "getAnnotation", "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getAnnotation },
+    { "isAnnotationPresent", "(Ljava/lang/Class;ILjava/lang/Class;)Z",
+        Dalvik_java_lang_reflect_Method_isAnnotationPresent },
+    { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Method_getParameterAnnotations },
+    { "getDefaultValue",    "(Ljava/lang/Class;I)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_getDefaultValue },
+    { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Method_getSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Proxy.cpp b/vm/native/java_lang_reflect_Proxy.cpp
new file mode 100644
index 0000000..da1232c
--- /dev/null
+++ b/vm/native/java_lang_reflect_Proxy.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.lang.reflect.Proxy
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class generateProxy(String name, Class[] interfaces,
+ *      ClassLoader loader)
+ *
+ * Generate a proxy class with the specified characteristics.  Throws an
+ * exception on error.
+ */
+static void Dalvik_java_lang_reflect_Proxy_generateProxy(const u4* args,
+    JValue* pResult)
+{
+    StringObject* str = (StringObject*) args[0];
+    ArrayObject* interfaces = (ArrayObject*) args[1];
+    Object* loader = (Object*) args[2];
+    ClassObject* result;
+
+    result = dvmGenerateProxyClass(str, interfaces, loader);
+    RETURN_PTR(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Proxy[] = {
+    { "generateProxy", "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/lang/Class;",
+        Dalvik_java_lang_reflect_Proxy_generateProxy },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_util_concurrent_atomic_AtomicLong.cpp b/vm/native/java_util_concurrent_atomic_AtomicLong.cpp
new file mode 100644
index 0000000..eb1d0de
--- /dev/null
+++ b/vm/native/java_util_concurrent_atomic_AtomicLong.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * java.util.concurrent.atomic.AtomicLong
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native boolean VMSupportsCS8();
+ */
+static void Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    RETURN_BOOLEAN(1);
+}
+
+const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[] = {
+    { "VMSupportsCS8", "()Z",
+        Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
new file mode 100644
index 0000000..ccc9467
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * org.apache.harmony.dalvik.NativeTestTarget
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void emptyInternalStaticMethod()
+ *
+ * For benchmarks, a do-nothing internal method with no arguments.
+ */
+static void Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[] =
+{
+    { "emptyInternalStaticMethod", "()V",
+        Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp
new file mode 100644
index 0000000..570d469
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmServer
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static void nativeSendChunk(int type, byte[] data,
+ *      int offset, int length)
+ *
+ * Send a DDM chunk to the server.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk(
+    const u4* args, JValue* pResult)
+{
+    int type = args[0];
+    ArrayObject* data = (ArrayObject*) args[1];
+    int offset = args[2];
+    int length = args[3];
+
+    assert(offset+length <= (int)data->length);
+
+    dvmDbgDdmSendChunk(type, length, (const u1*)data->contents + offset);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[] = {
+    { "nativeSendChunk",    "(I[BII)V",
+        Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp
new file mode 100644
index 0000000..558b2ff
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmVmInternal
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void threadNotify(boolean enable)
+ *
+ * Enable DDM thread notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify(
+    const u4* args, JValue* pResult)
+{
+    bool enable = (args[0] != 0);
+
+    //ALOGI("ddmThreadNotification: %d", enable);
+    dvmDdmSetThreadNotification(enable);
+    RETURN_VOID();
+}
+
+/*
+ * public static byte[] getThreadStats()
+ *
+ * Get a buffer full of thread info.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    ArrayObject* result = dvmDdmGenerateThreadStats();
+    dvmReleaseTrackedAlloc((Object*) result, NULL);
+    RETURN_PTR(result);
+}
+
+/*
+ * public static int heapInfoNotify(int what)
+ *
+ * Enable DDM heap notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify(
+    const u4* args, JValue* pResult)
+{
+    int when = args[0];
+    bool ret;
+
+    ret = dvmDdmHandleHpifChunk(when);
+    RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static boolean heapSegmentNotify(int when, int what, bool native)
+ *
+ * Enable DDM heap notifications.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify(
+    const u4* args, JValue* pResult)
+{
+    int  when   = args[0];        // 0=never (off), 1=during GC
+    int  what   = args[1];        // 0=merged objects, 1=distinct objects
+    bool native = (args[2] != 0); // false=virtual heap, true=native heap
+    bool ret;
+
+    ret = dvmDdmHandleHpsgNhsgChunk(when, what, native);
+    RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static StackTraceElement[] getStackTraceById(int threadId)
+ *
+ * Get a stack trace as an array of StackTraceElement objects.  Returns
+ * NULL on failure, e.g. if the threadId couldn't be found.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById(
+    const u4* args, JValue* pResult)
+{
+    u4 threadId = args[0];
+    ArrayObject* trace;
+
+    trace = dvmDdmGetStackTraceById(threadId);
+    RETURN_PTR(trace);
+}
+
+/*
+ * public static void enableRecentAllocations(boolean enable)
+ *
+ * Enable or disable recent allocation tracking.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations(
+    const u4* args, JValue* pResult)
+{
+    bool enable = (args[0] != 0);
+
+    if (enable)
+        (void) dvmEnableAllocTracker();
+    else
+        (void) dvmDisableAllocTracker();
+    RETURN_VOID();
+}
+
+/*
+ * public static boolean getRecentAllocationStatus()
+ *
+ * Returns "true" if allocation tracking is enabled.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+    RETURN_BOOLEAN(gDvm.allocRecords != NULL);
+}
+
+/*
+ * public static byte[] getRecentAllocations()
+ *
+ * Fill a buffer with data on recent heap allocations.
+ */
+static void
+    Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations(
+    const u4* args, JValue* pResult)
+{
+    ArrayObject* data;
+
+    data = dvmDdmGetRecentAllocations();
+    dvmReleaseTrackedAlloc((Object*) data, NULL);
+    RETURN_PTR(data);
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[] = {
+    { "threadNotify",       "(Z)V",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify },
+    { "getThreadStats",     "()[B",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats },
+    { "heapInfoNotify",     "(I)Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify },
+    { "heapSegmentNotify",  "(IIZ)Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify },
+    { "getStackTraceById",  "(I)[Ljava/lang/StackTraceElement;",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById },
+    { "enableRecentAllocations", "(Z)V",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations },
+    { "getRecentAllocationStatus", "()Z",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus },
+    { "getRecentAllocations", "()[B",
+      Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/sun_misc_Unsafe.cpp b/vm/native/sun_misc_Unsafe.cpp
new file mode 100644
index 0000000..db8493b
--- /dev/null
+++ b/vm/native/sun_misc_Unsafe.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * sun.misc.Unsafe
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native long objectFieldOffset0(Field field);
+ */
+static void Dalvik_sun_misc_Unsafe_objectFieldOffset0(const u4* args,
+    JValue* pResult)
+{
+    Object* fieldObject = (Object*) args[0];
+    InstField* field = (InstField*) dvmGetFieldFromReflectObj(fieldObject);
+    s8 result = ((s8) field->byteOffset);
+
+    RETURN_LONG(result);
+}
+
+/*
+ * private static native int arrayBaseOffset0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayBaseOffset0(const u4* args,
+    JValue* pResult)
+{
+    // The base offset is not type-dependent in this vm.
+    UNUSED_PARAMETER(args);
+    RETURN_INT(OFFSETOF_MEMBER(ArrayObject, contents));
+}
+
+/*
+ * private static native int arrayIndexScale0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayIndexScale0(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    RETURN_INT(dvmArrayClassElementWidth(clazz));
+}
+
+/*
+ * public native boolean compareAndSwapInt(Object obj, long offset,
+ *         int expectedValue, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapInt(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 expectedValue = args[4];
+    s4 newValue = args[5];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_release_cas() returns 0 on success, not failure.
+    int result = android_atomic_release_cas(expectedValue, newValue, address);
+
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapLong(Object obj, long offset,
+ *         long expectedValue, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapLong(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 expectedValue = GET_ARG_LONG(args, 4);
+    s8 newValue = GET_ARG_LONG(args, 6);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+    int result =
+        dvmQuasiAtomicCas64(expectedValue, newValue, address);
+
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapObject(Object obj, long offset,
+ *         Object expectedValue, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapObject(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* expectedValue = (Object*) args[4];
+    Object* newValue = (Object*) args[5];
+    int32_t* address = (int32_t*) (((u1*) obj) + offset);
+
+    // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+    int result = android_atomic_release_cas((int32_t) expectedValue,
+            (int32_t) newValue, address);
+    dvmWriteBarrierField(obj, address);
+    RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native int getIntVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getIntVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    int32_t value = android_atomic_acquire_load(address);
+    RETURN_INT(value);
+}
+
+/*
+ * public native void putIntVolatile(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putIntVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    android_atomic_release_store(value, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native long getLongVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLongVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    assert((offset & 7) == 0);
+    RETURN_LONG(dvmQuasiAtomicRead64(address));
+}
+
+/*
+ * public native void putLongVolatile(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLongVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+    assert((offset & 7) == 0);
+    dvmQuasiAtomicSwap64(value, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native Object getObjectVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObjectVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    RETURN_PTR((Object*) android_atomic_acquire_load(address));
+}
+
+/*
+ * public native void putObjectVolatile(Object obj, long offset,
+ *         Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObjectVolatile(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+    android_atomic_release_store((int32_t)value, address);
+    dvmWriteBarrierField(obj, (void *)address);
+    RETURN_VOID();
+}
+
+/*
+ * public native int getInt(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getInt(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    RETURN_INT(*address);
+}
+
+/*
+ * public native void putInt(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putInt(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedInt(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedInt(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s4 value = (s4) args[4];
+    s4* address = (s4*) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native long getLong(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLong(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    RETURN_LONG(*address);
+}
+
+/*
+ * public native void putLong(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLong(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedLong(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedLong(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    s8 value = GET_ARG_LONG(args, 4);
+    s8* address = (s8*) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    RETURN_VOID();
+}
+
+/*
+ * public native Object getObject(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObject(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    RETURN_PTR(*address);
+}
+
+/*
+ * public native void putObject(Object obj, long offset, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObject(const u4* args, JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    *address = value;
+    dvmWriteBarrierField(obj, address);
+    RETURN_VOID();
+}
+
+/*
+ * public native void putOrderedObject(Object obj, long offset,
+ *      Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putOrderedObject(const u4* args,
+    JValue* pResult)
+{
+    // We ignore the this pointer in args[0].
+    Object* obj = (Object*) args[1];
+    s8 offset = GET_ARG_LONG(args, 2);
+    Object* value = (Object*) args[4];
+    Object** address = (Object**) (((u1*) obj) + offset);
+
+    ANDROID_MEMBAR_STORE();
+    *address = value;
+    dvmWriteBarrierField(obj, address);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_sun_misc_Unsafe[] = {
+    { "objectFieldOffset0", "(Ljava/lang/reflect/Field;)J",
+      Dalvik_sun_misc_Unsafe_objectFieldOffset0 },
+    { "arrayBaseOffset0", "(Ljava/lang/Class;)I",
+      Dalvik_sun_misc_Unsafe_arrayBaseOffset0 },
+    { "arrayIndexScale0", "(Ljava/lang/Class;)I",
+      Dalvik_sun_misc_Unsafe_arrayIndexScale0 },
+    { "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapInt },
+    { "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapLong },
+    { "compareAndSwapObject",
+      "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+      Dalvik_sun_misc_Unsafe_compareAndSwapObject },
+    { "getIntVolatile", "(Ljava/lang/Object;J)I",
+      Dalvik_sun_misc_Unsafe_getIntVolatile },
+    { "putIntVolatile", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putIntVolatile },
+    { "getLongVolatile", "(Ljava/lang/Object;J)J",
+      Dalvik_sun_misc_Unsafe_getLongVolatile },
+    { "putLongVolatile", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putLongVolatile },
+    { "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+      Dalvik_sun_misc_Unsafe_getObjectVolatile },
+    { "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putObjectVolatile },
+    { "getInt", "(Ljava/lang/Object;J)I",
+      Dalvik_sun_misc_Unsafe_getInt },
+    { "putInt", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putInt },
+    { "putOrderedInt", "(Ljava/lang/Object;JI)V",
+      Dalvik_sun_misc_Unsafe_putOrderedInt },
+    { "getLong", "(Ljava/lang/Object;J)J",
+      Dalvik_sun_misc_Unsafe_getLong },
+    { "putLong", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putLong },
+    { "putOrderedLong", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putOrderedLong },
+    { "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+      Dalvik_sun_misc_Unsafe_getObject },
+    { "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putObject },
+    { "putOrderedObject", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+      Dalvik_sun_misc_Unsafe_putOrderedObject },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/oo/AccessCheck.cpp b/vm/oo/AccessCheck.cpp
new file mode 100644
index 0000000..cef4dab
--- /dev/null
+++ b/vm/oo/AccessCheck.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#include "Dalvik.h"
+
+/*
+ * Return the #of initial characters that match.
+ */
+static int strcmpCount(const char* str1, const char* str2)
+{
+    int count = 0;
+
+    while (true) {
+        char ch = str1[count];
+        if (ch == '\0' || ch != str2[count])
+            return count;
+        count++;
+    }
+}
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
+{
+    /* quick test for intra-class access */
+    if (class1 == class2)
+        return true;
+
+    /* class loaders must match */
+    if (class1->classLoader != class2->classLoader)
+        return false;
+
+    /*
+     * Switch array classes to their element types.  Arrays receive the
+     * class loader of the underlying element type.  The point of doing
+     * this is to get the un-decorated class name, without all the
+     * "[[L...;" stuff.
+     */
+    if (dvmIsArrayClass(class1))
+        class1 = class1->elementClass;
+    if (dvmIsArrayClass(class2))
+        class2 = class2->elementClass;
+
+    /* check again */
+    if (class1 == class2)
+        return true;
+
+    /*
+     * We have two classes with different names.  Compare them and see
+     * if they match up through the final '/'.
+     *
+     *  Ljava/lang/Object; + Ljava/lang/Class;          --> true
+     *  LFoo;              + LBar;                      --> true
+     *  Ljava/lang/Object; + Ljava/io/File;             --> false
+     *  Ljava/lang/Object; + Ljava/lang/reflect/Method; --> false
+     */
+    int commonLen;
+
+    commonLen = strcmpCount(class1->descriptor, class2->descriptor);
+    if (strchr(class1->descriptor + commonLen, '/') != NULL ||
+        strchr(class2->descriptor + commonLen, '/') != NULL)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Validate method/field access.
+ */
+static bool checkAccess(const ClassObject* accessFrom,
+    const ClassObject* accessTo, u4 accessFlags)
+{
+    /* quick accept for public access */
+    if (accessFlags & ACC_PUBLIC)
+        return true;
+
+    /* quick accept for access from same class */
+    if (accessFrom == accessTo)
+        return true;
+
+    /* quick reject for private access from another class */
+    if (accessFlags & ACC_PRIVATE)
+        return false;
+
+    /*
+     * Semi-quick test for protected access from a sub-class, which may or
+     * may not be in the same package.
+     */
+    if (accessFlags & ACC_PROTECTED)
+        if (dvmIsSubClass(accessFrom, accessTo))
+            return true;
+
+    /*
+     * Allow protected and private access from other classes in the same
+     * package.
+     */
+    return dvmInSamePackage(accessFrom, accessTo);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ *
+ * It's allowed if "clazz" is public or is in the same package.  (Only
+ * inner classes can be marked "private" or "protected", so we don't need
+ * to check for it here.)
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+    const ClassObject* clazz)
+{
+    if (dvmIsPublicClass(clazz))
+        return true;
+    return dvmInSamePackage(accessFrom, clazz);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method)
+{
+    return checkAccess(accessFrom, method->clazz, method->accessFlags);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field)
+{
+    //ALOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=%#x",
+    //    accessFrom->descriptor, field->name,
+    //    field->clazz->descriptor, field->accessFlags);
+    return checkAccess(accessFrom, field->clazz, field->accessFlags);
+}
diff --git a/vm/oo/AccessCheck.h b/vm/oo/AccessCheck.h
new file mode 100644
index 0000000..61d1650
--- /dev/null
+++ b/vm/oo/AccessCheck.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#ifndef DALVIK_OO_ACCESSCHECK_H_
+#define DALVIK_OO_ACCESSCHECK_H_
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+    const ClassObject* clazz);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field);
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2);
+
+#endif  // DALVIK_OO_ACCESSCHECK_H_
diff --git a/vm/oo/Array.cpp b/vm/oo/Array.cpp
new file mode 100644
index 0000000..f5ede04
--- /dev/null
+++ b/vm/oo/Array.cpp
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Array objects.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+
+/* width of an object reference, for arrays of objects */
+static size_t kObjectArrayRefWidth = sizeof(Object*);
+
+static ClassObject* createArrayClass(const char* descriptor, Object* loader);
+
+/*
+ * Allocate space for a new array object.  This is the lowest-level array
+ * allocation function.
+ *
+ * Pass in the array class and the width of each element.
+ *
+ * On failure, returns NULL with an exception raised.
+ */
+static ArrayObject* allocArray(ClassObject* arrayClass, size_t length,
+    size_t elemWidth, int allocFlags)
+{
+    assert(arrayClass != NULL);
+    assert(arrayClass->descriptor != NULL);
+    assert(arrayClass->descriptor[0] == '[');
+    assert(length <= 0x7fffffff);
+    assert(elemWidth > 0);
+    assert(elemWidth <= 8);
+    assert((elemWidth & (elemWidth - 1)) == 0);
+    size_t elementShift = sizeof(size_t) * CHAR_BIT - 1 - CLZ(elemWidth);
+    size_t elementSize = length << elementShift;
+    size_t headerSize = OFFSETOF_MEMBER(ArrayObject, contents);
+    size_t totalSize = elementSize + headerSize;
+    if (elementSize >> elementShift != length || totalSize < elementSize) {
+        std::string descriptor(dvmHumanReadableDescriptor(arrayClass->descriptor));
+        dvmThrowExceptionFmt(gDvm.exOutOfMemoryError,
+                "%s of length %zd exceeds the VM limit", descriptor.c_str(), length);
+        return NULL;
+    }
+    ArrayObject* newArray = (ArrayObject*)dvmMalloc(totalSize, allocFlags);
+    if (newArray != NULL) {
+        DVM_OBJECT_INIT(newArray, arrayClass);
+        newArray->length = length;
+        ANDROID_MEMBAR_STORE();
+        dvmTrackAllocation(arrayClass, totalSize);
+    }
+    return newArray;
+}
+
+/*
+ * Create a new array, given an array class.  The class may represent an
+ * array of references or primitives.
+ */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+    size_t length, int allocFlags)
+{
+    const char* descriptor = arrayClass->descriptor;
+
+    assert(descriptor[0] == '[');       /* must be array class */
+    if (descriptor[1] != '[' && descriptor[1] != 'L') {
+        /* primitive array */
+        assert(descriptor[2] == '\0');
+        return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags);
+    } else {
+        return allocArray(arrayClass, length, kObjectArrayRefWidth,
+            allocFlags);
+    }
+}
+
+/*
+ * Find the array class for "elemClassObj", which could itself be an
+ * array class.
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj)
+{
+    ClassObject* arrayClass;
+
+    assert(elemClassObj != NULL);
+
+    /* Simply prepend "[" to the descriptor. */
+    int nameLen = strlen(elemClassObj->descriptor);
+    char className[nameLen + 2];
+
+    className[0] = '[';
+    memcpy(className+1, elemClassObj->descriptor, nameLen+1);
+    arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader);
+
+    return arrayClass;
+}
+
+/*
+ * Create a new array that holds primitive types.
+ *
+ * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
+{
+    ArrayObject* newArray;
+    ClassObject* arrayClass;
+    int width;
+
+    switch (type) {
+    case 'I':
+        arrayClass = gDvm.classArrayInt;
+        width = 4;
+        break;
+    case 'C':
+        arrayClass = gDvm.classArrayChar;
+        width = 2;
+        break;
+    case 'B':
+        arrayClass = gDvm.classArrayByte;
+        width = 1;
+        break;
+    case 'Z':
+        arrayClass = gDvm.classArrayBoolean;
+        width = 1; /* special-case this? */
+        break;
+    case 'F':
+        arrayClass = gDvm.classArrayFloat;
+        width = 4;
+        break;
+    case 'D':
+        arrayClass = gDvm.classArrayDouble;
+        width = 8;
+        break;
+    case 'S':
+        arrayClass = gDvm.classArrayShort;
+        width = 2;
+        break;
+    case 'J':
+        arrayClass = gDvm.classArrayLong;
+        width = 8;
+        break;
+    default:
+        ALOGE("Unknown primitive type '%c'", type);
+        dvmAbort();
+        return NULL; // Keeps the compiler happy.
+    }
+
+    newArray = allocArray(arrayClass, length, width, allocFlags);
+
+    /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */
+    return newArray;
+}
+
+/*
+ * Recursively create an array with multiple dimensions.  Elements may be
+ * Objects or primitive types.
+ *
+ * The dimension we're creating is in dimensions[0], so when we recurse
+ * we advance the pointer.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+    const int* dimensions)
+{
+    ArrayObject* newArray;
+    const char* elemName = arrayClass->descriptor + 1; // Advance past one '['.
+
+    LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d",
+        arrayClass->descriptor, curDim, *dimensions);
+
+    if (curDim == 0) {
+        if (*elemName == 'L' || *elemName == '[') {
+            LOGVV("  end: array class (obj) is '%s'",
+                arrayClass->descriptor);
+            newArray = allocArray(arrayClass, *dimensions,
+                        kObjectArrayRefWidth, ALLOC_DEFAULT);
+        } else {
+            LOGVV("  end: array class (prim) is '%s'",
+                arrayClass->descriptor);
+            newArray = dvmAllocPrimitiveArray(
+                    dexGetPrimitiveTypeDescriptorChar(arrayClass->elementClass->primitiveType),
+                    *dimensions, ALLOC_DEFAULT);
+        }
+    } else {
+        ClassObject* subArrayClass;
+        int i;
+
+        /* if we have X[][], find X[] */
+        subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader);
+        if (subArrayClass == NULL) {
+            /* not enough '['s on the initial class? */
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+        assert(dvmIsArrayClass(subArrayClass));
+
+        /* allocate the array that holds the sub-arrays */
+        newArray = allocArray(arrayClass, *dimensions, kObjectArrayRefWidth,
+                        ALLOC_DEFAULT);
+        if (newArray == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+
+        /*
+         * Create a new sub-array in every element of the array.
+         */
+        for (i = 0; i < *dimensions; i++) {
+          ArrayObject* newSubArray;
+          newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1,
+                          dimensions+1);
+            if (newSubArray == NULL) {
+                dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+                assert(dvmCheckException(dvmThreadSelf()));
+                return NULL;
+            }
+            dvmSetObjectArrayElement(newArray, i, (Object *)newSubArray);
+            dvmReleaseTrackedAlloc((Object*) newSubArray, NULL);
+        }
+    }
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return newArray;
+}
+
+
+/*
+ * Find an array class, by name (e.g. "[I").
+ *
+ * If the array class doesn't exist, we generate it.
+ *
+ * If the element class doesn't exist, we return NULL (no exception raised).
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
+{
+    ClassObject* clazz;
+
+    assert(descriptor[0] == '[');
+    //ALOGV("dvmFindArrayClass: '%s' %p", descriptor, loader);
+
+    clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz == NULL) {
+        ALOGV("Array class '%s' %p not found; creating", descriptor, loader);
+        clazz = createArrayClass(descriptor, loader);
+        if (clazz != NULL)
+            dvmAddInitiatingLoader(clazz, loader);
+    }
+
+    return clazz;
+}
+
+/*
+ * Create an array class (i.e. the class object for the array, not the
+ * array itself).  "descriptor" looks like "[C" or "[Ljava/lang/String;".
+ *
+ * If "descriptor" refers to an array of primitives, look up the
+ * primitive type's internally-generated class object.
+ *
+ * "loader" is the class loader of the class that's referring to us.  It's
+ * used to ensure that we're looking for the element type in the right
+ * context.  It does NOT become the class loader for the array class; that
+ * always comes from the base element class.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+static ClassObject* createArrayClass(const char* descriptor, Object* loader)
+{
+    ClassObject* newClass = NULL;
+    ClassObject* elementClass = NULL;
+    int arrayDim;
+    u4 extraFlags;
+
+    assert(descriptor[0] == '[');
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(gDvm.classJavaLangObject != NULL);
+
+    /*
+     * Identify the underlying element class and the array dimension depth.
+     */
+    extraFlags = CLASS_ISARRAY;
+    if (descriptor[1] == '[') {
+        /* array of arrays; keep descriptor and grab stuff from parent */
+        ClassObject* outer;
+
+        outer = dvmFindClassNoInit(&descriptor[1], loader);
+        if (outer != NULL) {
+            /* want the base class, not "outer", in our elementClass */
+            elementClass = outer->elementClass;
+            arrayDim = outer->arrayDim + 1;
+            extraFlags |= CLASS_ISOBJECTARRAY;
+        } else {
+            assert(elementClass == NULL);     /* make sure we fail */
+        }
+    } else {
+        arrayDim = 1;
+        if (descriptor[1] == 'L') {
+            /* array of objects; strip off "[" and look up descriptor. */
+            const char* subDescriptor = &descriptor[1];
+            LOGVV("searching for element class '%s'", subDescriptor);
+            elementClass = dvmFindClassNoInit(subDescriptor, loader);
+            extraFlags |= CLASS_ISOBJECTARRAY;
+        } else {
+            /* array of a primitive type */
+            elementClass = dvmFindPrimitiveClass(descriptor[1]);
+        }
+    }
+
+    if (elementClass == NULL) {
+        /* failed */
+        assert(dvmCheckException(dvmThreadSelf()));
+        dvmFreeClassInnards(newClass);
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+        return NULL;
+    }
+
+    /*
+     * See if it's already loaded.  Array classes are always associated
+     * with the class loader of their underlying element type -- an array
+     * of Strings goes with the loader for java/lang/String -- so we need
+     * to look for it there.  (The caller should have checked for the
+     * existence of the class before calling here, but they did so with
+     * *their* class loader, not the element class' loader.)
+     *
+     * If we find it, the caller adds "loader" to the class' initiating
+     * loader list, which should prevent us from going through this again.
+     *
+     * This call is unnecessary if "loader" and "elementClass->classLoader"
+     * are the same, because our caller (dvmFindArrayClass) just did the
+     * lookup.  (Even if we get this wrong we still have correct behavior,
+     * because we effectively do this lookup again when we add the new
+     * class to the hash table -- necessary because of possible races with
+     * other threads.)
+     */
+    if (loader != elementClass->classLoader) {
+        LOGVV("--- checking for '%s' in %p vs. elem %p",
+            descriptor, loader, elementClass->classLoader);
+        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+        if (newClass != NULL) {
+            ALOGV("--- we already have %s in %p, don't need in %p",
+                descriptor, elementClass->classLoader, loader);
+            return newClass;
+        }
+    }
+
+
+    /*
+     * Fill out the fields in the ClassObject.
+     *
+     * It is possible to execute some methods against arrays, because all
+     * arrays are instances of Object, so we need to set up a vtable.  We
+     * can just point at the one in Object.
+     *
+     * Array classes are simple enough that we don't need to do a full
+     * link step.
+     */
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_NON_MOVING);
+    if (newClass == NULL)
+        return NULL;
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = strdup(descriptor);
+    newClass->descriptor = newClass->descriptorAlloc;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, super),
+                      (Object *)gDvm.classJavaLangObject);
+    newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
+    newClass->vtable = gDvm.classJavaLangObject->vtable;
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, elementClass),
+                      (Object *)elementClass);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)elementClass->classLoader);
+    newClass->arrayDim = arrayDim;
+    newClass->status = CLASS_INITIALIZED;
+
+    /* don't need to set newClass->objectSize */
+
+    /*
+     * All arrays have java/lang/Cloneable and java/io/Serializable as
+     * interfaces.  We need to set that up here, so that stuff like
+     * "instanceof" works right.
+     *
+     * Note: The GC could run during the call to dvmFindSystemClassNoInit(),
+     * so we need to make sure the class object is GC-valid while we're in
+     * there.  Do this by clearing the interface list so the GC will just
+     * think that the entries are null.
+     *
+     * TODO?
+     * We may want to cache these two classes to avoid the lookup, though
+     * it's not vital -- we only do it when creating an array class, not
+     * every time we create an array.  Better yet, create a single, global
+     * copy of "interfaces" and "iftable" somewhere near the start and
+     * just point to those (and remember not to free them for arrays).
+     */
+    newClass->interfaceCount = 2;
+    newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
+                                sizeof(ClassObject*) * 2);
+    memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2);
+    newClass->interfaces[0] =
+        dvmFindSystemClassNoInit("Ljava/lang/Cloneable;");
+    newClass->interfaces[1] =
+        dvmFindSystemClassNoInit("Ljava/io/Serializable;");
+    dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+    if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) {
+        ALOGE("Unable to create array class '%s': missing interfaces",
+            descriptor);
+        dvmFreeClassInnards(newClass);
+        dvmThrowInternalError("missing array ifaces");
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+        return NULL;
+    }
+    /*
+     * We assume that Cloneable/Serializable don't have superinterfaces --
+     * normally we'd have to crawl up and explicitly list all of the
+     * supers as well.  These interfaces don't have any methods, so we
+     * don't have to worry about the ifviPool either.
+     */
+    newClass->iftableCount = 2;
+    newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader,
+                                sizeof(InterfaceEntry) * 2);
+    memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2);
+    newClass->iftable[0].clazz = newClass->interfaces[0];
+    newClass->iftable[1].clazz = newClass->interfaces[1];
+    dvmLinearReadOnly(newClass->classLoader, newClass->iftable);
+
+    /*
+     * Inherit access flags from the element.  Arrays can't be used as a
+     * superclass or interface, so we want to add "abstract final" and remove
+     * "interface".
+     */
+    int accessFlags = elementClass->accessFlags;
+    if (!gDvm.optimizing) {
+        // If the element class is an inner class, make sure we get the correct access flags.
+        StringObject* className = NULL;
+        dvmGetInnerClass(elementClass, &className, &accessFlags);
+        dvmReleaseTrackedAlloc((Object*) className, NULL);
+    }
+    accessFlags &= JAVA_FLAGS_MASK;
+    accessFlags &= ~ACC_INTERFACE;
+    accessFlags |= ACC_ABSTRACT | ACC_FINAL;
+
+    // Set the flags we determined above.
+    SET_CLASS_FLAG(newClass, accessFlags | extraFlags);
+
+    if (!dvmAddClassToHash(newClass)) {
+        /*
+         * Another thread must have loaded the class after we
+         * started but before we finished.  Discard what we've
+         * done and leave some hints for the GC.
+         *
+         * (Yes, this happens.)
+         */
+
+        /* Clean up the class before letting the
+         * GC get its hands on it.
+         */
+        dvmFreeClassInnards(newClass);
+
+        /* Let the GC free the class.
+         */
+        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+        /* Grab the winning class.
+         */
+        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+        assert(newClass != NULL);
+        return newClass;
+    }
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    ALOGV("Created array class '%s' %p (access=0x%04x.%04x)",
+        descriptor, newClass->classLoader,
+        newClass->accessFlags >> 16,
+        newClass->accessFlags & JAVA_FLAGS_MASK);
+
+    return newClass;
+}
+
+/*
+ * Copy the entire contents of one array of objects to another.  If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass)
+{
+    Object** src = (Object**)(void*)srcArray->contents;
+    u4 length, count;
+
+    assert(srcArray->length == dstArray->length);
+    assert(dstArray->clazz->elementClass == dstElemClass ||
+        (dstArray->clazz->elementClass == dstElemClass->elementClass &&
+         dstArray->clazz->arrayDim == dstElemClass->arrayDim+1));
+
+    length = dstArray->length;
+    for (count = 0; count < length; count++) {
+        if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
+            ALOGW("dvmCopyObjectArray: can't store %s in %s",
+                src[count]->clazz->descriptor, dstElemClass->descriptor);
+            return false;
+        }
+        dvmSetObjectArrayElement(dstArray, count, src[count]);
+    }
+
+    return true;
+}
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives.  The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass)
+{
+    Object** src = (Object**)(void*)srcArray->contents;
+    void* dst = (void*)dstArray->contents;
+    u4 count = dstArray->length;
+    PrimitiveType typeIndex = dstElemClass->primitiveType;
+
+    assert(typeIndex != PRIM_NOT);
+    assert(srcArray->length == dstArray->length);
+
+    while (count--) {
+        JValue result;
+
+        /*
+         * This will perform widening conversions as appropriate.  It
+         * might make sense to be more restrictive and require that the
+         * primitive type exactly matches the box class, but it's not
+         * necessary for correctness.
+         */
+        if (!dvmUnboxPrimitive(*src, dstElemClass, &result)) {
+            ALOGW("dvmCopyObjectArray: can't store %s in %s",
+                (*src)->clazz->descriptor, dstElemClass->descriptor);
+            return false;
+        }
+
+        /* would be faster with 4 loops, but speed not crucial here */
+        switch (typeIndex) {
+        case PRIM_BOOLEAN:
+        case PRIM_BYTE:
+            {
+                u1* tmp = (u1*)dst;
+                *tmp++ = result.b;
+                dst = tmp;
+            }
+            break;
+        case PRIM_CHAR:
+        case PRIM_SHORT:
+            {
+                u2* tmp = (u2*)dst;
+                *tmp++ = result.s;
+                dst = tmp;
+            }
+            break;
+        case PRIM_FLOAT:
+        case PRIM_INT:
+            {
+                u4* tmp = (u4*)dst;
+                *tmp++ = result.i;
+                dst = tmp;
+            }
+            break;
+        case PRIM_DOUBLE:
+        case PRIM_LONG:
+            {
+                u8* tmp = (u8*)dst;
+                *tmp++ = result.j;
+                dst = tmp;
+            }
+            break;
+        default:
+            /* should not be possible to get here */
+            dvmAbort();
+        }
+
+        src++;
+    }
+
+    return true;
+}
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* arrayClass)
+{
+    const char *descriptor;
+
+    assert(dvmIsArrayClass(arrayClass));
+
+    if (dvmIsObjectArrayClass(arrayClass)) {
+        return sizeof(Object *);
+    } else {
+        descriptor = arrayClass->descriptor;
+        switch (descriptor[1]) {
+        case 'B': return 1;  /* byte */
+        case 'C': return 2;  /* char */
+        case 'D': return 8;  /* double */
+        case 'F': return 4;  /* float */
+        case 'I': return 4;  /* int */
+        case 'J': return 8;  /* long */
+        case 'S': return 2;  /* short */
+        case 'Z': return 1;  /* boolean */
+        }
+    }
+    ALOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+
+size_t dvmArrayObjectSize(const ArrayObject *array)
+{
+    assert(array != NULL);
+    size_t size = OFFSETOF_MEMBER(ArrayObject, contents);
+    size += array->length * dvmArrayClassElementWidth(array->clazz);
+    return size;
+}
diff --git a/vm/oo/Array.h b/vm/oo/Array.h
new file mode 100644
index 0000000..5390b1d
--- /dev/null
+++ b/vm/oo/Array.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Array handling.
+ */
+#ifndef DALVIK_OO_ARRAY_H_
+#define DALVIK_OO_ARRAY_H_
+
+/*
+ * Find a matching array class.  If it doesn't exist, create it.
+ *
+ * "descriptor" looks like "[I".
+ *
+ * "loader" should be the defining class loader for the elements held
+ * in the array.
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader);
+
+/*
+ * Find the array class for the specified class.  If "elemClassObj" is the
+ * class "Foo", this returns the class object for "[Foo".
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj);
+
+/*
+ * Create a new array, given an array class.  The class may represent an
+ * array of references or primitives.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+extern "C" ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+    size_t length, int allocFlags);
+
+/*
+ * Allocate an array whose members are primitives (bools, ints, etc.).
+ *
+ * "type" should be 'I', 'J', 'Z', etc.
+ *
+ * The new object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags);
+
+/*
+ * Allocate an array with multiple dimensions.  Elements may be Objects or
+ * primitive types.
+ *
+ * The base object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+    const int* dimensions);
+
+/*
+ * Verify that the object is actually an array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsArray(const ArrayObject* arrayObj)
+{
+    return ( ((Object*)arrayObj)->clazz->descriptor[0] == '[' );
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArrayClass(const ClassObject* clazz)
+{
+    const char* descriptor = clazz->descriptor;
+    return descriptor[0] == '[' && (descriptor[1] == 'L' ||
+                                    descriptor[1] == '[');
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArray(const ArrayObject* arrayObj)
+{
+    return dvmIsObjectArrayClass(arrayObj->clazz);
+}
+
+/*
+ * Verify that the class is an array class.
+ *
+ * TODO: there may be some performance advantage to setting a flag in
+ * the accessFlags field instead of chasing into the name string.
+ */
+INLINE bool dvmIsArrayClass(const ClassObject* clazz)
+{
+    return (clazz->descriptor[0] == '[');
+}
+
+/*
+ * Copy the entire contents of one array of objects to another.  If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ *
+ * "dstElemClass" is the type of element that "dstArray" holds.
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass);
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives.  The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+    ClassObject* dstElemClass);
+
+/*
+ * Returns the size of the given array object in bytes.
+ */
+size_t dvmArrayObjectSize(const ArrayObject *array);
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* clazz);
+
+#endif  // DALVIK_OO_ARRAY_H_
diff --git a/vm/oo/Class.cpp b/vm/oo/Class.cpp
new file mode 100644
index 0000000..904d53d
--- /dev/null
+++ b/vm/oo/Class.cpp
@@ -0,0 +1,4915 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Class loading, including bootstrap class loader, linking, and
+ * initialization.
+ */
+
+#define LOG_CLASS_LOADING 0
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+#include "libdex/ZipArchive.h"
+#include "analysis/Optimize.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+#if LOG_CLASS_LOADING
+#include <unistd.h>
+#include <pthread.h>
+#include <cutils/process_name.h>
+#include <sys/types.h>
+#endif
+
+/*
+Notes on Linking and Verification
+
+The basic way to retrieve a class is to load it, make sure its superclass
+and interfaces are available, prepare its fields, and return it.  This gets
+a little more complicated when multiple threads can be trying to retrieve
+the class simultaneously, requiring that we use the class object's monitor
+to keep things orderly.
+
+The linking (preparing, resolving) of a class can cause us to recursively
+load superclasses and interfaces.  Barring circular references (e.g. two
+classes that are superclasses of each other), this will complete without
+the loader attempting to access the partially-linked class.
+
+With verification, the situation is different.  If we try to verify
+every class as we load it, we quickly run into trouble.  Even the lowly
+java.lang.Object requires CloneNotSupportedException; follow the list
+of referenced classes and you can head down quite a trail.  The trail
+eventually leads back to Object, which is officially not fully-formed yet.
+
+The VM spec (specifically, v2 5.4.1) notes that classes pulled in during
+verification do not need to be prepared or verified.  This means that we
+are allowed to have loaded but unverified classes.  It further notes that
+the class must be verified before it is initialized, which allows us to
+defer verification for all classes until class init.  You can't execute
+code or access fields in an uninitialized class, so this is safe.
+
+It also allows a more peaceful coexistence between verified and
+unverifiable code.  If class A refers to B, and B has a method that
+refers to a bogus class C, should we allow class A to be verified?
+If A only exercises parts of B that don't use class C, then there is
+nothing wrong with running code in A.  We can fully verify both A and B,
+and allow execution to continue until B causes initialization of C.  The
+VerifyError is thrown close to the point of use.
+
+This gets a little weird with java.lang.Class, which is the only class
+that can be instantiated before it is initialized.  We have to force
+initialization right after the class is created, because by definition we
+have instances of it on the heap, and somebody might get a class object and
+start making virtual calls on it.  We can end up going recursive during
+verification of java.lang.Class, but we avoid that by checking to see if
+verification is already in progress before we try to initialize it.
+*/
+
+/*
+Notes on class loaders and interaction with optimization / verification
+
+In what follows, "pre-verification" and "optimization" are the steps
+performed by the dexopt command, which attempts to verify and optimize
+classes as part of unpacking jar files and storing the DEX data in the
+dalvik-cache directory.  These steps are performed by loading the DEX
+files directly, without any assistance from ClassLoader instances.
+
+When we pre-verify and optimize a class in a DEX file, we make some
+assumptions about where the class loader will go to look for classes.
+If we can't guarantee those assumptions, e.g. because a class ("AppClass")
+references something not defined in the bootstrap jars or the AppClass jar,
+we can't pre-verify or optimize the class.
+
+The VM doesn't define the behavior of user-defined class loaders.
+For example, suppose application class AppClass, loaded by UserLoader,
+has a method that creates a java.lang.String.  The first time
+AppClass.stringyMethod tries to do something with java.lang.String, it
+asks UserLoader to find it.  UserLoader is expected to defer to its parent
+loader, but isn't required to.  UserLoader might provide a replacement
+for String.
+
+We can run into trouble if we pre-verify AppClass with the assumption that
+java.lang.String will come from core.jar, and don't verify this assumption
+at runtime.  There are two places that an alternate implementation of
+java.lang.String can come from: the AppClass jar, or from some other jar
+that UserLoader knows about.  (Someday UserLoader will be able to generate
+some bytecode and call DefineClass, but not yet.)
+
+To handle the first situation, the pre-verifier will explicitly check for
+conflicts between the class being optimized/verified and the bootstrap
+classes.  If an app jar contains a class that has the same package and
+class name as a class in a bootstrap jar, the verification resolver refuses
+to find either, which will block pre-verification and optimization on
+classes that reference ambiguity.  The VM will postpone verification of
+the app class until first load.
+
+For the second situation, we need to ensure that all references from a
+pre-verified class are satisified by the class' jar or earlier bootstrap
+jars.  In concrete terms: when resolving a reference to NewClass,
+which was caused by a reference in class AppClass, we check to see if
+AppClass was pre-verified.  If so, we require that NewClass comes out
+of either the AppClass jar or one of the jars in the bootstrap path.
+(We may not control the class loaders, but we do manage the DEX files.
+We can verify that it's either (loader==null && dexFile==a_boot_dex)
+or (loader==UserLoader && dexFile==AppClass.dexFile).  Classes from
+DefineClass can't be pre-verified, so this doesn't apply.)
+
+This should ensure that you can't "fake out" the pre-verifier by creating
+a user-defined class loader that replaces system classes.  It should
+also ensure that you can write such a loader and have it work in the
+expected fashion; all you lose is some performance due to "just-in-time
+verification" and the lack of DEX optimizations.
+
+There is a "back door" of sorts in the class resolution check, due to
+the fact that the "class ref" entries are shared between the bytecode
+and meta-data references (e.g. annotations and exception handler lists).
+The class references in annotations have no bearing on class verification,
+so when a class does an annotation query that causes a class reference
+index to be resolved, we don't want to fail just because the calling
+class was pre-verified and the resolved class is in some random DEX file.
+The successful resolution adds the class to the "resolved classes" table,
+so when optimized bytecode references it we don't repeat the resolve-time
+check.  We can avoid this by not updating the "resolved classes" table
+when the class reference doesn't come out of something that has been
+checked by the verifier, but that has a nonzero performance impact.
+Since the ultimate goal of this test is to catch an unusual situation
+(user-defined class loaders redefining core classes), the added caution
+may not be worth the performance hit.
+*/
+
+/*
+ * Class serial numbers start at this value.  We use a nonzero initial
+ * value so they stand out in binary dumps (e.g. hprof output).
+ */
+#define INITIAL_CLASS_SERIAL_NUMBER 0x50000000
+
+/*
+ * Constant used to size an auxillary class object data structure.
+ * For optimum memory use this should be equal to or slightly larger than
+ * the number of classes loaded when the zygote finishes initializing.
+ */
+#define ZYGOTE_CLASS_CUTOFF 2304
+
+#define CLASS_SFIELD_SLOTS 1
+
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap);
+static void freeCpeArray(ClassPathEntry* cpe);
+
+static ClassObject* findClassFromLoaderNoInit(
+    const char* descriptor, Object* loader);
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,\
+    DvmDex* pDvmDex);
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+    const DexClassDef* pClassDef, Object* loader);
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,\
+    Method* meth);
+static int computeJniArgInfo(const DexProto* proto);
+static void loadSFieldFromDex(ClassObject* clazz,
+    const DexField* pDexSField, StaticField* sfield);
+static void loadIFieldFromDex(ClassObject* clazz,
+    const DexField* pDexIField, InstField* field);
+static bool precacheReferenceOffsets(ClassObject* clazz);
+static void computeRefOffsets(ClassObject* clazz);
+static void freeMethodInnards(Method* meth);
+static bool createVtable(ClassObject* clazz);
+static bool createIftable(ClassObject* clazz);
+static bool insertMethodStubs(ClassObject* clazz);
+static bool computeFieldOffsets(ClassObject* clazz);
+static void throwEarlierClassFailure(ClassObject* clazz);
+
+#if LOG_CLASS_LOADING
+/*
+ * Logs information about a class loading with given timestamp.
+ *
+ * TODO: In the case where we fail in dvmLinkClass() and log the class as closing (type='<'),
+ * it would probably be better to use a new type code to indicate the failure.  This change would
+ * require a matching change in the parser and analysis code in frameworks/base/tools/preload.
+ */
+static void logClassLoadWithTime(char type, ClassObject* clazz, u8 time) {
+    pid_t ppid = getppid();
+    pid_t pid = getpid();
+    unsigned int tid = (unsigned int) pthread_self();
+
+    ALOG(LOG_INFO, "PRELOAD", "%c%d:%d:%d:%s:%d:%s:%lld", type, ppid, pid, tid,
+        get_process_name(), (int) clazz->classLoader, clazz->descriptor,
+        time);
+}
+
+/*
+ * Logs information about a class loading.
+ */
+static void logClassLoad(char type, ClassObject* clazz) {
+    logClassLoadWithTime(type, clazz, dvmGetThreadCpuTimeNsec());
+}
+#endif
+
+/*
+ * Some LinearAlloc unit tests.
+ */
+static void linearAllocTests()
+{
+    char* fiddle;
+    int test = 1;
+
+    switch (test) {
+    case 0:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-28);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 1:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-24);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 2:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-20);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 3:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-16);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    case 4:
+        fiddle = (char*)dvmLinearAlloc(NULL, 3200-12);
+        dvmLinearReadOnly(NULL, (char*)fiddle);
+        break;
+    }
+    fiddle = (char*)dvmLinearAlloc(NULL, 896);
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 20);      // watch addr of this alloc
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+
+    fiddle = (char*)dvmLinearAlloc(NULL, 1);
+    fiddle[0] = 'q';
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 4096);
+    fiddle[0] = 'x';
+    fiddle[4095] = 'y';
+    dvmLinearReadOnly(NULL, fiddle);
+    dvmLinearFree(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 0);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 12);
+    fiddle[11] = 'z';
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+    fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 5);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = (char*)dvmLinearAlloc(NULL, 17001);
+    fiddle[0] = 'x';
+    fiddle[17000] = 'y';
+    dvmLinearReadOnly(NULL, (char*)fiddle);
+
+    char* str = (char*)dvmLinearStrdup(NULL, "This is a test!");
+    ALOGI("GOT: '%s'", str);
+
+    /* try to check the bounds; allocator may round allocation size up */
+    fiddle = (char*)dvmLinearAlloc(NULL, 12);
+    ALOGI("Should be 1: %d", dvmLinearAllocContains(fiddle, 12));
+    ALOGI("Should be 0: %d", dvmLinearAllocContains(fiddle, 13));
+    ALOGI("Should be 0: %d", dvmLinearAllocContains(fiddle - 128*1024, 1));
+
+    dvmLinearAllocDump(NULL);
+    dvmLinearFree(NULL, (char*)str);
+}
+
+static size_t classObjectSize(size_t sfieldCount)
+{
+    size_t offset = OFFSETOF_MEMBER(ClassObject, sfields);
+    return offset + sizeof(StaticField) * sfieldCount;
+}
+
+size_t dvmClassObjectSize(const ClassObject *clazz)
+{
+    assert(clazz != NULL);
+    return classObjectSize(clazz->sfieldCount);
+}
+
+/* (documented in header) */
+ClassObject* dvmFindPrimitiveClass(char type)
+{
+    PrimitiveType primitiveType = dexGetPrimitiveTypeFromDescriptorChar(type);
+
+    switch (primitiveType) {
+        case PRIM_VOID:    return gDvm.typeVoid;
+        case PRIM_BOOLEAN: return gDvm.typeBoolean;
+        case PRIM_BYTE:    return gDvm.typeByte;
+        case PRIM_SHORT:   return gDvm.typeShort;
+        case PRIM_CHAR:    return gDvm.typeChar;
+        case PRIM_INT:     return gDvm.typeInt;
+        case PRIM_LONG:    return gDvm.typeLong;
+        case PRIM_FLOAT:   return gDvm.typeFloat;
+        case PRIM_DOUBLE:  return gDvm.typeDouble;
+        default: {
+            ALOGW("Unknown primitive type '%c'", type);
+            return NULL;
+        }
+    }
+}
+
+/*
+ * Synthesize a primitive class.
+ *
+ * Just creates the class and returns it (does not add it to the class list).
+ */
+static bool createPrimitiveType(PrimitiveType primitiveType, ClassObject** pClass)
+{
+    /*
+     * Fill out a few fields in the ClassObject.
+     *
+     * Note that primitive classes do not sub-class the class Object.
+     * This matters for "instanceof" checks. Also, we assume that the
+     * primitive class does not override finalize().
+     */
+
+    const char* descriptor = dexGetPrimitiveTypeDescriptor(primitiveType);
+    assert(descriptor != NULL);
+
+    ClassObject* newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_NON_MOVING);
+    if (newClass == NULL) {
+        return false;
+    }
+
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
+    newClass->primitiveType = primitiveType;
+    newClass->descriptorAlloc = NULL;
+    newClass->descriptor = descriptor;
+    newClass->super = NULL;
+    newClass->status = CLASS_INITIALIZED;
+
+    /* don't need to set newClass->objectSize */
+
+    LOGVV("Constructed class for primitive type '%s'", newClass->descriptor);
+
+    *pClass = newClass;
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    return true;
+}
+
+/*
+ * Create the initial class instances. These consist of the class
+ * Class and all of the classes representing primitive types.
+ */
+static bool createInitialClasses() {
+    /*
+     * Initialize the class Class. This has to be done specially, particularly
+     * because it is an instance of itself.
+     */
+    ClassObject* clazz = (ClassObject*)
+        dvmMalloc(classObjectSize(CLASS_SFIELD_SLOTS), ALLOC_NON_MOVING);
+    if (clazz == NULL) {
+        return false;
+    }
+    DVM_OBJECT_INIT(clazz, clazz);
+    SET_CLASS_FLAG(clazz, ACC_PUBLIC | ACC_FINAL | CLASS_ISCLASS);
+    clazz->descriptor = "Ljava/lang/Class;";
+    gDvm.classJavaLangClass = clazz;
+    LOGVV("Constructed the class Class.");
+
+    /*
+     * Initialize the classes representing primitive types. These are
+     * instances of the class Class, but other than that they're fairly
+     * different from regular classes.
+     */
+    bool ok = true;
+    ok &= createPrimitiveType(PRIM_VOID,    &gDvm.typeVoid);
+    ok &= createPrimitiveType(PRIM_BOOLEAN, &gDvm.typeBoolean);
+    ok &= createPrimitiveType(PRIM_BYTE,    &gDvm.typeByte);
+    ok &= createPrimitiveType(PRIM_SHORT,   &gDvm.typeShort);
+    ok &= createPrimitiveType(PRIM_CHAR,    &gDvm.typeChar);
+    ok &= createPrimitiveType(PRIM_INT,     &gDvm.typeInt);
+    ok &= createPrimitiveType(PRIM_LONG,    &gDvm.typeLong);
+    ok &= createPrimitiveType(PRIM_FLOAT,   &gDvm.typeFloat);
+    ok &= createPrimitiveType(PRIM_DOUBLE,  &gDvm.typeDouble);
+
+    return ok;
+}
+
+/*
+ * Initialize the bootstrap class loader.
+ *
+ * Call this after the bootclasspath string has been finalized.
+ */
+bool dvmClassStartup()
+{
+    /* make this a requirement -- don't currently support dirs in path */
+    if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
+        ALOGE("ERROR: must specify non-'.' bootclasspath");
+        return false;
+    }
+
+    gDvm.loadedClasses =
+        dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
+
+    gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
+    if (gDvm.pBootLoaderAlloc == NULL)
+        return false;
+
+    if (false) {
+        linearAllocTests();
+        exit(0);
+    }
+
+    /*
+     * Class serial number.  We start with a high value to make it distinct
+     * in binary dumps (e.g. hprof).
+     */
+    gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
+
+    /*
+     * Set up the table we'll use for tracking initiating loaders for
+     * early classes.
+     * If it's NULL, we just fall back to the InitiatingLoaderList in the
+     * ClassObject, so it's not fatal to fail this allocation.
+     */
+    gDvm.initiatingLoaderList = (InitiatingLoaderList*)
+        calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
+
+    /*
+     * Create the initial classes. These are the first objects constructed
+     * within the nascent VM.
+     */
+    if (!createInitialClasses()) {
+        return false;
+    }
+
+    /*
+     * Process the bootstrap class path.  This means opening the specified
+     * DEX or Jar files and possibly running them through the optimizer.
+     */
+    assert(gDvm.bootClassPath == NULL);
+    processClassPath(gDvm.bootClassPathStr, true);
+
+    if (gDvm.bootClassPath == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmClassShutdown()
+{
+    /* discard all system-loaded classes */
+    dvmHashTableFree(gDvm.loadedClasses);
+    gDvm.loadedClasses = NULL;
+
+    /* discard primitive classes created for arrays */
+    dvmFreeClassInnards(gDvm.typeVoid);
+    dvmFreeClassInnards(gDvm.typeBoolean);
+    dvmFreeClassInnards(gDvm.typeByte);
+    dvmFreeClassInnards(gDvm.typeShort);
+    dvmFreeClassInnards(gDvm.typeChar);
+    dvmFreeClassInnards(gDvm.typeInt);
+    dvmFreeClassInnards(gDvm.typeLong);
+    dvmFreeClassInnards(gDvm.typeFloat);
+    dvmFreeClassInnards(gDvm.typeDouble);
+
+    /* this closes DEX files, JAR files, etc. */
+    freeCpeArray(gDvm.bootClassPath);
+    gDvm.bootClassPath = NULL;
+
+    dvmLinearAllocDestroy(NULL);
+
+    free(gDvm.initiatingLoaderList);
+}
+
+
+/*
+ * ===========================================================================
+ *      Bootstrap class loader
+ * ===========================================================================
+ */
+
+/*
+ * Dump the contents of a ClassPathEntry array.
+ */
+static void dumpClassPath(const ClassPathEntry* cpe)
+{
+    int idx = 0;
+
+    while (cpe->kind != kCpeLastEntry) {
+        const char* kindStr;
+
+        switch (cpe->kind) {
+        case kCpeJar:       kindStr = "jar";    break;
+        case kCpeDex:       kindStr = "dex";    break;
+        default:            kindStr = "???";    break;
+        }
+
+        ALOGI("  %2d: type=%s %s %p", idx, kindStr, cpe->fileName, cpe->ptr);
+        if (CALC_CACHE_STATS && cpe->kind == kCpeJar) {
+            JarFile* pJarFile = (JarFile*) cpe->ptr;
+            DvmDex* pDvmDex = dvmGetJarFileDex(pJarFile);
+            dvmDumpAtomicCacheStats(pDvmDex->pInterfaceCache);
+        }
+
+        cpe++;
+        idx++;
+    }
+}
+
+/*
+ * Dump the contents of the bootstrap class path.
+ */
+void dvmDumpBootClassPath()
+{
+    dumpClassPath(gDvm.bootClassPath);
+}
+
+/*
+ * Returns "true" if the class path contains the specified path.
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path)
+{
+    while (cpe->kind != kCpeLastEntry) {
+        if (strcmp(cpe->fileName, path) == 0)
+            return true;
+
+        cpe++;
+    }
+    return false;
+}
+
+/*
+ * Free an array of ClassPathEntry structs.
+ *
+ * We release the contents of each entry, then free the array itself.
+ */
+static void freeCpeArray(ClassPathEntry* cpe)
+{
+    ClassPathEntry* cpeStart = cpe;
+
+    if (cpe == NULL)
+        return;
+
+    while (cpe->kind != kCpeLastEntry) {
+        switch (cpe->kind) {
+        case kCpeJar:
+            /* free JarFile */
+            dvmJarFileFree((JarFile*) cpe->ptr);
+            break;
+        case kCpeDex:
+            /* free RawDexFile */
+            dvmRawDexFileFree((RawDexFile*) cpe->ptr);
+            break;
+        default:
+            assert(false);
+            break;
+        }
+
+        free(cpe->fileName);
+        cpe++;
+    }
+
+    free(cpeStart);
+}
+
+/*
+ * Get the filename suffix of the given file (everything after the
+ * last "." if any, or "<none>" if there's no apparent suffix). The
+ * passed-in buffer will always be '\0' terminated.
+ */
+static void getFileNameSuffix(const char* fileName, char* suffixBuf, size_t suffixBufLen)
+{
+    const char* lastDot = strrchr(fileName, '.');
+
+    strlcpy(suffixBuf, (lastDot == NULL) ? "<none>" : (lastDot + 1), suffixBufLen);
+}
+
+/*
+ * Prepare a ClassPathEntry struct, which at this point only has a valid
+ * filename.  We need to figure out what kind of file it is, and for
+ * everything other than directories we need to open it up and see
+ * what's inside.
+ */
+static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap)
+{
+    struct stat sb;
+
+    if (stat(cpe->fileName, &sb) < 0) {
+        ALOGD("Unable to stat classpath element '%s'", cpe->fileName);
+        return false;
+    }
+    if (S_ISDIR(sb.st_mode)) {
+        ALOGE("Directory classpath elements are not supported: %s", cpe->fileName);
+        return false;
+    }
+
+    char suffix[10];
+    getFileNameSuffix(cpe->fileName, suffix, sizeof(suffix));
+
+    if ((strcmp(suffix, "jar") == 0) || (strcmp(suffix, "zip") == 0) ||
+            (strcmp(suffix, "apk") == 0)) {
+        JarFile* pJarFile = NULL;
+        if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
+            cpe->kind = kCpeJar;
+            cpe->ptr = pJarFile;
+            return true;
+        }
+    } else if (strcmp(suffix, "dex") == 0) {
+        RawDexFile* pRawDexFile = NULL;
+        if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0) {
+            cpe->kind = kCpeDex;
+            cpe->ptr = pRawDexFile;
+            return true;
+        }
+    } else {
+        ALOGE("Unknown type suffix '%s'", suffix);
+    }
+
+    ALOGD("Unable to process classpath element '%s'", cpe->fileName);
+    return false;
+}
+
+/*
+ * Convert a colon-separated list of directories, Zip files, and DEX files
+ * into an array of ClassPathEntry structs.
+ *
+ * During normal startup we fail if there are no entries, because we won't
+ * get very far without the basic language support classes, but if we're
+ * optimizing a DEX file we allow it.
+ *
+ * If entries are added or removed from the bootstrap class path, the
+ * dependencies in the DEX files will break, and everything except the
+ * very first entry will need to be regenerated.
+ */
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap)
+{
+    ClassPathEntry* cpe = NULL;
+    char* mangle;
+    char* cp;
+    const char* end;
+    int idx, count;
+
+    assert(pathStr != NULL);
+
+    mangle = strdup(pathStr);
+
+    /*
+     * Run through and essentially strtok() the string.  Get a count of
+     * the #of elements while we're at it.
+     *
+     * If the path was constructed strangely (e.g. ":foo::bar:") this will
+     * over-allocate, which isn't ideal but is mostly harmless.
+     */
+    count = 1;
+    for (cp = mangle; *cp != '\0'; cp++) {
+        if (*cp == ':') {   /* separates two entries */
+            count++;
+            *cp = '\0';
+        }
+    }
+    end = cp;
+
+    /*
+     * Allocate storage.  We over-alloc by one so we can set an "end" marker.
+     */
+    cpe = (ClassPathEntry*) calloc(count+1, sizeof(ClassPathEntry));
+
+    /*
+     * Set the global pointer so the DEX file dependency stuff can find it.
+     */
+    gDvm.bootClassPath = cpe;
+
+    /*
+     * Go through a second time, pulling stuff out.
+     */
+    cp = mangle;
+    idx = 0;
+    while (cp < end) {
+        if (*cp == '\0') {
+            /* leading, trailing, or doubled ':'; ignore it */
+        } else {
+            if (isBootstrap &&
+                    dvmPathToAbsolutePortion(cp) == NULL) {
+                ALOGE("Non-absolute bootclasspath entry '%s'", cp);
+                free(cpe);
+                cpe = NULL;
+                goto bail;
+            }
+
+            ClassPathEntry tmp;
+            tmp.kind = kCpeUnknown;
+            tmp.fileName = strdup(cp);
+            tmp.ptr = NULL;
+
+            /*
+             * Drop an end marker here so DEX loader can walk unfinished
+             * list.
+             */
+            cpe[idx].kind = kCpeLastEntry;
+            cpe[idx].fileName = NULL;
+            cpe[idx].ptr = NULL;
+
+            if (!prepareCpe(&tmp, isBootstrap)) {
+                /* drop from list and continue on */
+                free(tmp.fileName);
+            } else {
+                /* copy over, pointers and all */
+                cpe[idx] = tmp;
+                idx++;
+            }
+        }
+
+        cp += strlen(cp) +1;
+    }
+    assert(idx <= count);
+    if (idx == 0 && !gDvm.optimizing) {
+        /*
+         * There's no way the vm will be doing anything if this is the
+         * case, so just bail out (reasonably) gracefully.
+         */
+        ALOGE("No valid entries found in bootclasspath '%s'", pathStr);
+        gDvm.lastMessage = pathStr;
+        dvmAbort();
+    }
+
+    LOGVV("  (filled %d of %d slots)", idx, count);
+
+    /* put end marker in over-alloc slot */
+    cpe[idx].kind = kCpeLastEntry;
+    cpe[idx].fileName = NULL;
+    cpe[idx].ptr = NULL;
+
+    //dumpClassPath(cpe);
+
+bail:
+    free(mangle);
+    gDvm.bootClassPath = cpe;
+    return cpe;
+}
+
+/*
+ * Search the DEX files we loaded from the bootstrap class path for a DEX
+ * file that has the class with the matching descriptor.
+ *
+ * Returns the matching DEX file and DexClassDef entry if found, otherwise
+ * returns NULL.
+ */
+static DvmDex* searchBootPathForClass(const char* descriptor,
+    const DexClassDef** ppClassDef)
+{
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+    const DexClassDef* pFoundDef = NULL;
+    DvmDex* pFoundFile = NULL;
+
+    LOGVV("+++ class '%s' not yet loaded, scanning bootclasspath...",
+        descriptor);
+
+    while (cpe->kind != kCpeLastEntry) {
+        //ALOGV("+++  checking '%s' (%d)", cpe->fileName, cpe->kind);
+
+        switch (cpe->kind) {
+        case kCpeJar:
+            {
+                JarFile* pJarFile = (JarFile*) cpe->ptr;
+                const DexClassDef* pClassDef;
+                DvmDex* pDvmDex;
+
+                pDvmDex = dvmGetJarFileDex(pJarFile);
+                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+                if (pClassDef != NULL) {
+                    /* found */
+                    pFoundDef = pClassDef;
+                    pFoundFile = pDvmDex;
+                    goto found;
+                }
+            }
+            break;
+        case kCpeDex:
+            {
+                RawDexFile* pRawDexFile = (RawDexFile*) cpe->ptr;
+                const DexClassDef* pClassDef;
+                DvmDex* pDvmDex;
+
+                pDvmDex = dvmGetRawDexFileDex(pRawDexFile);
+                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+                if (pClassDef != NULL) {
+                    /* found */
+                    pFoundDef = pClassDef;
+                    pFoundFile = pDvmDex;
+                    goto found;
+                }
+            }
+            break;
+        default:
+            ALOGE("Unknown kind %d", cpe->kind);
+            assert(false);
+            break;
+        }
+
+        cpe++;
+    }
+
+    /*
+     * Special handling during verification + optimization.
+     *
+     * The DEX optimizer needs to load classes from the DEX file it's working
+     * on.  Rather than trying to insert it into the bootstrap class path
+     * or synthesizing a class loader to manage it, we just make it available
+     * here.  It logically comes after all existing entries in the bootstrap
+     * class path.
+     */
+    if (gDvm.bootClassPathOptExtra != NULL) {
+        const DexClassDef* pClassDef;
+
+        pClassDef =
+            dexFindClass(gDvm.bootClassPathOptExtra->pDexFile, descriptor);
+        if (pClassDef != NULL) {
+            /* found */
+            pFoundDef = pClassDef;
+            pFoundFile = gDvm.bootClassPathOptExtra;
+        }
+    }
+
+found:
+    *ppClassDef = pFoundDef;
+    return pFoundFile;
+}
+
+/*
+ * Set the "extra" DEX, which becomes a de facto member of the bootstrap
+ * class set.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex)
+{
+    gDvm.bootClassPathOptExtra = pDvmDex;
+}
+
+
+/*
+ * Return the #of entries in the bootstrap class path.
+ *
+ * (Used for ClassLoader.getResources().)
+ */
+int dvmGetBootPathSize()
+{
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+
+    while (cpe->kind != kCpeLastEntry)
+        cpe++;
+
+    return cpe - gDvm.bootClassPath;
+}
+
+/*
+ * Find a resource with the specified name in entry N of the boot class path.
+ *
+ * We return a newly-allocated String of one of these forms:
+ *   file://path/name
+ *   jar:file://path!/name
+ * Where "path" is the bootstrap class path entry and "name" is the string
+ * passed into this method.  "path" needs to be an absolute path (starting
+ * with '/'); if it's not we'd need to "absolutify" it as part of forming
+ * the URL string.
+ */
+StringObject* dvmGetBootPathResource(const char* name, int idx)
+{
+    const int kUrlOverhead = 13;        // worst case for Jar URL
+    const ClassPathEntry* cpe = gDvm.bootClassPath;
+    StringObject* urlObj = NULL;
+    ZipEntry entry;
+
+    ALOGV("+++ searching for resource '%s' in %d(%s)",
+        name, idx, cpe[idx].fileName);
+
+    /* we could use direct array index, but I don't entirely trust "idx" */
+    while (idx-- && cpe->kind != kCpeLastEntry)
+        cpe++;
+    if (cpe->kind == kCpeLastEntry) {
+        assert(false);
+        return NULL;
+    }
+
+    char urlBuf[strlen(name) + strlen(cpe->fileName) + kUrlOverhead +1];
+
+    switch (cpe->kind) {
+    case kCpeJar:
+        {
+            JarFile* pJarFile = (JarFile*) cpe->ptr;
+            if (dexZipFindEntry(pJarFile->archive, name, &entry) != 0)
+                goto bail;
+            sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name);
+        }
+        break;
+    case kCpeDex:
+        ALOGV("No resources in DEX files");
+        goto bail;
+    default:
+        assert(false);
+        goto bail;
+    }
+
+    ALOGV("+++ using URL='%s'", urlBuf);
+    urlObj = dvmCreateStringFromCstr(urlBuf);
+
+bail:
+    return urlObj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Class list management
+ * ===========================================================================
+ */
+
+/* search for these criteria in the Class hash table */
+struct ClassMatchCriteria {
+    const char* descriptor;
+    Object*     loader;
+};
+
+#define kInitLoaderInc  4       /* must be power of 2 */
+
+static InitiatingLoaderList *dvmGetInitiatingLoaderList(ClassObject* clazz)
+{
+    assert(clazz->serialNumber >= INITIAL_CLASS_SERIAL_NUMBER);
+    int classIndex = clazz->serialNumber-INITIAL_CLASS_SERIAL_NUMBER;
+    if (gDvm.initiatingLoaderList != NULL &&
+        classIndex < ZYGOTE_CLASS_CUTOFF) {
+        return &(gDvm.initiatingLoaderList[classIndex]);
+    } else {
+        return &(clazz->initiatingLoaderList);
+    }
+}
+
+/*
+ * Determine if "loader" appears in clazz' initiating loader list.
+ *
+ * The class hash table lock must be held when calling here, since
+ * it's also used when updating a class' initiating loader list.
+ *
+ * TODO: switch to some sort of lock-free data structure so we don't have
+ * to grab the lock to do a lookup.  Among other things, this would improve
+ * the speed of compareDescriptorClasses().
+ */
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader)
+{
+    /*
+     * The bootstrap class loader can't be just an initiating loader for
+     * anything (it's always the defining loader if the class is visible
+     * to it).  We don't put defining loaders in the initiating list.
+     */
+    if (loader == NULL)
+        return false;
+
+    /*
+     * Scan the list for a match.  The list is expected to be short.
+     */
+    /* Cast to remove the const from clazz, but use const loaderList */
+    ClassObject* nonConstClazz = (ClassObject*) clazz;
+    const InitiatingLoaderList *loaderList =
+        dvmGetInitiatingLoaderList(nonConstClazz);
+    int i;
+    for (i = loaderList->initiatingLoaderCount-1; i >= 0; --i) {
+        if (loaderList->initiatingLoaders[i] == loader) {
+            //ALOGI("+++ found initiating match %p in %s",
+            //    loader, clazz->descriptor);
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Add "loader" to clazz's initiating loader set, unless it's the defining
+ * class loader.
+ *
+ * In the common case this will be a short list, so we don't need to do
+ * anything too fancy here.
+ *
+ * This locks gDvm.loadedClasses for synchronization, so don't hold it
+ * when calling here.
+ */
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader)
+{
+    if (loader != clazz->classLoader) {
+        assert(loader != NULL);
+
+        LOGVV("Adding %p to '%s' init list", loader, clazz->descriptor);
+        dvmHashTableLock(gDvm.loadedClasses);
+
+        /*
+         * Make sure nobody snuck in.  The penalty for adding twice is
+         * pretty minor, and probably outweighs the O(n^2) hit for
+         * checking before every add, so we may not want to do this.
+         */
+        //if (dvmLoaderInInitiatingList(clazz, loader)) {
+        //    ALOGW("WOW: simultaneous add of initiating class loader");
+        //    goto bail_unlock;
+        //}
+
+        /*
+         * The list never shrinks, so we just keep a count of the
+         * number of elements in it, and reallocate the buffer when
+         * we run off the end.
+         *
+         * The pointer is initially NULL, so we *do* want to call realloc
+         * when count==0.
+         */
+        InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+        if ((loaderList->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
+            Object** newList;
+
+            newList = (Object**) realloc(loaderList->initiatingLoaders,
+                        (loaderList->initiatingLoaderCount + kInitLoaderInc)
+                         * sizeof(Object*));
+            if (newList == NULL) {
+                /* this is mainly a cache, so it's not the EotW */
+                assert(false);
+                goto bail_unlock;
+            }
+            loaderList->initiatingLoaders = newList;
+
+            //ALOGI("Expanded init list to %d (%s)",
+            //    loaderList->initiatingLoaderCount+kInitLoaderInc,
+            //    clazz->descriptor);
+        }
+        loaderList->initiatingLoaders[loaderList->initiatingLoaderCount++] =
+            loader;
+
+bail_unlock:
+        dvmHashTableUnlock(gDvm.loadedClasses);
+    }
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Entries in the class hash table are stored as { descriptor, d-loader }
+ * tuples.  If the hashed class descriptor matches the requested descriptor,
+ * and the hashed defining class loader matches the requested class
+ * loader, we're good.  If only the descriptor matches, we check to see if the
+ * loader is in the hashed class' initiating loader list.  If so, we
+ * can return "true" immediately and skip some of the loadClass melodrama.
+ *
+ * The caller must lock the hash table before calling here.
+ *
+ * Returns 0 if a matching entry is found, nonzero otherwise.
+ */
+static int hashcmpClassByCrit(const void* vclazz, const void* vcrit)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassMatchCriteria* pCrit = (const ClassMatchCriteria*) vcrit;
+    bool match;
+
+    match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&
+             (clazz->classLoader == pCrit->loader ||
+              (pCrit->loader != NULL &&
+               dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));
+    //if (match)
+    //    ALOGI("+++ %s %p matches existing %s %p",
+    //        pCrit->descriptor, pCrit->loader,
+    //        clazz->descriptor, clazz->classLoader);
+    return !match;
+}
+
+/*
+ * Like hashcmpClassByCrit, but passing in a fully-formed ClassObject
+ * instead of a ClassMatchCriteria.
+ */
+static int hashcmpClassByClass(const void* vclazz, const void* vaddclazz)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassObject* addClazz = (const ClassObject*) vaddclazz;
+    bool match;
+
+    match = (strcmp(clazz->descriptor, addClazz->descriptor) == 0 &&
+             (clazz->classLoader == addClazz->classLoader ||
+              (addClazz->classLoader != NULL &&
+               dvmLoaderInInitiatingList(clazz, addClazz->classLoader)) ));
+    return !match;
+}
+
+/*
+ * Search through the hash table to find an entry with a matching descriptor
+ * and an initiating class loader that matches "loader".
+ *
+ * The table entries are hashed on descriptor only, because they're unique
+ * on *defining* class loader, not *initiating* class loader.  This isn't
+ * great, because it guarantees we will have to probe when multiple
+ * class loaders are used.
+ *
+ * Note this does NOT try to load a class; it just finds a class that
+ * has already been loaded.
+ *
+ * If "unprepOkay" is set, this will return classes that have been added
+ * to the hash table but are not yet fully loaded and linked.  Otherwise,
+ * such classes are ignored.  (The only place that should set "unprepOkay"
+ * is findClassNoInit(), which will wait for the prep to finish.)
+ *
+ * Returns NULL if not found.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+    bool unprepOkay)
+{
+    ClassMatchCriteria crit;
+    void* found;
+    u4 hash;
+
+    crit.descriptor = descriptor;
+    crit.loader = loader;
+    hash = dvmComputeUtf8Hash(descriptor);
+
+    LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p",
+        dvmThreadSelf()->threadId, descriptor, loader);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
+                hashcmpClassByCrit, false);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    /*
+     * The class has been added to the hash table but isn't ready for use.
+     * We're going to act like we didn't see it, so that the caller will
+     * go through the full "find class" path, which includes locking the
+     * object and waiting until it's ready.  We could do that lock/wait
+     * here, but this is an extremely rare case, and it's simpler to have
+     * the wait-for-class code centralized.
+     */
+    if (found && !unprepOkay && !dvmIsClassLinked((ClassObject*)found)) {
+        ALOGV("Ignoring not-yet-ready %s, using slow path",
+            ((ClassObject*)found)->descriptor);
+        found = NULL;
+    }
+
+    return (ClassObject*) found;
+}
+
+/*
+ * Add a new class to the hash table.
+ *
+ * The class is considered "new" if it doesn't match on both the class
+ * descriptor and the defining class loader.
+ *
+ * TODO: we should probably have separate hash tables for each
+ * ClassLoader. This could speed up dvmLookupClass and
+ * other common operations. It does imply a VM-visible data structure
+ * for each ClassLoader object with loaded classes, which we don't
+ * have yet.
+ */
+bool dvmAddClassToHash(ClassObject* clazz)
+{
+    void* found;
+    u4 hash;
+
+    hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz,
+                hashcmpClassByClass, true);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    ALOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p",
+        clazz->descriptor, clazz->classLoader,
+        (found == (void*) clazz), clazz);
+
+    //dvmCheckClassTablePerf();
+
+    /* can happen if two threads load the same class simultaneously */
+    return (found == (void*) clazz);
+}
+
+#if 0
+/*
+ * Compute hash value for a class.
+ */
+u4 hashcalcClass(const void* item)
+{
+    return dvmComputeUtf8Hash(((const ClassObject*) item)->descriptor);
+}
+
+/*
+ * Check the performance of the "loadedClasses" hash table.
+ */
+void dvmCheckClassTablePerf()
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashTableProbeCount(gDvm.loadedClasses, hashcalcClass,
+        hashcmpClassByClass);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+/*
+ * Remove a class object from the hash table.
+ */
+static void removeClassFromHash(ClassObject* clazz)
+{
+    ALOGV("+++ removeClassFromHash '%s'", clazz->descriptor);
+
+    u4 hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    if (!dvmHashTableRemove(gDvm.loadedClasses, hash, clazz))
+        ALOGW("Hash table remove failed on class '%s'", clazz->descriptor);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+
+/*
+ * ===========================================================================
+ *      Class creation
+ * ===========================================================================
+ */
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ *
+ * This usually happens *very* early in class creation, so don't expect
+ * anything else in the class to be ready.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz)
+{
+    assert(clazz->serialNumber == 0);
+    clazz->serialNumber = android_atomic_inc(&gDvm.classSerialNumber);
+}
+
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded and initialized if it has not already been.
+ * If necessary, the superclass will be loaded.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindClassNoInit(descriptor, loader);
+    if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+        /* initialize class */
+        if (!dvmInitClass(clazz)) {
+            /* init failed; leave it in the list, marked as bad */
+            assert(dvmCheckException(dvmThreadSelf()));
+            assert(clazz->status == CLASS_ERROR);
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded if it has not already been, as will its
+ * superclass.  It will not be initialized.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClassNoInit(const char* descriptor,
+        Object* loader)
+{
+    assert(descriptor != NULL);
+    //assert(loader != NULL);
+
+    LOGVV("FindClassNoInit '%s' %p", descriptor, loader);
+
+    if (*descriptor == '[') {
+        /*
+         * Array class.  Find in table, generate if not found.
+         */
+        return dvmFindArrayClass(descriptor, loader);
+    } else {
+        /*
+         * Regular class.  Find in table, load if not found.
+         */
+        if (loader != NULL) {
+            return findClassFromLoaderNoInit(descriptor, loader);
+        } else {
+            return dvmFindSystemClassNoInit(descriptor);
+        }
+    }
+}
+
+/*
+ * Load the named class (by descriptor) from the specified class
+ * loader.  This calls out to let the ClassLoader object do its thing.
+ *
+ * Returns with NULL and an exception raised on error.
+ */
+static ClassObject* findClassFromLoaderNoInit(const char* descriptor,
+    Object* loader)
+{
+    //ALOGI("##### findClassFromLoaderNoInit (%s,%p)",
+    //        descriptor, loader);
+
+    Thread* self = dvmThreadSelf();
+
+    assert(loader != NULL);
+
+    /*
+     * Do we already have it?
+     *
+     * The class loader code does the "is it already loaded" check as
+     * well.  However, this call is much faster than calling through
+     * interpreted code.  Doing this does mean that in the common case
+     * (365 out of 420 calls booting the sim) we're doing the
+     * lookup-by-descriptor twice.  It appears this is still a win, so
+     * I'm keeping it in.
+     */
+    ClassObject* clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz != NULL) {
+        LOGVV("Already loaded: %s %p", descriptor, loader);
+        return clazz;
+    } else {
+        LOGVV("Not already loaded: %s %p", descriptor, loader);
+    }
+
+    char* dotName = NULL;
+    StringObject* nameObj = NULL;
+
+    /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
+    dotName = dvmDescriptorToDot(descriptor);
+    if (dotName == NULL) {
+        dvmThrowOutOfMemoryError(NULL);
+        return NULL;
+    }
+    nameObj = dvmCreateStringFromCstr(dotName);
+    if (nameObj == NULL) {
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+
+    dvmMethodTraceClassPrepBegin();
+
+    /*
+     * Invoke loadClass().  This will probably result in a couple of
+     * exceptions being thrown, because the ClassLoader.loadClass()
+     * implementation eventually calls VMClassLoader.loadClass to see if
+     * the bootstrap class loader can find it before doing its own load.
+     */
+    LOGVV("--- Invoking loadClass(%s, %p)", dotName, loader);
+    {
+        const Method* loadClass =
+            loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];
+        JValue result;
+        dvmCallMethod(self, loadClass, loader, &result, nameObj);
+        clazz = (ClassObject*) result.l;
+
+        dvmMethodTraceClassPrepEnd();
+        Object* excep = dvmGetException(self);
+        if (excep != NULL) {
+#if DVM_SHOW_EXCEPTION >= 2
+            ALOGD("NOTE: loadClass '%s' %p threw exception %s",
+                 dotName, loader, excep->clazz->descriptor);
+#endif
+            dvmAddTrackedAlloc(excep, self);
+            dvmClearException(self);
+            dvmThrowChainedNoClassDefFoundError(descriptor, excep);
+            dvmReleaseTrackedAlloc(excep, self);
+            clazz = NULL;
+            goto bail;
+        } else if (clazz == NULL) {
+            ALOGW("ClassLoader returned NULL w/o exception pending");
+            dvmThrowNullPointerException("ClassLoader returned null");
+            goto bail;
+        }
+    }
+
+    /* not adding clazz to tracked-alloc list, because it's a ClassObject */
+
+    dvmAddInitiatingLoader(clazz, loader);
+
+    LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)",
+        descriptor, clazz->classLoader, loader, clazz);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*)nameObj, NULL);
+    free(dotName);
+    return clazz;
+}
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+    Object* classLoader)
+{
+    assert(pDvmDex != NULL);
+
+    return findClassNoInit(descriptor, classLoader, pDvmDex);
+}
+
+
+/*
+ * Find the named class (by descriptor), scanning through the
+ * bootclasspath if it hasn't already been loaded.
+ *
+ * "descriptor" looks like "Landroid/debug/Stuff;".
+ *
+ * Uses NULL as the defining class loader.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor)
+{
+    ClassObject* clazz;
+
+    clazz = dvmFindSystemClassNoInit(descriptor);
+    if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+        /* initialize class */
+        if (!dvmInitClass(clazz)) {
+            /* init failed; leave it in the list, marked as bad */
+            assert(dvmCheckException(dvmThreadSelf()));
+            assert(clazz->status == CLASS_ERROR);
+            return NULL;
+        }
+    }
+
+    return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), searching for it in the
+ * bootclasspath.
+ *
+ * On failure, this returns NULL with an exception raised.
+ */
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
+{
+    return findClassNoInit(descriptor, NULL, NULL);
+}
+
+/*
+ * Find the named class (by descriptor). If it's not already loaded,
+ * we load it and link it, but don't execute <clinit>. (The VM has
+ * specific limitations on which events can cause initialization.)
+ *
+ * If "pDexFile" is NULL, we will search the bootclasspath for an entry.
+ *
+ * On failure, this returns NULL with an exception raised.
+ *
+ * TODO: we need to return an indication of whether we loaded the class or
+ * used an existing definition.  If somebody deliberately tries to load a
+ * class twice in the same class loader, they should get a LinkageError,
+ * but inadvertent simultaneous class references should "just work".
+ */
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
+    DvmDex* pDvmDex)
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clazz;
+    bool profilerNotified = false;
+
+    if (loader != NULL) {
+        LOGVV("#### findClassNoInit(%s,%p,%p)", descriptor, loader,
+            pDvmDex->pDexFile);
+    }
+
+    /*
+     * We don't expect an exception to be raised at this point.  The
+     * exception handling code is good about managing this.  This *can*
+     * happen if a JNI lookup fails and the JNI code doesn't do any
+     * error checking before doing another class lookup, so we may just
+     * want to clear this and restore it on exit.  If we don't, some kinds
+     * of failures can't be detected without rearranging other stuff.
+     *
+     * Most often when we hit this situation it means that something is
+     * broken in the VM or in JNI code, so I'm keeping it in place (and
+     * making it an informative abort rather than an assert).
+     */
+    if (dvmCheckException(self)) {
+        ALOGE("Class lookup %s attempted with exception pending", descriptor);
+        ALOGW("Pending exception is:");
+        dvmLogExceptionStackTrace();
+        dvmDumpAllThreads(false);
+        dvmAbort();
+    }
+
+    clazz = dvmLookupClass(descriptor, loader, true);
+    if (clazz == NULL) {
+        const DexClassDef* pClassDef;
+
+        dvmMethodTraceClassPrepBegin();
+        profilerNotified = true;
+
+#if LOG_CLASS_LOADING
+        u8 startTime = dvmGetThreadCpuTimeNsec();
+#endif
+
+        if (pDvmDex == NULL) {
+            assert(loader == NULL);     /* shouldn't be here otherwise */
+            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
+        } else {
+            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+        }
+
+        if (pDvmDex == NULL || pClassDef == NULL) {
+            if (gDvm.noClassDefFoundErrorObj != NULL) {
+                /* usual case -- use prefabricated object */
+                dvmSetException(self, gDvm.noClassDefFoundErrorObj);
+            } else {
+                /* dexopt case -- can't guarantee prefab (core.jar) */
+                dvmThrowNoClassDefFoundError(descriptor);
+            }
+            goto bail;
+        }
+
+        /* found a match, try to load it */
+        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
+        if (dvmCheckException(self)) {
+            /* class was found but had issues */
+            if (clazz != NULL) {
+                dvmFreeClassInnards(clazz);
+                dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+            }
+            goto bail;
+        }
+
+        /*
+         * Lock the class while we link it so other threads must wait for us
+         * to finish.  Set the "initThreadId" so we can identify recursive
+         * invocation.  (Note all accesses to initThreadId here are
+         * guarded by the class object's lock.)
+         */
+        dvmLockObject(self, (Object*) clazz);
+        clazz->initThreadId = self->threadId;
+
+        /*
+         * Add to hash table so lookups succeed.
+         *
+         * [Are circular references possible when linking a class?]
+         */
+        assert(clazz->classLoader == loader);
+        if (!dvmAddClassToHash(clazz)) {
+            /*
+             * Another thread must have loaded the class after we
+             * started but before we finished.  Discard what we've
+             * done and leave some hints for the GC.
+             *
+             * (Yes, this happens.)
+             */
+            //ALOGW("WOW: somebody loaded %s simultaneously", descriptor);
+            clazz->initThreadId = 0;
+            dvmUnlockObject(self, (Object*) clazz);
+
+            /* Let the GC free the class.
+             */
+            dvmFreeClassInnards(clazz);
+            dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+            /* Grab the winning class.
+             */
+            clazz = dvmLookupClass(descriptor, loader, true);
+            assert(clazz != NULL);
+            goto got_class;
+        }
+        dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+#if LOG_CLASS_LOADING
+        logClassLoadWithTime('>', clazz, startTime);
+#endif
+        /*
+         * Prepare and resolve.
+         */
+        if (!dvmLinkClass(clazz)) {
+            assert(dvmCheckException(self));
+
+            /* Make note of the error and clean up the class.
+             */
+            removeClassFromHash(clazz);
+            clazz->status = CLASS_ERROR;
+            dvmFreeClassInnards(clazz);
+
+            /* Let any waiters know.
+             */
+            clazz->initThreadId = 0;
+            dvmObjectNotifyAll(self, (Object*) clazz);
+            dvmUnlockObject(self, (Object*) clazz);
+
+#if LOG_CLASS_LOADING
+            ALOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s",
+                clazz->descriptor, get_process_name());
+
+            /*
+             * TODO: It would probably be better to use a new type code here (instead of '<') to
+             * indicate the failure.  This change would require a matching change in the parser
+             * and analysis code in frameworks/base/tools/preload.
+             */
+            logClassLoad('<', clazz);
+#endif
+            clazz = NULL;
+            if (gDvm.optimizing) {
+                /* happens with "external" libs */
+                ALOGV("Link of class '%s' failed", descriptor);
+            } else {
+                ALOGW("Link of class '%s' failed", descriptor);
+            }
+            goto bail;
+        }
+        dvmObjectNotifyAll(self, (Object*) clazz);
+        dvmUnlockObject(self, (Object*) clazz);
+
+        /*
+         * Add class stats to global counters.
+         *
+         * TODO: these should probably be atomic ops.
+         */
+        gDvm.numLoadedClasses++;
+        gDvm.numDeclaredMethods +=
+            clazz->virtualMethodCount + clazz->directMethodCount;
+        gDvm.numDeclaredInstFields += clazz->ifieldCount;
+        gDvm.numDeclaredStaticFields += clazz->sfieldCount;
+
+        /*
+         * Cache pointers to basic classes.  We want to use these in
+         * various places, and it's easiest to initialize them on first
+         * use rather than trying to force them to initialize (startup
+         * ordering makes it weird).
+         */
+        if (gDvm.classJavaLangObject == NULL &&
+            strcmp(descriptor, "Ljava/lang/Object;") == 0)
+        {
+            /* It should be impossible to get here with anything
+             * but the bootclasspath loader.
+             */
+            assert(loader == NULL);
+            gDvm.classJavaLangObject = clazz;
+        }
+
+#if LOG_CLASS_LOADING
+        logClassLoad('<', clazz);
+#endif
+
+    } else {
+got_class:
+        if (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+            /*
+             * We can race with other threads for class linking.  We should
+             * never get here recursively; doing so indicates that two
+             * classes have circular dependencies.
+             *
+             * One exception: we force discovery of java.lang.Class in
+             * dvmLinkClass(), and Class has Object as its superclass.  So
+             * if the first thing we ever load is Object, we will init
+             * Object->Class->Object.  The easiest way to avoid this is to
+             * ensure that Object is never the first thing we look up, so
+             * we get Foo->Class->Object instead.
+             */
+            dvmLockObject(self, (Object*) clazz);
+            if (!dvmIsClassLinked(clazz) &&
+                clazz->initThreadId == self->threadId)
+            {
+                ALOGW("Recursive link on class %s", clazz->descriptor);
+                dvmUnlockObject(self, (Object*) clazz);
+                dvmThrowClassCircularityError(clazz->descriptor);
+                clazz = NULL;
+                goto bail;
+            }
+            //ALOGI("WAITING  for '%s' (owner=%d)",
+            //    clazz->descriptor, clazz->initThreadId);
+            while (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+                dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+            }
+            dvmUnlockObject(self, (Object*) clazz);
+        }
+        if (clazz->status == CLASS_ERROR) {
+            /*
+             * Somebody else tried to load this and failed.  We need to raise
+             * an exception and report failure.
+             */
+            throwEarlierClassFailure(clazz);
+            clazz = NULL;
+            goto bail;
+        }
+    }
+
+    /* check some invariants */
+    assert(dvmIsClassLinked(clazz));
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+    assert(clazz == gDvm.classJavaLangObject || clazz->super != NULL);
+    if (!dvmIsInterfaceClass(clazz)) {
+        //ALOGI("class=%s vtableCount=%d, virtualMeth=%d",
+        //    clazz->descriptor, clazz->vtableCount,
+        //    clazz->virtualMethodCount);
+        assert(clazz->vtableCount >= clazz->virtualMethodCount);
+    }
+
+bail:
+    if (profilerNotified)
+        dvmMethodTraceClassPrepEnd();
+    assert(clazz != NULL || dvmCheckException(self));
+    return clazz;
+}
+
+/*
+ * Helper for loadClassFromDex, which takes a DexClassDataHeader and
+ * encoded data pointer in addition to the other arguments.
+ */
+static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
+    const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
+    const u1* pEncodedData, Object* classLoader)
+{
+    ClassObject* newClass = NULL;
+    const DexFile* pDexFile;
+    const char* descriptor;
+    int i;
+
+    pDexFile = pDvmDex->pDexFile;
+    descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
+
+    /*
+     * Make sure the aren't any "bonus" flags set, since we use them for
+     * runtime state.
+     */
+    /* bits we can reasonably expect to see set in a DEX access flags field */
+    const uint32_t EXPECTED_FILE_FLAGS = (ACC_CLASS_MASK | CLASS_ISPREVERIFIED |
+                                          CLASS_ISOPTIMIZED);
+    if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
+        ALOGW("Invalid file flags in class %s: %04x",
+            descriptor, pClassDef->accessFlags);
+        return NULL;
+    }
+
+    /*
+     * Allocate storage for the class object on the GC heap, so that other
+     * objects can have references to it.  We bypass the usual mechanism
+     * (allocObject), because we don't have all the bits and pieces yet.
+     *
+     * Note that we assume that java.lang.Class does not override
+     * finalize().
+     */
+    /* TODO: Can there be fewer special checks in the usual path? */
+    assert(descriptor != NULL);
+    if (classLoader == NULL &&
+        strcmp(descriptor, "Ljava/lang/Class;") == 0) {
+        assert(gDvm.classJavaLangClass != NULL);
+        newClass = gDvm.classJavaLangClass;
+    } else {
+        size_t size = classObjectSize(pHeader->staticFieldsSize);
+        newClass = (ClassObject*) dvmMalloc(size, ALLOC_NON_MOVING);
+    }
+    if (newClass == NULL)
+        return NULL;
+
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptor = descriptor;
+    assert(newClass->descriptorAlloc == NULL);
+    SET_CLASS_FLAG(newClass, pClassDef->accessFlags);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)classLoader);
+    newClass->pDvmDex = pDvmDex;
+    newClass->primitiveType = PRIM_NOT;
+    newClass->status = CLASS_IDX;
+
+    /*
+     * Stuff the superclass index into the object pointer field.  The linker
+     * pulls it out and replaces it with a resolved ClassObject pointer.
+     * I'm doing it this way (rather than having a dedicated superclassIdx
+     * field) to save a few bytes of overhead per class.
+     *
+     * newClass->super is not traversed or freed by dvmFreeClassInnards, so
+     * this is safe.
+     */
+    assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */
+    newClass->super = (ClassObject*) pClassDef->superclassIdx;
+
+    /*
+     * Stuff class reference indices into the pointer fields.
+     *
+     * The elements of newClass->interfaces are not traversed or freed by
+     * dvmFreeClassInnards, so this is GC-safe.
+     */
+    const DexTypeList* pInterfacesList;
+    pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
+    if (pInterfacesList != NULL) {
+        newClass->interfaceCount = pInterfacesList->size;
+        newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
+                newClass->interfaceCount * sizeof(ClassObject*));
+
+        for (i = 0; i < newClass->interfaceCount; i++) {
+            const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
+            newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
+        }
+        dvmLinearReadOnly(classLoader, newClass->interfaces);
+    }
+
+    /* load field definitions */
+
+    /*
+     * Over-allocate the class object and append static field info
+     * onto the end.  It's fixed-size and known at alloc time.  This
+     * seems to increase zygote sharing.  Heap compaction will have to
+     * be careful if it ever tries to move ClassObject instances,
+     * because we pass Field pointers around internally. But at least
+     * now these Field pointers are in the object heap.
+     */
+
+    if (pHeader->staticFieldsSize != 0) {
+        /* static fields stay on system heap; field data isn't "write once" */
+        int count = (int) pHeader->staticFieldsSize;
+        u4 lastIndex = 0;
+        DexField field;
+
+        newClass->sfieldCount = count;
+        for (i = 0; i < count; i++) {
+            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+            loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
+        }
+    }
+
+    if (pHeader->instanceFieldsSize != 0) {
+        int count = (int) pHeader->instanceFieldsSize;
+        u4 lastIndex = 0;
+        DexField field;
+
+        newClass->ifieldCount = count;
+        newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
+                count * sizeof(InstField));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+            loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
+        }
+        dvmLinearReadOnly(classLoader, newClass->ifields);
+    }
+
+    /*
+     * Load method definitions.  We do this in two batches, direct then
+     * virtual.
+     *
+     * If register maps have already been generated for this class, and
+     * precise GC is enabled, we pull out pointers to them.  We know that
+     * they were streamed to the DEX file in the same order in which the
+     * methods appear.
+     *
+     * If the class wasn't pre-verified, the maps will be generated when
+     * the class is verified during class initialization.
+     */
+    u4 classDefIdx = dexGetIndexForClassDef(pDexFile, pClassDef);
+    const void* classMapData;
+    u4 numMethods;
+
+    if (gDvm.preciseGc) {
+        classMapData =
+            dvmRegisterMapGetClassData(pDexFile, classDefIdx, &numMethods);
+
+        /* sanity check */
+        if (classMapData != NULL &&
+            pHeader->directMethodsSize + pHeader->virtualMethodsSize != numMethods)
+        {
+            ALOGE("ERROR: in %s, direct=%d virtual=%d, maps have %d",
+                newClass->descriptor, pHeader->directMethodsSize,
+                pHeader->virtualMethodsSize, numMethods);
+            assert(false);
+            classMapData = NULL;        /* abandon */
+        }
+    } else {
+        classMapData = NULL;
+    }
+
+    if (pHeader->directMethodsSize != 0) {
+        int count = (int) pHeader->directMethodsSize;
+        u4 lastIndex = 0;
+        DexMethod method;
+
+        newClass->directMethodCount = count;
+        newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
+                count * sizeof(Method));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+            loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
+            if (classMapData != NULL) {
+                const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+                if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+                    newClass->directMethods[i].registerMap = pMap;
+                    /* TODO: add rigorous checks */
+                    assert((newClass->directMethods[i].registersSize+7) / 8 ==
+                        newClass->directMethods[i].registerMap->regWidth);
+                }
+            }
+        }
+        dvmLinearReadOnly(classLoader, newClass->directMethods);
+    }
+
+    if (pHeader->virtualMethodsSize != 0) {
+        int count = (int) pHeader->virtualMethodsSize;
+        u4 lastIndex = 0;
+        DexMethod method;
+
+        newClass->virtualMethodCount = count;
+        newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
+                count * sizeof(Method));
+        for (i = 0; i < count; i++) {
+            dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+            loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
+            if (classMapData != NULL) {
+                const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+                if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+                    newClass->virtualMethods[i].registerMap = pMap;
+                    /* TODO: add rigorous checks */
+                    assert((newClass->virtualMethods[i].registersSize+7) / 8 ==
+                        newClass->virtualMethods[i].registerMap->regWidth);
+                }
+            }
+        }
+        dvmLinearReadOnly(classLoader, newClass->virtualMethods);
+    }
+
+    newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return newClass;
+}
+
+/*
+ * Try to load the indicated class from the specified DEX file.
+ *
+ * This is effectively loadClass()+defineClass() for a DexClassDef.  The
+ * loading was largely done when we crunched through the DEX.
+ *
+ * Returns NULL on failure.  If we locate the class but encounter an error
+ * while processing it, an appropriate exception is thrown.
+ */
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+    const DexClassDef* pClassDef, Object* classLoader)
+{
+    ClassObject* result;
+    DexClassDataHeader header;
+    const u1* pEncodedData;
+    const DexFile* pDexFile;
+
+    assert((pDvmDex != NULL) && (pClassDef != NULL));
+    pDexFile = pDvmDex->pDexFile;
+
+    if (gDvm.verboseClass) {
+        ALOGV("CLASS: loading '%s'...",
+            dexGetClassDescriptor(pDexFile, pClassDef));
+    }
+
+    pEncodedData = dexGetClassData(pDexFile, pClassDef);
+
+    if (pEncodedData != NULL) {
+        dexReadClassDataHeader(&pEncodedData, &header);
+    } else {
+        // Provide an all-zeroes header for the rest of the loading.
+        memset(&header, 0, sizeof(header));
+    }
+
+    result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
+            classLoader);
+
+    if (gDvm.verboseClass && (result != NULL)) {
+        ALOGI("[Loaded %s from DEX %p (cl=%p)]",
+            result->descriptor, pDvmDex, classLoader);
+    }
+
+    return result;
+}
+
+/*
+ * Free anything in a ClassObject that was allocated on the system heap.
+ *
+ * The ClassObject itself is allocated on the GC heap, so we leave it for
+ * the garbage collector.
+ *
+ * NOTE: this may be called with a partially-constructed object.
+ * NOTE: there is no particular ordering imposed, so don't go poking at
+ * superclasses.
+ */
+void dvmFreeClassInnards(ClassObject* clazz)
+{
+    void *tp;
+    int i;
+
+    if (clazz == NULL)
+        return;
+
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+
+    /* Guarantee that dvmFreeClassInnards can be called on a given
+     * class multiple times by clearing things out as we free them.
+     * We don't make any attempt at real atomicity here; higher
+     * levels need to make sure that no two threads can free the
+     * same ClassObject at the same time.
+     *
+     * TODO: maybe just make it so the GC will never free the
+     * innards of an already-freed class.
+     *
+     * TODO: this #define isn't MT-safe -- the compiler could rearrange it.
+     */
+#define NULL_AND_FREE(p) \
+    do { \
+        if ((p) != NULL) { \
+            tp = (p); \
+            (p) = NULL; \
+            free(tp); \
+        } \
+    } while (0)
+#define NULL_AND_LINEAR_FREE(p) \
+    do { \
+        if ((p) != NULL) { \
+            tp = (p); \
+            (p) = NULL; \
+            dvmLinearFree(clazz->classLoader, tp); \
+        } \
+    } while (0)
+
+    /* arrays just point at Object's vtable; don't free vtable in this case.
+     */
+    clazz->vtableCount = -1;
+    if (clazz->vtable == gDvm.classJavaLangObject->vtable) {
+        clazz->vtable = NULL;
+    } else {
+        NULL_AND_LINEAR_FREE(clazz->vtable);
+    }
+
+    clazz->descriptor = NULL;
+    NULL_AND_FREE(clazz->descriptorAlloc);
+
+    if (clazz->directMethods != NULL) {
+        Method *directMethods = clazz->directMethods;
+        int directMethodCount = clazz->directMethodCount;
+        clazz->directMethods = NULL;
+        clazz->directMethodCount = -1;
+        dvmLinearReadWrite(clazz->classLoader, directMethods);
+        for (i = 0; i < directMethodCount; i++) {
+            freeMethodInnards(&directMethods[i]);
+        }
+        dvmLinearReadOnly(clazz->classLoader, directMethods);
+        dvmLinearFree(clazz->classLoader, directMethods);
+    }
+    if (clazz->virtualMethods != NULL) {
+        Method *virtualMethods = clazz->virtualMethods;
+        int virtualMethodCount = clazz->virtualMethodCount;
+        clazz->virtualMethodCount = -1;
+        clazz->virtualMethods = NULL;
+        dvmLinearReadWrite(clazz->classLoader, virtualMethods);
+        for (i = 0; i < virtualMethodCount; i++) {
+            freeMethodInnards(&virtualMethods[i]);
+        }
+        dvmLinearReadOnly(clazz->classLoader, virtualMethods);
+        dvmLinearFree(clazz->classLoader, virtualMethods);
+    }
+
+    InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+    loaderList->initiatingLoaderCount = -1;
+    NULL_AND_FREE(loaderList->initiatingLoaders);
+
+    clazz->interfaceCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->interfaces);
+
+    clazz->iftableCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->iftable);
+
+    clazz->ifviPoolCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->ifviPool);
+
+    clazz->sfieldCount = -1;
+    /* The sfields are attached to the ClassObject, and will be freed
+     * with it. */
+
+    clazz->ifieldCount = -1;
+    NULL_AND_LINEAR_FREE(clazz->ifields);
+
+#undef NULL_AND_FREE
+#undef NULL_AND_LINEAR_FREE
+}
+
+/*
+ * Free anything in a Method that was allocated on the system heap.
+ *
+ * The containing class is largely torn down by this point.
+ */
+static void freeMethodInnards(Method* meth)
+{
+#if 0
+    free(meth->exceptions);
+    free(meth->lines);
+    free(meth->locals);
+#endif
+
+    /*
+     * Some register maps are allocated on the heap, either because of late
+     * verification or because we're caching an uncompressed form.
+     */
+    const RegisterMap* pMap = meth->registerMap;
+    if (pMap != NULL && dvmRegisterMapGetOnHeap(pMap)) {
+        dvmFreeRegisterMap((RegisterMap*) pMap);
+        meth->registerMap = NULL;
+    }
+
+    /*
+     * We may have copied the instructions.
+     */
+    if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+        dvmLinearFree(meth->clazz->classLoader, methodDexCode);
+    }
+}
+
+/*
+ * Clone a Method, making new copies of anything that will be freed up
+ * by freeMethodInnards().  This is used for "miranda" methods.
+ */
+static void cloneMethod(Method* dst, const Method* src)
+{
+    if (src->registerMap != NULL) {
+        ALOGE("GLITCH: only expected abstract methods here");
+        ALOGE("        cloning %s.%s", src->clazz->descriptor, src->name);
+        dvmAbort();
+    }
+    memcpy(dst, src, sizeof(Method));
+}
+
+/*
+ * Pull the interesting pieces out of a DexMethod.
+ *
+ * The DEX file isn't going anywhere, so we don't need to make copies of
+ * the code area.
+ */
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
+    Method* meth)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexMethodId* pMethodId;
+    const DexCode* pDexCode;
+
+    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+
+    meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
+    dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
+    meth->shorty = dexProtoGetShorty(&meth->prototype);
+    meth->accessFlags = pDexMethod->accessFlags;
+    meth->clazz = clazz;
+    meth->jniArgInfo = 0;
+
+    if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
+        /*
+         * The Enum class declares a "final" finalize() method to
+         * prevent subclasses from introducing a finalizer.  We don't
+         * want to set the finalizable flag for Enum or its subclasses,
+         * so we check for it here.
+         *
+         * We also want to avoid setting it on Object, but it's easier
+         * to just strip that out later.
+         */
+        if (clazz->classLoader != NULL ||
+            strcmp(clazz->descriptor, "Ljava/lang/Enum;") != 0)
+        {
+            SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+        }
+    }
+
+    pDexCode = dexGetCode(pDexFile, pDexMethod);
+    if (pDexCode != NULL) {
+        /* integer constants, copy over for faster access */
+        meth->registersSize = pDexCode->registersSize;
+        meth->insSize = pDexCode->insSize;
+        meth->outsSize = pDexCode->outsSize;
+
+        /* pointer to code area */
+        meth->insns = pDexCode->insns;
+    } else {
+        /*
+         * We don't have a DexCode block, but we still want to know how
+         * much space is needed for the arguments (so we don't have to
+         * compute it later).  We also take this opportunity to compute
+         * JNI argument info.
+         *
+         * We do this for abstract methods as well, because we want to
+         * be able to substitute our exception-throwing "stub" in.
+         */
+        int argsSize = dvmComputeMethodArgsSize(meth);
+        if (!dvmIsStaticMethod(meth))
+            argsSize++;
+        meth->registersSize = meth->insSize = argsSize;
+        assert(meth->outsSize == 0);
+        assert(meth->insns == NULL);
+
+        if (dvmIsNativeMethod(meth)) {
+            meth->nativeFunc = dvmResolveNativeMethod;
+            meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
+        }
+    }
+}
+
+#if 0       /* replaced with private/read-write mapping */
+/*
+ * We usually map bytecode directly out of the DEX file, which is mapped
+ * shared read-only.  If we want to be able to modify it, we have to make
+ * a new copy.
+ *
+ * Once copied, the code will be in the LinearAlloc region, which may be
+ * marked read-only.
+ *
+ * The bytecode instructions are embedded inside a DexCode structure, so we
+ * need to copy all of that.  (The dvmGetMethodCode function backs up the
+ * instruction pointer to find the start of the DexCode.)
+ */
+void dvmMakeCodeReadWrite(Method* meth)
+{
+    DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+
+    if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+        dvmLinearReadWrite(meth->clazz->classLoader, methodDexCode);
+        return;
+    }
+
+    assert(!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth));
+
+    size_t dexCodeSize = dexGetDexCodeSize(methodDexCode);
+    ALOGD("Making a copy of %s.%s code (%d bytes)",
+        meth->clazz->descriptor, meth->name, dexCodeSize);
+
+    DexCode* newCode =
+        (DexCode*) dvmLinearAlloc(meth->clazz->classLoader, dexCodeSize);
+    memcpy(newCode, methodDexCode, dexCodeSize);
+
+    meth->insns = newCode->insns;
+    SET_METHOD_FLAG(meth, METHOD_ISWRITABLE);
+}
+
+/*
+ * Mark the bytecode read-only.
+ *
+ * If the contents of the DexCode haven't actually changed, we could revert
+ * to the original shared page.
+ */
+void dvmMakeCodeReadOnly(Method* meth)
+{
+    DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+    ALOGV("+++ marking %p read-only", methodDexCode);
+    dvmLinearReadOnly(meth->clazz->classLoader, methodDexCode);
+}
+#endif
+
+
+/*
+ * jniArgInfo (32-bit int) layout:
+ *   SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ *   S - if set, do things the hard way (scan the signature)
+ *   R - return-type enumeration
+ *   H - target-specific hints
+ *
+ * This info is used at invocation time by dvmPlatformInvoke.  In most
+ * cases, the target-specific hints allow dvmPlatformInvoke to avoid
+ * having to fully parse the signature.
+ *
+ * The return-type bits are always set, even if target-specific hint bits
+ * are unavailable.
+ */
+static int computeJniArgInfo(const DexProto* proto)
+{
+    const char* sig = dexProtoGetShorty(proto);
+    int returnType, jniArgInfo;
+    u4 hints;
+
+    /* The first shorty character is the return type. */
+    switch (*(sig++)) {
+    case 'V':
+        returnType = DALVIK_JNI_RETURN_VOID;
+        break;
+    case 'F':
+        returnType = DALVIK_JNI_RETURN_FLOAT;
+        break;
+    case 'D':
+        returnType = DALVIK_JNI_RETURN_DOUBLE;
+        break;
+    case 'J':
+        returnType = DALVIK_JNI_RETURN_S8;
+        break;
+    case 'Z':
+    case 'B':
+        returnType = DALVIK_JNI_RETURN_S1;
+        break;
+    case 'C':
+        returnType = DALVIK_JNI_RETURN_U2;
+        break;
+    case 'S':
+        returnType = DALVIK_JNI_RETURN_S2;
+        break;
+    default:
+        returnType = DALVIK_JNI_RETURN_S4;
+        break;
+    }
+
+    jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT;
+
+    hints = dvmPlatformInvokeHints(proto);
+
+    if (hints & DALVIK_JNI_NO_ARG_INFO) {
+        jniArgInfo |= DALVIK_JNI_NO_ARG_INFO;
+    } else {
+        assert((hints & DALVIK_JNI_RETURN_MASK) == 0);
+        jniArgInfo |= hints;
+    }
+
+    return jniArgInfo;
+}
+
+/*
+ * Load information about a static field.
+ *
+ * This also "prepares" static fields by initializing them
+ * to their "standard default values".
+ */
+static void loadSFieldFromDex(ClassObject* clazz,
+    const DexField* pDexSField, StaticField* sfield)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexFieldId* pFieldId;
+
+    pFieldId = dexGetFieldId(pDexFile, pDexSField->fieldIdx);
+
+    sfield->clazz = clazz;
+    sfield->name = dexStringById(pDexFile, pFieldId->nameIdx);
+    sfield->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    sfield->accessFlags = pDexSField->accessFlags;
+
+    /* Static object field values are set to "standard default values"
+     * (null or 0) until the class is initialized.  We delay loading
+     * constant values from the class until that time.
+     */
+    //sfield->value.j = 0;
+    assert(sfield->value.j == 0LL);     // cleared earlier with calloc
+}
+
+/*
+ * Load information about an instance field.
+ */
+static void loadIFieldFromDex(ClassObject* clazz,
+    const DexField* pDexIField, InstField* ifield)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexFieldId* pFieldId;
+
+    pFieldId = dexGetFieldId(pDexFile, pDexIField->fieldIdx);
+
+    ifield->clazz = clazz;
+    ifield->name = dexStringById(pDexFile, pFieldId->nameIdx);
+    ifield->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    ifield->accessFlags = pDexIField->accessFlags;
+#ifndef NDEBUG
+    assert(ifield->byteOffset == 0);    // cleared earlier with calloc
+    ifield->byteOffset = -1;    // make it obvious if we fail to set later
+#endif
+}
+
+/*
+ * Cache java.lang.ref.Reference fields and methods.
+ */
+static bool precacheReferenceOffsets(ClassObject* clazz)
+{
+    int i;
+
+    /* We trick the GC object scanner by not counting
+     * java.lang.ref.Reference.referent as an object
+     * field.  It will get explicitly scanned as part
+     * of the reference-walking process.
+     *
+     * Find the object field named "referent" and put it
+     * just after the list of object reference fields.
+     */
+    dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+    for (i = 0; i < clazz->ifieldRefCount; i++) {
+        InstField *pField = &clazz->ifields[i];
+        if (strcmp(pField->name, "referent") == 0) {
+            int targetIndex;
+
+            /* Swap this field with the last object field.
+             */
+            targetIndex = clazz->ifieldRefCount - 1;
+            if (i != targetIndex) {
+                InstField *swapField = &clazz->ifields[targetIndex];
+                InstField tmpField;
+                int tmpByteOffset;
+
+                /* It's not currently strictly necessary
+                 * for the fields to be in byteOffset order,
+                 * but it's more predictable that way.
+                 */
+                tmpByteOffset = swapField->byteOffset;
+                swapField->byteOffset = pField->byteOffset;
+                pField->byteOffset = tmpByteOffset;
+
+                tmpField = *swapField;
+                *swapField = *pField;
+                *pField = tmpField;
+            }
+
+            /* One fewer object field (wink wink).
+             */
+            clazz->ifieldRefCount--;
+            i--;        /* don't trip "didn't find it" test if field was last */
+            break;
+        }
+    }
+    dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+    if (i == clazz->ifieldRefCount) {
+        ALOGE("Unable to reorder 'referent' in %s", clazz->descriptor);
+        return false;
+    }
+
+    /*
+     * Now that the above has been done, it is safe to cache
+     * info about the class.
+     */
+    if (!dvmFindReferenceMembers(clazz)) {
+        ALOGE("Trouble with Reference setup");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Set the bitmap of reference offsets, refOffsets, from the ifields
+ * list.
+ */
+static void computeRefOffsets(ClassObject* clazz)
+{
+    if (clazz->super != NULL) {
+        clazz->refOffsets = clazz->super->refOffsets;
+    } else {
+        clazz->refOffsets = 0;
+    }
+    /*
+     * If our superclass overflowed, we don't stand a chance.
+     */
+    if (clazz->refOffsets != CLASS_WALK_SUPER) {
+        InstField *f;
+        int i;
+
+        /* All of the fields that contain object references
+         * are guaranteed to be at the beginning of the ifields list.
+         */
+        f = clazz->ifields;
+        const int ifieldRefCount = clazz->ifieldRefCount;
+        for (i = 0; i < ifieldRefCount; i++) {
+          /*
+           * Note that, per the comment on struct InstField,
+           * f->byteOffset is the offset from the beginning of
+           * obj, not the offset into obj->instanceData.
+           */
+          assert(f->byteOffset >= (int) CLASS_SMALLEST_OFFSET);
+          assert((f->byteOffset & (CLASS_OFFSET_ALIGNMENT - 1)) == 0);
+          if (CLASS_CAN_ENCODE_OFFSET(f->byteOffset)) {
+              u4 newBit = CLASS_BIT_FROM_OFFSET(f->byteOffset);
+              assert(newBit != 0);
+              clazz->refOffsets |= newBit;
+          } else {
+              clazz->refOffsets = CLASS_WALK_SUPER;
+              break;
+          }
+          f++;
+        }
+    }
+}
+
+
+/*
+ * Link (prepare and resolve).  Verification is deferred until later.
+ *
+ * This converts symbolic references into pointers.  It's independent of
+ * the source file format.
+ *
+ * If clazz->status is CLASS_IDX, then clazz->super and interfaces[] are
+ * holding class reference indices rather than pointers.  The class
+ * references will be resolved during link.  (This is done when
+ * loading from DEX to avoid having to create additional storage to
+ * pass the indices around.)
+ *
+ * Returns "false" with an exception pending on failure.
+ */
+bool dvmLinkClass(ClassObject* clazz)
+{
+    u4 superclassIdx = 0;
+    u4 *interfaceIdxArray = NULL;
+    bool okay = false;
+    int i;
+
+    assert(clazz != NULL);
+    assert(clazz->descriptor != NULL);
+    assert(clazz->status == CLASS_IDX || clazz->status == CLASS_LOADED);
+    if (gDvm.verboseClass)
+        ALOGV("CLASS: linking '%s'...", clazz->descriptor);
+
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(clazz->clazz == gDvm.classJavaLangClass);
+    assert(dvmIsClassObject(clazz));
+    if (clazz->classLoader == NULL &&
+        (strcmp(clazz->descriptor, "Ljava/lang/Class;") == 0))
+    {
+        if (gDvm.classJavaLangClass->ifieldCount > CLASS_FIELD_SLOTS) {
+            ALOGE("java.lang.Class has %d instance fields (expected at most %d)",
+                 gDvm.classJavaLangClass->ifieldCount, CLASS_FIELD_SLOTS);
+            dvmAbort();
+        }
+        if (gDvm.classJavaLangClass->sfieldCount != CLASS_SFIELD_SLOTS) {
+            ALOGE("java.lang.Class has %d static fields (expected %d)",
+                 gDvm.classJavaLangClass->sfieldCount, CLASS_SFIELD_SLOTS);
+            dvmAbort();
+        }
+    }
+
+    /* "Resolve" the class.
+     *
+     * At this point, clazz's reference fields may contain Dex file
+     * indices instead of direct object references.  Proxy objects are
+     * an exception, and may be the only exception.  We need to
+     * translate those indices into real references, and let the GC
+     * look inside this ClassObject.
+     */
+    if (clazz->status == CLASS_IDX) {
+        if (clazz->interfaceCount > 0) {
+            /* Copy u4 DEX idx values out of the ClassObject* array
+             * where we stashed them.
+             */
+            assert(sizeof(*interfaceIdxArray) == sizeof(*clazz->interfaces));
+            size_t len = clazz->interfaceCount * sizeof(*interfaceIdxArray);
+            interfaceIdxArray = (u4*)malloc(len);
+            if (interfaceIdxArray == NULL) {
+                ALOGW("Unable to allocate memory to link %s", clazz->descriptor);
+                goto bail;
+            }
+            memcpy(interfaceIdxArray, clazz->interfaces, len);
+
+            dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+            memset(clazz->interfaces, 0, len);
+            dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+        }
+
+        assert(sizeof(superclassIdx) == sizeof(clazz->super));
+        superclassIdx = (u4) clazz->super;
+        clazz->super = NULL;
+        /* After this line, clazz will be fair game for the GC. The
+         * superclass and interfaces are all NULL.
+         */
+        clazz->status = CLASS_LOADED;
+
+        if (superclassIdx != kDexNoIndex) {
+            ClassObject* super = dvmResolveClass(clazz, superclassIdx, false);
+            if (super == NULL) {
+                assert(dvmCheckException(dvmThreadSelf()));
+                if (gDvm.optimizing) {
+                    /* happens with "external" libs */
+                    ALOGV("Unable to resolve superclass of %s (%d)",
+                         clazz->descriptor, superclassIdx);
+                } else {
+                    ALOGW("Unable to resolve superclass of %s (%d)",
+                         clazz->descriptor, superclassIdx);
+                }
+                goto bail;
+            }
+            dvmSetFieldObject((Object *)clazz,
+                              OFFSETOF_MEMBER(ClassObject, super),
+                              (Object *)super);
+        }
+
+        if (clazz->interfaceCount > 0) {
+            /* Resolve the interfaces implemented directly by this class. */
+            assert(interfaceIdxArray != NULL);
+            dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+            for (i = 0; i < clazz->interfaceCount; i++) {
+                assert(interfaceIdxArray[i] != kDexNoIndex);
+                clazz->interfaces[i] =
+                    dvmResolveClass(clazz, interfaceIdxArray[i], false);
+                if (clazz->interfaces[i] == NULL) {
+                    const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+
+                    assert(dvmCheckException(dvmThreadSelf()));
+                    dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+
+                    const char* classDescriptor;
+                    classDescriptor =
+                        dexStringByTypeIdx(pDexFile, interfaceIdxArray[i]);
+                    if (gDvm.optimizing) {
+                        /* happens with "external" libs */
+                        ALOGV("Failed resolving %s interface %d '%s'",
+                             clazz->descriptor, interfaceIdxArray[i],
+                             classDescriptor);
+                    } else {
+                        ALOGI("Failed resolving %s interface %d '%s'",
+                             clazz->descriptor, interfaceIdxArray[i],
+                             classDescriptor);
+                    }
+                    goto bail;
+                }
+
+                /* are we allowed to implement this interface? */
+                if (!dvmCheckClassAccess(clazz, clazz->interfaces[i])) {
+                    dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+                    ALOGW("Interface '%s' is not accessible to '%s'",
+                         clazz->interfaces[i]->descriptor, clazz->descriptor);
+                    dvmThrowIllegalAccessError("interface not accessible");
+                    goto bail;
+                }
+                LOGVV("+++  found interface '%s'",
+                      clazz->interfaces[i]->descriptor);
+            }
+            dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+        }
+    }
+    /*
+     * There are now Class references visible to the GC in super and
+     * interfaces.
+     */
+
+    /*
+     * All classes have a direct superclass, except for
+     * java/lang/Object and primitive classes. Primitive classes are
+     * are created CLASS_INITIALIZED, so won't get here.
+     */
+    assert(clazz->primitiveType == PRIM_NOT);
+    if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
+        if (clazz->super != NULL) {
+            /* TODO: is this invariant true for all java/lang/Objects,
+             * regardless of the class loader?  For now, assume it is.
+             */
+            dvmThrowClassFormatError("java.lang.Object has a superclass");
+            goto bail;
+        }
+
+        /* Don't finalize objects whose classes use the
+         * default (empty) Object.finalize().
+         */
+        CLEAR_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+    } else {
+        if (clazz->super == NULL) {
+            dvmThrowLinkageError("no superclass defined");
+            goto bail;
+        }
+        /* verify */
+        if (dvmIsFinalClass(clazz->super)) {
+            ALOGW("Superclass of '%s' is final '%s'",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIncompatibleClassChangeError("superclass is final");
+            goto bail;
+        } else if (dvmIsInterfaceClass(clazz->super)) {
+            ALOGW("Superclass of '%s' is interface '%s'",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIncompatibleClassChangeError("superclass is an interface");
+            goto bail;
+        } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
+            ALOGW("Superclass of '%s' (%s) is not accessible",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowIllegalAccessError("superclass not accessible");
+            goto bail;
+        }
+
+        /* Inherit finalizability from the superclass.  If this
+         * class also overrides finalize(), its CLASS_ISFINALIZABLE
+         * bit will already be set.
+         */
+        if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISFINALIZABLE)) {
+            SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+        }
+
+        /* See if this class descends from java.lang.Reference
+         * and set the class flags appropriately.
+         */
+        if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISREFERENCE)) {
+            u4 superRefFlags;
+
+            /* We've already determined the reference type of this
+             * inheritance chain.  Inherit reference-ness from the superclass.
+             */
+            superRefFlags = GET_CLASS_FLAG_GROUP(clazz->super,
+                    CLASS_ISREFERENCE |
+                    CLASS_ISWEAKREFERENCE |
+                    CLASS_ISFINALIZERREFERENCE |
+                    CLASS_ISPHANTOMREFERENCE);
+            SET_CLASS_FLAG(clazz, superRefFlags);
+        } else if (clazz->classLoader == NULL &&
+                clazz->super->classLoader == NULL &&
+                strcmp(clazz->super->descriptor,
+                       "Ljava/lang/ref/Reference;") == 0)
+        {
+            u4 refFlags;
+
+            /* This class extends Reference, which means it should
+             * be one of the magic Soft/Weak/PhantomReference classes.
+             */
+            refFlags = CLASS_ISREFERENCE;
+            if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/SoftReference;") == 0)
+            {
+                /* Only CLASS_ISREFERENCE is set for soft references.
+                 */
+            } else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/WeakReference;") == 0)
+            {
+                refFlags |= CLASS_ISWEAKREFERENCE;
+            } else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/FinalizerReference;") == 0)
+            {
+                refFlags |= CLASS_ISFINALIZERREFERENCE;
+            }  else if (strcmp(clazz->descriptor,
+                       "Ljava/lang/ref/PhantomReference;") == 0)
+            {
+                refFlags |= CLASS_ISPHANTOMREFERENCE;
+            } else {
+                /* No-one else is allowed to inherit directly
+                 * from Reference.
+                 */
+//xxx is this the right exception?  better than an assertion.
+                dvmThrowLinkageError("illegal inheritance from Reference");
+                goto bail;
+            }
+
+            /* The class should not have any reference bits set yet.
+             */
+            assert(GET_CLASS_FLAG_GROUP(clazz,
+                    CLASS_ISREFERENCE |
+                    CLASS_ISWEAKREFERENCE |
+                    CLASS_ISFINALIZERREFERENCE |
+                    CLASS_ISPHANTOMREFERENCE) == 0);
+
+            SET_CLASS_FLAG(clazz, refFlags);
+        }
+    }
+
+    /*
+     * Populate vtable.
+     */
+    if (dvmIsInterfaceClass(clazz)) {
+        /* no vtable; just set the method indices */
+        int count = clazz->virtualMethodCount;
+
+        if (count != (u2) count) {
+            ALOGE("Too many methods (%d) in interface '%s'", count,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+        for (i = 0; i < count; i++)
+            clazz->virtualMethods[i].methodIndex = (u2) i;
+
+        dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    } else {
+        if (!createVtable(clazz)) {
+            ALOGW("failed creating vtable");
+            goto bail;
+        }
+    }
+
+    /*
+     * Populate interface method tables.  Can alter the vtable.
+     */
+    if (!createIftable(clazz))
+        goto bail;
+
+    /*
+     * Insert special-purpose "stub" method implementations.
+     */
+    if (!insertMethodStubs(clazz))
+        goto bail;
+
+    /*
+     * Compute instance field offsets and, hence, the size of the object.
+     */
+    if (!computeFieldOffsets(clazz))
+        goto bail;
+
+    /*
+     * Cache field and method info for the class Reference (as loaded
+     * by the boot classloader). This has to happen after the call to
+     * computeFieldOffsets().
+     */
+    if ((clazz->classLoader == NULL)
+            && (strcmp(clazz->descriptor, "Ljava/lang/ref/Reference;") == 0)) {
+        if (!precacheReferenceOffsets(clazz)) {
+            ALOGE("failed pre-caching Reference offsets");
+            dvmThrowInternalError(NULL);
+            goto bail;
+        }
+    }
+
+    /*
+     * Compact the offsets the GC has to examine into a bitmap, if
+     * possible.  (This has to happen after Reference.referent is
+     * massaged in precacheReferenceOffsets.)
+     */
+    computeRefOffsets(clazz);
+
+    /*
+     * Done!
+     */
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED))
+        clazz->status = CLASS_VERIFIED;
+    else
+        clazz->status = CLASS_RESOLVED;
+    okay = true;
+    if (gDvm.verboseClass)
+        ALOGV("CLASS: linked '%s'", clazz->descriptor);
+
+    /*
+     * We send CLASS_PREPARE events to the debugger from here.  The
+     * definition of "preparation" is creating the static fields for a
+     * class and initializing them to the standard default values, but not
+     * executing any code (that comes later, during "initialization").
+     *
+     * We did the static prep in loadSFieldFromDex() while loading the class.
+     *
+     * The class has been prepared and resolved but possibly not yet verified
+     * at this point.
+     */
+    if (gDvm.debuggerActive) {
+        dvmDbgPostClassPrepare(clazz);
+    }
+
+bail:
+    if (!okay) {
+        clazz->status = CLASS_ERROR;
+        if (!dvmCheckException(dvmThreadSelf())) {
+            dvmThrowVirtualMachineError(NULL);
+        }
+    }
+    if (interfaceIdxArray != NULL) {
+        free(interfaceIdxArray);
+    }
+
+    return okay;
+}
+
+/*
+ * Create the virtual method table.
+ *
+ * The top part of the table is a copy of the table from our superclass,
+ * with our local methods overriding theirs.  The bottom part of the table
+ * has any new methods we defined.
+ */
+static bool createVtable(ClassObject* clazz)
+{
+    bool result = false;
+    int maxCount;
+    int i;
+
+    if (clazz->super != NULL) {
+        //ALOGI("SUPER METHODS %d %s->%s", clazz->super->vtableCount,
+        //    clazz->descriptor, clazz->super->descriptor);
+    }
+
+    /* the virtual methods we define, plus the superclass vtable size */
+    maxCount = clazz->virtualMethodCount;
+    if (clazz->super != NULL) {
+        maxCount += clazz->super->vtableCount;
+    } else {
+        /* TODO: is this invariant true for all java/lang/Objects,
+         * regardless of the class loader?  For now, assume it is.
+         */
+        assert(strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0);
+    }
+    //ALOGD("+++ max vmethods for '%s' is %d", clazz->descriptor, maxCount);
+
+    /*
+     * Over-allocate the table, then realloc it down if necessary.  So
+     * long as we don't allocate anything in between we won't cause
+     * fragmentation, and reducing the size should be unlikely to cause
+     * a buffer copy.
+     */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    clazz->vtable = (Method**) dvmLinearAlloc(clazz->classLoader,
+                        sizeof(Method*) * maxCount);
+    if (clazz->vtable == NULL)
+        goto bail;
+
+    if (clazz->super != NULL) {
+        int actualCount;
+
+        memcpy(clazz->vtable, clazz->super->vtable,
+            sizeof(*(clazz->vtable)) * clazz->super->vtableCount);
+        actualCount = clazz->super->vtableCount;
+
+        /*
+         * See if any of our virtual methods override the superclass.
+         */
+        for (i = 0; i < clazz->virtualMethodCount; i++) {
+            Method* localMeth = &clazz->virtualMethods[i];
+            int si;
+
+            for (si = 0; si < clazz->super->vtableCount; si++) {
+                Method* superMeth = clazz->vtable[si];
+
+                if (dvmCompareMethodNamesAndProtos(localMeth, superMeth) == 0) {
+                    // We should have an access check here, but some apps rely on us not
+                    // checking access: http://b/7301030
+                    bool isAccessible = dvmCheckMethodAccess(clazz, superMeth);
+                    if (dvmIsFinalMethod(superMeth)) {
+                        ALOGE("Method %s.%s overrides final %s.%s",
+                              localMeth->clazz->descriptor, localMeth->name,
+                              superMeth->clazz->descriptor, superMeth->name);
+                        goto bail;
+                    }
+
+                    // Warn if we just spotted code relying on this bug...
+                    if (!isAccessible) {
+                        ALOGW("method %s.%s incorrectly overrides "
+                              "package-private method with same name in %s",
+                              localMeth->clazz->descriptor, localMeth->name,
+                              superMeth->clazz->descriptor);
+                    }
+
+                    clazz->vtable[si] = localMeth;
+                    localMeth->methodIndex = (u2) si;
+                    //ALOGV("+++   override %s.%s (slot %d)",
+                    //    clazz->descriptor, localMeth->name, si);
+                    break;
+                }
+            }
+
+            if (si == clazz->super->vtableCount) {
+                /* not an override, add to end */
+                clazz->vtable[actualCount] = localMeth;
+                localMeth->methodIndex = (u2) actualCount;
+                actualCount++;
+
+                //ALOGV("+++   add method %s.%s",
+                //    clazz->descriptor, localMeth->name);
+            }
+        }
+
+        if (actualCount != (u2) actualCount) {
+            ALOGE("Too many methods (%d) in class '%s'", actualCount,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        assert(actualCount <= maxCount);
+
+        if (actualCount < maxCount) {
+            assert(clazz->vtable != NULL);
+            dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+            clazz->vtable = (Method **)dvmLinearRealloc(clazz->classLoader,
+                clazz->vtable, sizeof(*(clazz->vtable)) * actualCount);
+            if (clazz->vtable == NULL) {
+                ALOGE("vtable realloc failed");
+                goto bail;
+            } else {
+                LOGVV("+++  reduced vtable from %d to %d",
+                    maxCount, actualCount);
+            }
+        }
+
+        clazz->vtableCount = actualCount;
+    } else {
+        /* java/lang/Object case */
+        int count = clazz->virtualMethodCount;
+        if (count != (u2) count) {
+            ALOGE("Too many methods (%d) in base class '%s'", count,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        for (i = 0; i < count; i++) {
+            clazz->vtable[i] = &clazz->virtualMethods[i];
+            clazz->virtualMethods[i].methodIndex = (u2) i;
+        }
+        clazz->vtableCount = clazz->virtualMethodCount;
+    }
+
+    result = true;
+
+bail:
+    dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    return result;
+}
+
+/*
+ * Create and populate "iftable".
+ *
+ * The set of interfaces we support is the combination of the interfaces
+ * we implement directly and those implemented by our superclass.  Each
+ * interface can have one or more "superinterfaces", which we must also
+ * support.  For speed we flatten the tree out.
+ *
+ * We might be able to speed this up when there are lots of interfaces
+ * by merge-sorting the class pointers and binary-searching when removing
+ * duplicates.  We could also drop the duplicate removal -- it's only
+ * there to reduce the memory footprint.
+ *
+ * Because of "Miranda methods", this may reallocate clazz->virtualMethods.
+ *
+ * Returns "true" on success.
+ */
+static bool createIftable(ClassObject* clazz)
+{
+    bool result = false;
+    bool zapIftable = false;
+    bool zapVtable = false;
+    bool zapIfvipool = false;
+    int poolOffset = 0, poolSize = 0;
+    Method** mirandaList = NULL;
+    int mirandaCount = 0, mirandaAlloc = 0;
+
+    int superIfCount;
+    if (clazz->super != NULL)
+        superIfCount = clazz->super->iftableCount;
+    else
+        superIfCount = 0;
+
+    int ifCount = superIfCount;
+    ifCount += clazz->interfaceCount;
+    for (int i = 0; i < clazz->interfaceCount; i++)
+        ifCount += clazz->interfaces[i]->iftableCount;
+
+    LOGVV("INTF: class '%s' direct w/supra=%d super=%d total=%d",
+        clazz->descriptor, ifCount - superIfCount, superIfCount, ifCount);
+
+    if (ifCount == 0) {
+        assert(clazz->iftableCount == 0);
+        assert(clazz->iftable == NULL);
+        return true;
+    }
+
+    /*
+     * Create a table with enough space for all interfaces, and copy the
+     * superclass' table in.
+     */
+    clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+                        sizeof(InterfaceEntry) * ifCount);
+    zapIftable = true;
+    memset(clazz->iftable, 0x00, sizeof(InterfaceEntry) * ifCount);
+    if (superIfCount != 0) {
+        memcpy(clazz->iftable, clazz->super->iftable,
+            sizeof(InterfaceEntry) * superIfCount);
+    }
+
+    /*
+     * Create a flattened interface hierarchy of our immediate interfaces.
+     */
+    int idx = superIfCount;
+
+    for (int i = 0; i < clazz->interfaceCount; i++) {
+        ClassObject* interf = clazz->interfaces[i];
+        assert(interf != NULL);
+
+        /* make sure this is still an interface class */
+        if (!dvmIsInterfaceClass(interf)) {
+            ALOGW("Class '%s' implements non-interface '%s'",
+                clazz->descriptor, interf->descriptor);
+            dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                clazz->descriptor);
+            goto bail;
+        }
+
+        /* add entry for this interface */
+        clazz->iftable[idx++].clazz = interf;
+
+        /* add entries for the interface's superinterfaces */
+        for (int j = 0; j < interf->iftableCount; j++) {
+            int k;
+            ClassObject *cand;
+
+            cand = interf->iftable[j].clazz;
+
+            /*
+             * Check if this interface was already added and add only if new.
+             * This is to avoid a potential blowup in the number of
+             * interfaces for sufficiently complicated interface hierarchies.
+             * This has quadratic runtime in the number of interfaces.
+             * However, in common cases with little interface inheritance, this
+             * doesn't make much of a difference.
+             */
+            for (k = 0; k < idx; k++)
+                if (clazz->iftable[k].clazz == cand)
+                    break;
+
+            if (k == idx)
+                clazz->iftable[idx++].clazz = cand;
+        }
+    }
+
+    assert(idx <= ifCount);
+
+    /*
+     * Adjust the ifCount. We could reallocate the interface memory here,
+     * but it's probably not worth the effort, the important thing here
+     * is to avoid the interface blowup and keep the ifCount low.
+     */
+    if (false) {
+        if (idx != ifCount) {
+            int newIfCount = idx;
+            InterfaceEntry* oldmem = clazz->iftable;
+
+            clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+                            sizeof(InterfaceEntry) * newIfCount);
+            memcpy(clazz->iftable, oldmem, sizeof(InterfaceEntry) * newIfCount);
+            dvmLinearFree(clazz->classLoader, oldmem);
+        }
+    }
+
+    ifCount = idx;
+    clazz->iftableCount = ifCount;
+
+    /*
+     * If we're an interface, we don't need the vtable pointers, so
+     * we're done.  If this class doesn't implement an interface that our
+     * superclass doesn't have, then we again have nothing to do.
+     */
+    if (dvmIsInterfaceClass(clazz) || superIfCount == ifCount) {
+        //dvmDumpClass(clazz, kDumpClassFullDetail);
+        result = true;
+        goto bail;
+    }
+
+    /*
+     * When we're handling invokeinterface, we probably have an object
+     * whose type is an interface class rather than a concrete class.  We
+     * need to convert the method reference into a vtable index.  So, for
+     * every entry in "iftable", we create a list of vtable indices.
+     *
+     * Because our vtable encompasses the superclass vtable, we can use
+     * the vtable indices from our superclass for all of the interfaces
+     * that weren't directly implemented by us.
+     *
+     * Each entry in "iftable" has a pointer to the start of its set of
+     * vtable offsets.  The iftable entries in the superclass point to
+     * storage allocated in the superclass, and the iftable entries added
+     * for this class point to storage allocated in this class.  "iftable"
+     * is flat for fast access in a class and all of its subclasses, but
+     * "ifviPool" is only created for the topmost implementor.
+     */
+    for (int i = superIfCount; i < ifCount; i++) {
+        /*
+         * Note it's valid for an interface to have no methods (e.g.
+         * java/io/Serializable).
+         */
+        LOGVV("INTF: pool: %d from %s",
+            clazz->iftable[i].clazz->virtualMethodCount,
+            clazz->iftable[i].clazz->descriptor);
+        poolSize += clazz->iftable[i].clazz->virtualMethodCount;
+    }
+
+    if (poolSize == 0) {
+        LOGVV("INTF: didn't find any new interfaces with methods");
+        result = true;
+        goto bail;
+    }
+
+    clazz->ifviPoolCount = poolSize;
+    clazz->ifviPool = (int*) dvmLinearAlloc(clazz->classLoader,
+                        poolSize * sizeof(int*));
+    zapIfvipool = true;
+
+    /*
+     * Fill in the vtable offsets for the interfaces that weren't part of
+     * our superclass.
+     */
+    for (int i = superIfCount; i < ifCount; i++) {
+        ClassObject* interface;
+        int methIdx;
+
+        clazz->iftable[i].methodIndexArray = clazz->ifviPool + poolOffset;
+        interface = clazz->iftable[i].clazz;
+        poolOffset += interface->virtualMethodCount;    // end here
+
+        /*
+         * For each method listed in the interface's method list, find the
+         * matching method in our class's method list.  We want to favor the
+         * subclass over the superclass, which just requires walking
+         * back from the end of the vtable.  (This only matters if the
+         * superclass defines a private method and this class redefines
+         * it -- otherwise it would use the same vtable slot.  In Dalvik
+         * those don't end up in the virtual method table, so it shouldn't
+         * matter which direction we go.  We walk it backward anyway.)
+         *
+         *
+         * Suppose we have the following arrangement:
+         *   public interface MyInterface
+         *     public boolean inInterface();
+         *   public abstract class MirandaAbstract implements MirandaInterface
+         *     //public abstract boolean inInterface(); // not declared!
+         *     public boolean inAbstract() { stuff }    // in vtable
+         *   public class MirandClass extends MirandaAbstract
+         *     public boolean inInterface() { stuff }
+         *     public boolean inAbstract() { stuff }    // in vtable
+         *
+         * The javac compiler happily compiles MirandaAbstract even though
+         * it doesn't declare all methods from its interface.  When we try
+         * to set up a vtable for MirandaAbstract, we find that we don't
+         * have an slot for inInterface.  To prevent this, we synthesize
+         * abstract method declarations in MirandaAbstract.
+         *
+         * We have to expand vtable and update some things that point at it,
+         * so we accumulate the method list and do it all at once below.
+         */
+        for (methIdx = 0; methIdx < interface->virtualMethodCount; methIdx++) {
+            Method* imeth = &interface->virtualMethods[methIdx];
+            int j;
+
+            IF_LOGVV() {
+                char* desc = dexProtoCopyMethodDescriptor(&imeth->prototype);
+                LOGVV("INTF:  matching '%s' '%s'", imeth->name, desc);
+                free(desc);
+            }
+
+            for (j = clazz->vtableCount-1; j >= 0; j--) {
+                if (dvmCompareMethodNamesAndProtos(imeth, clazz->vtable[j])
+                    == 0)
+                {
+                    LOGVV("INTF:   matched at %d", j);
+                    if (!dvmIsAbstractMethod(clazz->vtable[j]) &&
+                        !dvmIsPublicMethod(clazz->vtable[j]))
+                    {
+                        ALOGW("Implementation of %s.%s is not public",
+                            clazz->descriptor, clazz->vtable[j]->name);
+                        dvmThrowIllegalAccessError(
+                            "interface implementation not public");
+                        goto bail;
+                    }
+                    clazz->iftable[i].methodIndexArray[methIdx] = j;
+                    break;
+                }
+            }
+            if (j < 0) {
+                IF_ALOGV() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&imeth->prototype);
+                    ALOGV("No match for '%s' '%s' in '%s' (creating miranda)",
+                            imeth->name, desc, clazz->descriptor);
+                    free(desc);
+                }
+                //dvmThrowRuntimeException("Miranda!");
+                //return false;
+
+                if (mirandaCount == mirandaAlloc) {
+                    mirandaAlloc += 8;
+                    if (mirandaList == NULL) {
+                        mirandaList = (Method**)dvmLinearAlloc(
+                                        clazz->classLoader,
+                                        mirandaAlloc * sizeof(Method*));
+                    } else {
+                        dvmLinearReadOnly(clazz->classLoader, mirandaList);
+                        mirandaList = (Method**)dvmLinearRealloc(
+                                clazz->classLoader,
+                                mirandaList, mirandaAlloc * sizeof(Method*));
+                    }
+                    assert(mirandaList != NULL);    // mem failed + we leaked
+                }
+
+                /*
+                 * These may be redundant (e.g. method with same name and
+                 * signature declared in two interfaces implemented by the
+                 * same abstract class).  We can squeeze the duplicates
+                 * out here.
+                 */
+                int mir;
+                for (mir = 0; mir < mirandaCount; mir++) {
+                    if (dvmCompareMethodNamesAndProtos(
+                            mirandaList[mir], imeth) == 0)
+                    {
+                        IF_LOGVV() {
+                            char* desc = dexProtoCopyMethodDescriptor(
+                                    &imeth->prototype);
+                            LOGVV("MIRANDA dupe: %s and %s %s%s",
+                                mirandaList[mir]->clazz->descriptor,
+                                imeth->clazz->descriptor,
+                                imeth->name, desc);
+                            free(desc);
+                        }
+                        break;
+                    }
+                }
+
+                /* point the iftable at a phantom slot index */
+                clazz->iftable[i].methodIndexArray[methIdx] =
+                    clazz->vtableCount + mir;
+                LOGVV("MIRANDA: %s points at slot %d",
+                    imeth->name, clazz->vtableCount + mir);
+
+                /* if non-duplicate among Mirandas, add to Miranda list */
+                if (mir == mirandaCount) {
+                    //ALOGV("MIRANDA: holding '%s' in slot %d",
+                    //    imeth->name, mir);
+                    mirandaList[mirandaCount++] = imeth;
+                }
+            }
+        }
+    }
+
+    if (mirandaCount != 0) {
+        static const int kManyMirandas = 150;   /* arbitrary */
+        Method* newVirtualMethods;
+        Method* meth;
+        int oldMethodCount, oldVtableCount;
+
+        for (int i = 0; i < mirandaCount; i++) {
+            LOGVV("MIRANDA %d: %s.%s", i,
+                mirandaList[i]->clazz->descriptor, mirandaList[i]->name);
+        }
+        if (mirandaCount > kManyMirandas) {
+            /*
+             * Some obfuscators like to create an interface with a huge
+             * pile of methods, declare classes as implementing it, and then
+             * only define a couple of methods.  This leads to a rather
+             * massive collection of Miranda methods and a lot of wasted
+             * space, sometimes enough to blow out the LinearAlloc cap.
+             */
+            ALOGD("Note: class %s has %d unimplemented (abstract) methods",
+                clazz->descriptor, mirandaCount);
+        }
+
+        /*
+         * We found methods in one or more interfaces for which we do not
+         * have vtable entries.  We have to expand our virtualMethods
+         * table (which might be empty) to hold some new entries.
+         */
+        if (clazz->virtualMethods == NULL) {
+            newVirtualMethods = (Method*) dvmLinearAlloc(clazz->classLoader,
+                sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+        } else {
+            //dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+            newVirtualMethods = (Method*) dvmLinearRealloc(clazz->classLoader,
+                clazz->virtualMethods,
+                sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+        }
+        if (newVirtualMethods != clazz->virtualMethods) {
+            /*
+             * Table was moved in memory.  We have to run through the
+             * vtable and fix the pointers.  The vtable entries might be
+             * pointing at superclasses, so we flip it around: run through
+             * all locally-defined virtual methods, and fix their entries
+             * in the vtable.  (This would get really messy if sub-classes
+             * had already been loaded.)
+             *
+             * Reminder: clazz->virtualMethods and clazz->virtualMethodCount
+             * hold the virtual methods declared by this class.  The
+             * method's methodIndex is the vtable index, and is the same
+             * for all sub-classes (and all super classes in which it is
+             * defined).  We're messing with these because the Miranda
+             * stuff makes it look like the class actually has an abstract
+             * method declaration in it.
+             */
+            LOGVV("MIRANDA fixing vtable pointers");
+            dvmLinearReadWrite(clazz->classLoader, clazz->vtable);
+            Method* meth = newVirtualMethods;
+            for (int i = 0; i < clazz->virtualMethodCount; i++, meth++)
+                clazz->vtable[meth->methodIndex] = meth;
+            dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+        }
+
+        oldMethodCount = clazz->virtualMethodCount;
+        clazz->virtualMethods = newVirtualMethods;
+        clazz->virtualMethodCount += mirandaCount;
+
+        dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+        /*
+         * We also have to expand the vtable.
+         */
+        assert(clazz->vtable != NULL);
+        clazz->vtable = (Method**) dvmLinearRealloc(clazz->classLoader,
+                        clazz->vtable,
+                        sizeof(Method*) * (clazz->vtableCount + mirandaCount));
+        if (clazz->vtable == NULL) {
+            assert(false);
+            goto bail;
+        }
+        zapVtable = true;
+
+        oldVtableCount = clazz->vtableCount;
+        clazz->vtableCount += mirandaCount;
+
+        /*
+         * Now we need to create the fake methods.  We clone the abstract
+         * method definition from the interface and then replace a few
+         * things.
+         *
+         * The Method will be an "abstract native", with nativeFunc set to
+         * dvmAbstractMethodStub().
+         */
+        meth = clazz->virtualMethods + oldMethodCount;
+        for (int i = 0; i < mirandaCount; i++, meth++) {
+            dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+            cloneMethod(meth, mirandaList[i]);
+            meth->clazz = clazz;
+            meth->accessFlags |= ACC_MIRANDA;
+            meth->methodIndex = (u2) (oldVtableCount + i);
+            dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+            /* point the new vtable entry at the new method */
+            clazz->vtable[oldVtableCount + i] = meth;
+        }
+
+        dvmLinearReadOnly(clazz->classLoader, mirandaList);
+        dvmLinearFree(clazz->classLoader, mirandaList);
+
+    }
+
+    /*
+     * TODO?
+     * Sort the interfaces by number of declared methods.  All we really
+     * want is to get the interfaces with zero methods at the end of the
+     * list, so that when we walk through the list during invoke-interface
+     * we don't examine interfaces that can't possibly be useful.
+     *
+     * The set will usually be small, so a simple insertion sort works.
+     *
+     * We have to be careful not to change the order of two interfaces
+     * that define the same method.  (Not a problem if we only move the
+     * zero-method interfaces to the end.)
+     *
+     * PROBLEM:
+     * If we do this, we will no longer be able to identify super vs.
+     * current class interfaces by comparing clazz->super->iftableCount.  This
+     * breaks anything that only wants to find interfaces declared directly
+     * by the class (dvmFindStaticFieldHier, ReferenceType.Interfaces,
+     * dvmDbgOutputAllInterfaces, etc).  Need to provide a workaround.
+     *
+     * We can sort just the interfaces implemented directly by this class,
+     * but that doesn't seem like it would provide much of an advantage.  I'm
+     * not sure this is worthwhile.
+     *
+     * (This has been made largely obsolete by the interface cache mechanism.)
+     */
+
+    //dvmDumpClass(clazz);
+
+    result = true;
+
+bail:
+    if (zapIftable)
+        dvmLinearReadOnly(clazz->classLoader, clazz->iftable);
+    if (zapVtable)
+        dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+    if (zapIfvipool)
+        dvmLinearReadOnly(clazz->classLoader, clazz->ifviPool);
+    return result;
+}
+
+
+/*
+ * Provide "stub" implementations for methods without them.
+ *
+ * Currently we provide an implementation for all abstract methods that
+ * throws an AbstractMethodError exception.  This allows us to avoid an
+ * explicit check for abstract methods in every virtual call.
+ *
+ * NOTE: for Miranda methods, the method declaration is a clone of what
+ * was found in the interface class.  That copy may already have had the
+ * function pointer filled in, so don't be surprised if it's not NULL.
+ *
+ * NOTE: this sets the "native" flag, giving us an "abstract native" method,
+ * which is nonsensical.  Need to make sure that this doesn't escape the
+ * VM.  We can either mask it out in reflection calls, or copy "native"
+ * into the high 16 bits of accessFlags and check that internally.
+ */
+static bool insertMethodStubs(ClassObject* clazz)
+{
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+    Method* meth;
+    int i;
+
+    meth = clazz->virtualMethods;
+    for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if (dvmIsAbstractMethod(meth)) {
+            assert(meth->insns == NULL);
+            assert(meth->nativeFunc == NULL ||
+                meth->nativeFunc == (DalvikBridgeFunc)dvmAbstractMethodStub);
+
+            meth->accessFlags |= ACC_NATIVE;
+            meth->nativeFunc = (DalvikBridgeFunc) dvmAbstractMethodStub;
+        }
+    }
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    return true;
+}
+
+
+/*
+ * Swap two instance fields.
+ */
+static inline void swapField(InstField* pOne, InstField* pTwo)
+{
+    InstField swap;
+
+    LOGVV("  --- swap '%s' and '%s'", pOne->name, pTwo->name);
+    swap = *pOne;
+    *pOne = *pTwo;
+    *pTwo = swap;
+}
+
+/*
+ * Assign instance fields to u4 slots.
+ *
+ * The top portion of the instance field area is occupied by the superclass
+ * fields, the bottom by the fields for this class.
+ *
+ * "long" and "double" fields occupy two adjacent slots.  On some
+ * architectures, 64-bit quantities must be 64-bit aligned, so we need to
+ * arrange fields (or introduce padding) to ensure this.  We assume the
+ * fields of the topmost superclass (i.e. Object) are 64-bit aligned, so
+ * we can just ensure that the offset is "even".  To avoid wasting space,
+ * we want to move non-reference 32-bit fields into gaps rather than
+ * creating pad words.
+ *
+ * In the worst case we will waste 4 bytes, but because objects are
+ * allocated on >= 64-bit boundaries, those bytes may well be wasted anyway
+ * (assuming this is the most-derived class).
+ *
+ * Pad words are not represented in the field table, so the field table
+ * itself does not change size.
+ *
+ * The number of field slots determines the size of the object, so we
+ * set that here too.
+ *
+ * This function feels a little more complicated than I'd like, but it
+ * has the property of moving the smallest possible set of fields, which
+ * should reduce the time required to load a class.
+ *
+ * NOTE: reference fields *must* come first, or precacheReferenceOffsets()
+ * will break.
+ */
+static bool computeFieldOffsets(ClassObject* clazz)
+{
+    int fieldOffset;
+    int i, j;
+
+    dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+
+    if (clazz->super != NULL)
+        fieldOffset = clazz->super->objectSize;
+    else
+        fieldOffset = OFFSETOF_MEMBER(DataObject, instanceData);
+
+    LOGVV("--- computeFieldOffsets '%s'", clazz->descriptor);
+
+    //ALOGI("OFFSETS fieldCount=%d", clazz->ifieldCount);
+    //ALOGI("dataobj, instance: %d", offsetof(DataObject, instanceData));
+    //ALOGI("classobj, access: %d", offsetof(ClassObject, accessFlags));
+    //ALOGI("super=%p, fieldOffset=%d", clazz->super, fieldOffset);
+
+    /*
+     * Start by moving all reference fields to the front.
+     */
+    clazz->ifieldRefCount = 0;
+    j = clazz->ifieldCount - 1;
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != '[' && c != 'L') {
+            /* This isn't a reference field; see if any reference fields
+             * follow this one.  If so, we'll move it to this position.
+             * (quicksort-style partitioning)
+             */
+            while (j > i) {
+                InstField* refField = &clazz->ifields[j--];
+                char rc = refField->signature[0];
+
+                if (rc == '[' || rc == 'L') {
+                    /* Here's a reference field that follows at least one
+                     * non-reference field.  Swap it with the current field.
+                     * (When this returns, "pField" points to the reference
+                     * field, and "refField" points to the non-ref field.)
+                     */
+                    swapField(pField, refField);
+
+                    /* Fix the signature.
+                     */
+                    c = rc;
+
+                    clazz->ifieldRefCount++;
+                    break;
+                }
+            }
+            /* We may or may not have swapped a field.
+             */
+        } else {
+            /* This is a reference field.
+             */
+            clazz->ifieldRefCount++;
+        }
+
+        /*
+         * If we've hit the end of the reference fields, break.
+         */
+        if (c != '[' && c != 'L')
+            break;
+
+        pField->byteOffset = fieldOffset;
+        fieldOffset += sizeof(u4);
+        LOGVV("  --- offset1 '%s'=%d", pField->name,pField->byteOffset);
+    }
+
+    /*
+     * Now we want to pack all of the double-wide fields together.  If we're
+     * not aligned, though, we want to shuffle one 32-bit field into place.
+     * If we can't find one, we'll have to pad it.
+     */
+    if (i != clazz->ifieldCount && (fieldOffset & 0x04) != 0) {
+        LOGVV("  +++ not aligned");
+
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != 'J' && c != 'D') {
+            /*
+             * The field that comes next is 32-bit, so just advance past it.
+             */
+            assert(c != '[' && c != 'L');
+            pField->byteOffset = fieldOffset;
+            fieldOffset += sizeof(u4);
+            i++;
+            LOGVV("  --- offset2 '%s'=%d",
+                pField->name, pField->byteOffset);
+        } else {
+            /*
+             * Next field is 64-bit, so search for a 32-bit field we can
+             * swap into it.
+             */
+            bool found = false;
+            j = clazz->ifieldCount - 1;
+            while (j > i) {
+                InstField* singleField = &clazz->ifields[j--];
+                char rc = singleField->signature[0];
+
+                if (rc != 'J' && rc != 'D') {
+                    swapField(pField, singleField);
+                    //c = rc;
+                    LOGVV("  +++ swapped '%s' for alignment",
+                        pField->name);
+                    pField->byteOffset = fieldOffset;
+                    fieldOffset += sizeof(u4);
+                    LOGVV("  --- offset3 '%s'=%d",
+                        pField->name, pField->byteOffset);
+                    found = true;
+                    i++;
+                    break;
+                }
+            }
+            if (!found) {
+                ALOGV("  +++ inserting pad field in '%s'", clazz->descriptor);
+                fieldOffset += sizeof(u4);
+            }
+        }
+    }
+
+    /*
+     * Alignment is good, shuffle any double-wide fields forward, and
+     * finish assigning field offsets to all fields.
+     */
+    assert(i == clazz->ifieldCount || (fieldOffset & 0x04) == 0);
+    j = clazz->ifieldCount - 1;
+    for ( ; i < clazz->ifieldCount; i++) {
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c != 'D' && c != 'J') {
+            /* This isn't a double-wide field; see if any double fields
+             * follow this one.  If so, we'll move it to this position.
+             * (quicksort-style partitioning)
+             */
+            while (j > i) {
+                InstField* doubleField = &clazz->ifields[j--];
+                char rc = doubleField->signature[0];
+
+                if (rc == 'D' || rc == 'J') {
+                    /* Here's a double-wide field that follows at least one
+                     * non-double field.  Swap it with the current field.
+                     * (When this returns, "pField" points to the reference
+                     * field, and "doubleField" points to the non-double field.)
+                     */
+                    swapField(pField, doubleField);
+                    c = rc;
+
+                    break;
+                }
+            }
+            /* We may or may not have swapped a field.
+             */
+        } else {
+            /* This is a double-wide field, leave it be.
+             */
+        }
+
+        pField->byteOffset = fieldOffset;
+        LOGVV("  --- offset4 '%s'=%d", pField->name,pField->byteOffset);
+        fieldOffset += sizeof(u4);
+        if (c == 'J' || c == 'D')
+            fieldOffset += sizeof(u4);
+    }
+
+#ifndef NDEBUG
+    /* Make sure that all reference fields appear before
+     * non-reference fields, and all double-wide fields are aligned.
+     */
+    j = 0;  // seen non-ref
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        InstField *pField = &clazz->ifields[i];
+        char c = pField->signature[0];
+
+        if (c == 'D' || c == 'J') {
+            assert((pField->byteOffset & 0x07) == 0);
+        }
+
+        if (c != '[' && c != 'L') {
+            if (!j) {
+                assert(i == clazz->ifieldRefCount);
+                j = 1;
+            }
+        } else if (j) {
+            assert(false);
+        }
+    }
+    if (!j) {
+        assert(clazz->ifieldRefCount == clazz->ifieldCount);
+    }
+#endif
+
+    /*
+     * We map a C struct directly on top of java/lang/Class objects.  Make
+     * sure we left enough room for the instance fields.
+     */
+    assert(!dvmIsTheClassClass(clazz) || (size_t)fieldOffset <
+        OFFSETOF_MEMBER(ClassObject, instanceData) + sizeof(clazz->instanceData));
+
+    clazz->objectSize = fieldOffset;
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+    return true;
+}
+
+/*
+ * The class failed to initialize on a previous attempt, so we want to throw
+ * a NoClassDefFoundError (v2 2.17.5).  The exception to this rule is if we
+ * failed in verification, in which case v2 5.4.1 says we need to re-throw
+ * the previous error.
+ */
+static void throwEarlierClassFailure(ClassObject* clazz)
+{
+    ALOGI("Rejecting re-init on previously-failed class %s v=%p",
+        clazz->descriptor, clazz->verifyErrorClass);
+
+    if (clazz->verifyErrorClass == NULL) {
+        dvmThrowNoClassDefFoundError(clazz->descriptor);
+    } else {
+        dvmThrowExceptionWithClassMessage(clazz->verifyErrorClass,
+            clazz->descriptor);
+    }
+}
+
+/*
+ * Initialize any static fields whose values are stored in
+ * the DEX file.  This must be done during class initialization.
+ */
+static void initSFields(ClassObject* clazz)
+{
+    Thread* self = dvmThreadSelf(); /* for dvmReleaseTrackedAlloc() */
+    DexFile* pDexFile;
+    const DexClassDef* pClassDef;
+    const DexEncodedArray* pValueList;
+    EncodedArrayIterator iterator;
+    int i;
+
+    if (clazz->sfieldCount == 0) {
+        return;
+    }
+    if (clazz->pDvmDex == NULL) {
+        /* generated class; any static fields should already be set up */
+        ALOGV("Not initializing static fields in %s", clazz->descriptor);
+        return;
+    }
+    pDexFile = clazz->pDvmDex->pDexFile;
+
+    pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+    assert(pClassDef != NULL);
+
+    pValueList = dexGetStaticValuesList(pDexFile, pClassDef);
+    if (pValueList == NULL) {
+        return;
+    }
+
+    dvmEncodedArrayIteratorInitialize(&iterator, pValueList, clazz);
+
+    /*
+     * Iterate over the initial values array, setting the corresponding
+     * static field for each array element.
+     */
+
+    for (i = 0; dvmEncodedArrayIteratorHasNext(&iterator); i++) {
+        AnnotationValue value;
+        bool parsed = dvmEncodedArrayIteratorGetNext(&iterator, &value);
+        StaticField* sfield = &clazz->sfields[i];
+        const char* descriptor = sfield->signature;
+        bool isObj = false;
+
+        if (! parsed) {
+            /*
+             * TODO: Eventually verification should attempt to ensure
+             * that this can't happen at least due to a data integrity
+             * problem.
+             */
+            ALOGE("Static initializer parse failed for %s at index %d",
+                    clazz->descriptor, i);
+            dvmAbort();
+        }
+
+        /* Verify that the value we got was of a valid type. */
+
+        switch (descriptor[0]) {
+            case 'Z': parsed = (value.type == kDexAnnotationBoolean); break;
+            case 'B': parsed = (value.type == kDexAnnotationByte);    break;
+            case 'C': parsed = (value.type == kDexAnnotationChar);    break;
+            case 'S': parsed = (value.type == kDexAnnotationShort);   break;
+            case 'I': parsed = (value.type == kDexAnnotationInt);     break;
+            case 'J': parsed = (value.type == kDexAnnotationLong);    break;
+            case 'F': parsed = (value.type == kDexAnnotationFloat);   break;
+            case 'D': parsed = (value.type == kDexAnnotationDouble);  break;
+            case '[': parsed = (value.type == kDexAnnotationNull);    break;
+            case 'L': {
+                switch (value.type) {
+                    case kDexAnnotationNull: {
+                        /* No need for further tests. */
+                        break;
+                    }
+                    case kDexAnnotationString: {
+                        parsed =
+                            (strcmp(descriptor, "Ljava/lang/String;") == 0);
+                        isObj = true;
+                        break;
+                    }
+                    case kDexAnnotationType: {
+                        parsed =
+                            (strcmp(descriptor, "Ljava/lang/Class;") == 0);
+                        isObj = true;
+                        break;
+                    }
+                    default: {
+                        parsed = false;
+                        break;
+                    }
+                }
+                break;
+            }
+            default: {
+                parsed = false;
+                break;
+            }
+        }
+
+        if (parsed) {
+            /*
+             * All's well, so store the value.
+             */
+            if (isObj) {
+                dvmSetStaticFieldObject(sfield, (Object*)value.value.l);
+                dvmReleaseTrackedAlloc((Object*)value.value.l, self);
+            } else {
+                /*
+                 * Note: This always stores the full width of a
+                 * JValue, even though most of the time only the first
+                 * word is needed.
+                 */
+                sfield->value = value.value;
+            }
+        } else {
+            /*
+             * Something up above had a problem. TODO: See comment
+             * above the switch about verfication.
+             */
+            ALOGE("Bogus static initialization: value type %d in field type "
+                    "%s for %s at index %d",
+                value.type, descriptor, clazz->descriptor, i);
+            dvmAbort();
+        }
+    }
+}
+
+
+/*
+ * Determine whether "descriptor" yields the same class object in the
+ * context of clazz1 and clazz2.
+ *
+ * The caller must hold gDvm.loadedClasses.
+ *
+ * Returns "true" if they match.
+ */
+static bool compareDescriptorClasses(const char* descriptor,
+    const ClassObject* clazz1, const ClassObject* clazz2)
+{
+    ClassObject* result1;
+    ClassObject* result2;
+
+    /*
+     * Do the first lookup by name.
+     */
+    result1 = dvmFindClassNoInit(descriptor, clazz1->classLoader);
+
+    /*
+     * We can skip a second lookup by name if the second class loader is
+     * in the initiating loader list of the class object we retrieved.
+     * (This means that somebody already did a lookup of this class through
+     * the second loader, and it resolved to the same class.)  If it's not
+     * there, we may simply not have had an opportunity to add it yet, so
+     * we do the full lookup.
+     *
+     * The initiating loader test should catch the majority of cases
+     * (in particular, the zillions of references to String/Object).
+     *
+     * Unfortunately we're still stuck grabbing a mutex to do the lookup.
+     *
+     * For this to work, the superclass/interface should be the first
+     * argument, so that way if it's from the bootstrap loader this test
+     * will work.  (The bootstrap loader, by definition, never shows up
+     * as the initiating loader of a class defined by some other loader.)
+     */
+    dvmHashTableLock(gDvm.loadedClasses);
+    bool isInit = dvmLoaderInInitiatingList(result1, clazz2->classLoader);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    if (isInit) {
+        //printf("%s(obj=%p) / %s(cl=%p): initiating\n",
+        //    result1->descriptor, result1,
+        //    clazz2->descriptor, clazz2->classLoader);
+        return true;
+    } else {
+        //printf("%s(obj=%p) / %s(cl=%p): RAW\n",
+        //    result1->descriptor, result1,
+        //    clazz2->descriptor, clazz2->classLoader);
+        result2 = dvmFindClassNoInit(descriptor, clazz2->classLoader);
+    }
+
+    if (result1 == NULL || result2 == NULL) {
+        dvmClearException(dvmThreadSelf());
+        if (result1 == result2) {
+            /*
+             * Neither class loader could find this class.  Apparently it
+             * doesn't exist.
+             *
+             * We can either throw some sort of exception now, or just
+             * assume that it'll fail later when something actually tries
+             * to use the class.  For strict handling we should throw now,
+             * because a "tricky" class loader could start returning
+             * something later, and a pair of "tricky" loaders could set
+             * us up for confusion.
+             *
+             * I'm not sure if we're allowed to complain about nonexistent
+             * classes in method signatures during class init, so for now
+             * this will just return "true" and let nature take its course.
+             */
+            return true;
+        } else {
+            /* only one was found, so clearly they're not the same */
+            return false;
+        }
+    }
+
+    return result1 == result2;
+}
+
+/*
+ * For every component in the method descriptor, resolve the class in the
+ * context of the two classes and compare the results.
+ *
+ * For best results, the "superclass" class should be first.
+ *
+ * Returns "true" if the classes match, "false" otherwise.
+ */
+static bool checkMethodDescriptorClasses(const Method* meth,
+    const ClassObject* clazz1, const ClassObject* clazz2)
+{
+    DexParameterIterator iterator;
+    const char* descriptor;
+
+    /* walk through the list of parameters */
+    dexParameterIteratorInit(&iterator, &meth->prototype);
+    while (true) {
+        descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (descriptor == NULL)
+            break;
+
+        if (descriptor[0] == 'L' || descriptor[0] == '[') {
+            /* non-primitive type */
+            if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+                return false;
+        }
+    }
+
+    /* check the return type */
+    descriptor = dexProtoGetReturnType(&meth->prototype);
+    if (descriptor[0] == 'L' || descriptor[0] == '[') {
+        if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+            return false;
+    }
+    return true;
+}
+
+/*
+ * Validate the descriptors in the superclass and interfaces.
+ *
+ * What we need to do is ensure that the classes named in the method
+ * descriptors in our ancestors and ourselves resolve to the same class
+ * objects.  We can get conflicts when the classes come from different
+ * class loaders, and the resolver comes up with different results for
+ * the same class name in different contexts.
+ *
+ * An easy way to cause the problem is to declare a base class that uses
+ * class Foo in a method signature (e.g. as the return type).  Then,
+ * define a subclass and a different version of Foo, and load them from a
+ * different class loader.  If the subclass overrides the method, it will
+ * have a different concept of what Foo is than its parent does, so even
+ * though the method signature strings are identical, they actually mean
+ * different things.
+ *
+ * A call to the method through a base-class reference would be treated
+ * differently than a call to the method through a subclass reference, which
+ * isn't the way polymorphism works, so we have to reject the subclass.
+ * If the subclass doesn't override the base method, then there's no
+ * problem, because calls through base-class references and subclass
+ * references end up in the same place.
+ *
+ * We don't need to check to see if an interface's methods match with its
+ * superinterface's methods, because you can't instantiate an interface
+ * and do something inappropriate with it.  If interface I1 extends I2
+ * and is implemented by C, and I1 and I2 are in separate class loaders
+ * and have conflicting views of other classes, we will catch the conflict
+ * when we process C.  Anything that implements I1 is doomed to failure,
+ * but we don't need to catch that while processing I1.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+static bool validateSuperDescriptors(const ClassObject* clazz)
+{
+    int i;
+
+    if (dvmIsInterfaceClass(clazz))
+        return true;
+
+    /*
+     * Start with the superclass-declared methods.
+     */
+    if (clazz->super != NULL &&
+        clazz->classLoader != clazz->super->classLoader)
+    {
+        /*
+         * Walk through every overridden method and compare resolved
+         * descriptor components.  We pull the Method structs out of
+         * the vtable.  It doesn't matter whether we get the struct from
+         * the parent or child, since we just need the UTF-8 descriptor,
+         * which must match.
+         *
+         * We need to do this even for the stuff inherited from Object,
+         * because it's possible that the new class loader has redefined
+         * a basic class like String.
+         *
+         * We don't need to check stuff defined in a superclass because
+         * it was checked when the superclass was loaded.
+         */
+        const Method* meth;
+
+        //printf("Checking %s %p vs %s %p\n",
+        //    clazz->descriptor, clazz->classLoader,
+        //    clazz->super->descriptor, clazz->super->classLoader);
+        for (i = clazz->super->vtableCount - 1; i >= 0; i--) {
+            meth = clazz->vtable[i];
+            if (meth != clazz->super->vtable[i] &&
+                !checkMethodDescriptorClasses(meth, clazz->super, clazz))
+            {
+                ALOGW("Method mismatch: %s in %s (cl=%p) and super %s (cl=%p)",
+                    meth->name, clazz->descriptor, clazz->classLoader,
+                    clazz->super->descriptor, clazz->super->classLoader);
+                dvmThrowLinkageError(
+                    "Classes resolve differently in superclass");
+                return false;
+            }
+        }
+    }
+
+    /*
+     * Check the methods defined by this class against the interfaces it
+     * implements.  If we inherited the implementation from a superclass,
+     * we have to check it against the superclass (which might be in a
+     * different class loader).  If the superclass also implements the
+     * interface, we could skip the check since by definition it was
+     * performed when the class was loaded.
+     */
+    for (i = 0; i < clazz->iftableCount; i++) {
+        const InterfaceEntry* iftable = &clazz->iftable[i];
+
+        if (clazz->classLoader != iftable->clazz->classLoader) {
+            const ClassObject* iface = iftable->clazz;
+            int j;
+
+            for (j = 0; j < iface->virtualMethodCount; j++) {
+                const Method* meth;
+                int vtableIndex;
+
+                vtableIndex = iftable->methodIndexArray[j];
+                meth = clazz->vtable[vtableIndex];
+
+                if (!checkMethodDescriptorClasses(meth, iface, meth->clazz)) {
+                    ALOGW("Method mismatch: %s in %s (cl=%p) and "
+                            "iface %s (cl=%p)",
+                        meth->name, clazz->descriptor, clazz->classLoader,
+                        iface->descriptor, iface->classLoader);
+                    dvmThrowLinkageError(
+                        "Classes resolve differently in interface");
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Returns true if the class is being initialized by us (which means that
+ * calling dvmInitClass will return immediately after fiddling with locks).
+ * Returns false if it's not being initialized, or if it's being
+ * initialized by another thread.
+ *
+ * The value for initThreadId is always set to "self->threadId", by the
+ * thread doing the initializing.  If it was done by the current thread,
+ * we are guaranteed to see "initializing" and our thread ID, even on SMP.
+ * If it was done by another thread, the only bad situation is one in
+ * which we see "initializing" and a stale copy of our own thread ID
+ * while another thread is actually handling init.
+ *
+ * The initThreadId field is used during class linking, so it *is*
+ * possible to have a stale value floating around.  We need to ensure
+ * that memory accesses happen in the correct order.
+ */
+bool dvmIsClassInitializing(const ClassObject* clazz)
+{
+    const int32_t* addr = (const int32_t*)(const void*)&clazz->status;
+    int32_t value = android_atomic_acquire_load(addr);
+    ClassStatus status = static_cast<ClassStatus>(value);
+    return (status == CLASS_INITIALIZING &&
+            clazz->initThreadId == dvmThreadSelf()->threadId);
+}
+
+/*
+ * If a class has not been initialized, do so by executing the code in
+ * <clinit>.  The sequence is described in the VM spec v2 2.17.5.
+ *
+ * It is possible for multiple threads to arrive here simultaneously, so
+ * we need to lock the class while we check stuff.  We know that no
+ * interpreted code has access to the class yet, so we can use the class's
+ * monitor lock.
+ *
+ * We will often be called recursively, e.g. when the <clinit> code resolves
+ * one of its fields, the field resolution will try to initialize the class.
+ * In that case we will return "true" even though the class isn't actually
+ * ready to go.  The ambiguity can be resolved with dvmIsClassInitializing().
+ * (TODO: consider having this return an enum to avoid the extra call --
+ * return -1 on failure, 0 on success, 1 on still-initializing.  Looks like
+ * dvmIsClassInitializing() is always paired with *Initialized())
+ *
+ * This can get very interesting if a class has a static field initialized
+ * to a new instance of itself.  <clinit> will end up calling <init> on
+ * the members it is initializing, which is fine unless it uses the contents
+ * of static fields to initialize instance fields.  This will leave the
+ * static-referenced objects in a partially initialized state.  This is
+ * reasonably rare and can sometimes be cured with proper field ordering.
+ *
+ * On failure, returns "false" with an exception raised.
+ *
+ * -----
+ *
+ * It is possible to cause a deadlock by having a situation like this:
+ *   class A { static { sleep(10000); new B(); } }
+ *   class B { static { sleep(10000); new A(); } }
+ *   new Thread() { public void run() { new A(); } }.start();
+ *   new Thread() { public void run() { new B(); } }.start();
+ * This appears to be expected under the spec.
+ *
+ * The interesting question is what to do if somebody calls Thread.interrupt()
+ * on one of the deadlocked threads.  According to the VM spec, they're both
+ * sitting in "wait".  Should the interrupt code quietly raise the
+ * "interrupted" flag, or should the "wait" return immediately with an
+ * exception raised?
+ *
+ * This gets a little murky.  The VM spec says we call "wait", and the
+ * spec for Thread.interrupt says Object.wait is interruptible.  So it
+ * seems that, if we get unlucky and interrupt class initialization, we
+ * are expected to throw (which gets converted to ExceptionInInitializerError
+ * since InterruptedException is checked).
+ *
+ * There are a couple of problems here.  First, all threads are expected to
+ * present a consistent view of class initialization, so we can't have it
+ * fail in one thread and succeed in another.  Second, once a class fails
+ * to initialize, it must *always* fail.  This means that a stray interrupt()
+ * call could render a class unusable for the lifetime of the VM.
+ *
+ * In most cases -- the deadlock example above being a counter-example --
+ * the interrupting thread can't tell whether the target thread handled
+ * the initialization itself or had to wait while another thread did the
+ * work.  Refusing to interrupt class initialization is, in most cases,
+ * not something that a program can reliably detect.
+ *
+ * On the assumption that interrupting class initialization is highly
+ * undesirable in most circumstances, and that failing to do so does not
+ * deviate from the spec in a meaningful way, we don't allow class init
+ * to be interrupted by Thread.interrupt().
+ */
+bool dvmInitClass(ClassObject* clazz)
+{
+    u8 startWhen = 0;
+
+#if LOG_CLASS_LOADING
+    bool initializedByUs = false;
+#endif
+
+    Thread* self = dvmThreadSelf();
+    const Method* method;
+
+    dvmLockObject(self, (Object*) clazz);
+    assert(dvmIsClassLinked(clazz) || clazz->status == CLASS_ERROR);
+
+    /*
+     * If the class hasn't been verified yet, do so now.
+     */
+    if (clazz->status < CLASS_VERIFIED) {
+        /*
+         * If we're in an "erroneous" state, throw an exception and bail.
+         */
+        if (clazz->status == CLASS_ERROR) {
+            throwEarlierClassFailure(clazz);
+            goto bail_unlock;
+        }
+
+        assert(clazz->status == CLASS_RESOLVED);
+        assert(!IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+
+        if (gDvm.classVerifyMode == VERIFY_MODE_NONE ||
+            (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
+             clazz->classLoader == NULL))
+        {
+            /* advance to "verified" state */
+            ALOGV("+++ not verifying class %s (cl=%p)",
+                clazz->descriptor, clazz->classLoader);
+            clazz->status = CLASS_VERIFIED;
+            goto noverify;
+        }
+
+        if (!gDvm.optimizing)
+            ALOGV("+++ late verify on %s", clazz->descriptor);
+
+        /*
+         * We're not supposed to optimize an unverified class, but during
+         * development this mode was useful.  We can't verify an optimized
+         * class because the optimization process discards information.
+         */
+        if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+            ALOGW("Class '%s' was optimized without verification; "
+                 "not verifying now",
+                clazz->descriptor);
+            ALOGW("  ('rm /data/dalvik-cache/*' and restart to fix this)");
+            goto verify_failed;
+        }
+
+        clazz->status = CLASS_VERIFYING;
+        if (!dvmVerifyClass(clazz)) {
+verify_failed:
+            dvmThrowVerifyError(clazz->descriptor);
+            dvmSetFieldObject((Object*) clazz,
+                OFFSETOF_MEMBER(ClassObject, verifyErrorClass),
+                (Object*) dvmGetException(self)->clazz);
+            clazz->status = CLASS_ERROR;
+            goto bail_unlock;
+        }
+
+        clazz->status = CLASS_VERIFIED;
+    }
+noverify:
+
+    /*
+     * We need to ensure that certain instructions, notably accesses to
+     * volatile fields, are replaced before any code is executed.  This
+     * must happen even if DEX optimizations are disabled.
+     *
+     * The only exception to this rule is that we don't want to do this
+     * during dexopt.  We don't generally initialize classes at all
+     * during dexopt, but because we're loading classes we need Class and
+     * Object (and possibly some Throwable stuff if a class isn't found).
+     * If optimizations are disabled, we don't want to output optimized
+     * instructions at this time.  This means we will be executing <clinit>
+     * code with un-fixed volatiles, but we're only doing it for a few
+     * system classes, and dexopt runs single-threaded.
+     */
+    if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED) && !gDvm.optimizing) {
+        ALOGV("+++ late optimize on %s (pv=%d)",
+            clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+        bool essentialOnly = (gDvm.dexOptMode != OPTIMIZE_MODE_FULL);
+        dvmOptimizeClass(clazz, essentialOnly);
+        SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED);
+    }
+
+    /* update instruction stream now that verification + optimization is done */
+    dvmFlushBreakpoints(clazz);
+
+    if (clazz->status == CLASS_INITIALIZED)
+        goto bail_unlock;
+
+    while (clazz->status == CLASS_INITIALIZING) {
+        /* we caught somebody else in the act; was it us? */
+        if (clazz->initThreadId == self->threadId) {
+            //ALOGV("HEY: found a recursive <clinit>");
+            goto bail_unlock;
+        }
+
+        if (dvmCheckException(self)) {
+            ALOGW("GLITCH: exception pending at start of class init");
+            dvmAbort();
+        }
+
+        /*
+         * Wait for the other thread to finish initialization.  We pass
+         * "false" for the "interruptShouldThrow" arg so it doesn't throw
+         * an exception on interrupt.
+         */
+        dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+
+        /*
+         * When we wake up, repeat the test for init-in-progress.  If there's
+         * an exception pending (only possible if "interruptShouldThrow"
+         * was set), bail out.
+         */
+        if (dvmCheckException(self)) {
+            ALOGI("Class init of '%s' failing with wait() exception",
+                clazz->descriptor);
+            /*
+             * TODO: this is bogus, because it means the two threads have a
+             * different idea of the class status.  We need to flag the
+             * class as bad and ensure that the initializer thread respects
+             * our notice.  If we get lucky and wake up after the class has
+             * finished initialization but before being woken, we have to
+             * swallow the exception, perhaps raising thread->interrupted
+             * to preserve semantics.
+             *
+             * Since we're not currently allowing interrupts, this should
+             * never happen and we don't need to fix this.
+             */
+            assert(false);
+            dvmThrowExceptionInInitializerError();
+            clazz->status = CLASS_ERROR;
+            goto bail_unlock;
+        }
+        if (clazz->status == CLASS_INITIALIZING) {
+            ALOGI("Waiting again for class init");
+            continue;
+        }
+        assert(clazz->status == CLASS_INITIALIZED ||
+               clazz->status == CLASS_ERROR);
+        if (clazz->status == CLASS_ERROR) {
+            /*
+             * The caller wants an exception, but it was thrown in a
+             * different thread.  Synthesize one here.
+             */
+            dvmThrowUnsatisfiedLinkError(
+                "(<clinit> failed, see exception in other thread)");
+        }
+        goto bail_unlock;
+    }
+
+    /* see if we failed previously */
+    if (clazz->status == CLASS_ERROR) {
+        // might be wise to unlock before throwing; depends on which class
+        // it is that we have locked
+        dvmUnlockObject(self, (Object*) clazz);
+        throwEarlierClassFailure(clazz);
+        return false;
+    }
+
+    if (gDvm.allocProf.enabled) {
+        startWhen = dvmGetRelativeTimeNsec();
+    }
+
+    /*
+     * We're ready to go, and have exclusive access to the class.
+     *
+     * Before we start initialization, we need to do one extra bit of
+     * validation: make sure that the methods declared here match up
+     * with our superclass and interfaces.  We know that the UTF-8
+     * descriptors match, but classes from different class loaders can
+     * have the same name.
+     *
+     * We do this now, rather than at load/link time, for the same reason
+     * that we defer verification.
+     *
+     * It's unfortunate that we need to do this at all, but we risk
+     * mixing reference types with identical names (see Dalvik test 068).
+     */
+    if (!validateSuperDescriptors(clazz)) {
+        assert(dvmCheckException(self));
+        clazz->status = CLASS_ERROR;
+        goto bail_unlock;
+    }
+
+    /*
+     * Let's initialize this thing.
+     *
+     * We unlock the object so that other threads can politely sleep on
+     * our mutex with Object.wait(), instead of hanging or spinning trying
+     * to grab our mutex.
+     */
+    assert(clazz->status < CLASS_INITIALIZING);
+
+#if LOG_CLASS_LOADING
+    // We started initializing.
+    logClassLoad('+', clazz);
+    initializedByUs = true;
+#endif
+
+    /* order matters here, esp. interaction with dvmIsClassInitializing */
+    clazz->initThreadId = self->threadId;
+    android_atomic_release_store(CLASS_INITIALIZING,
+                                 (int32_t*)(void*)&clazz->status);
+    dvmUnlockObject(self, (Object*) clazz);
+
+    /* init our superclass */
+    if (clazz->super != NULL && clazz->super->status != CLASS_INITIALIZED) {
+        assert(!dvmIsInterfaceClass(clazz));
+        if (!dvmInitClass(clazz->super)) {
+            assert(dvmCheckException(self));
+            clazz->status = CLASS_ERROR;
+            /* wake up anybody who started waiting while we were unlocked */
+            dvmLockObject(self, (Object*) clazz);
+            goto bail_notify;
+        }
+    }
+
+    /* Initialize any static fields whose values are
+     * stored in the Dex file.  This should include all of the
+     * simple "final static" fields, which are required to
+     * be initialized first. (vmspec 2 sec 2.17.5 item 8)
+     * More-complicated final static fields should be set
+     * at the beginning of <clinit>;  all we can do is trust
+     * that the compiler did the right thing.
+     */
+    initSFields(clazz);
+
+    /* Execute any static initialization code.
+     */
+    method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
+    if (method == NULL) {
+        LOGVV("No <clinit> found for %s", clazz->descriptor);
+    } else {
+        LOGVV("Invoking %s.<clinit>", clazz->descriptor);
+        JValue unused;
+        dvmCallMethod(self, method, NULL, &unused);
+    }
+
+    if (dvmCheckException(self)) {
+        /*
+         * We've had an exception thrown during static initialization.  We
+         * need to throw an ExceptionInInitializerError, but we want to
+         * tuck the original exception into the "cause" field.
+         */
+        ALOGW("Exception %s thrown while initializing %s",
+            (dvmGetException(self)->clazz)->descriptor, clazz->descriptor);
+        dvmThrowExceptionInInitializerError();
+        //ALOGW("+++ replaced");
+
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_ERROR;
+    } else {
+        /* success! */
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_INITIALIZED;
+        LOGVV("Initialized class: %s", clazz->descriptor);
+
+        /*
+         * Update alloc counters.  TODO: guard with mutex.
+         */
+        if (gDvm.allocProf.enabled && startWhen != 0) {
+            u8 initDuration = dvmGetRelativeTimeNsec() - startWhen;
+            gDvm.allocProf.classInitTime += initDuration;
+            self->allocProf.classInitTime += initDuration;
+            gDvm.allocProf.classInitCount++;
+            self->allocProf.classInitCount++;
+        }
+    }
+
+bail_notify:
+    /*
+     * Notify anybody waiting on the object.
+     */
+    dvmObjectNotifyAll(self, (Object*) clazz);
+
+bail_unlock:
+
+#if LOG_CLASS_LOADING
+    if (initializedByUs) {
+        // We finished initializing.
+        logClassLoad('-', clazz);
+    }
+#endif
+
+    dvmUnlockObject(self, (Object*) clazz);
+
+    return (clazz->status != CLASS_ERROR);
+}
+
+/*
+ * Replace method->nativeFunc and method->insns with new values.  This is
+ * commonly performed after successful resolution of a native method.
+ *
+ * There are three basic states:
+ *  (1) (initial) nativeFunc = dvmResolveNativeMethod, insns = NULL
+ *  (2) (internal native) nativeFunc = <impl>, insns = NULL
+ *  (3) (JNI) nativeFunc = JNI call bridge, insns = <impl>
+ *
+ * nativeFunc must never be NULL for a native method.
+ *
+ * The most common transitions are (1)->(2) and (1)->(3).  The former is
+ * atomic, since only one field is updated; the latter is not, but since
+ * dvmResolveNativeMethod ignores the "insns" field we just need to make
+ * sure the update happens in the correct order.
+ *
+ * A transition from (2)->(1) would work fine, but (3)->(1) will not,
+ * because both fields change.  If we did this while a thread was executing
+ * in the call bridge, we could null out the "insns" field right before
+ * the bridge tried to call through it.  So, once "insns" is set, we do
+ * not allow it to be cleared.  A NULL value for the "insns" argument is
+ * treated as "do not change existing value".
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func,
+    const u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+
+    assert(func != NULL);
+
+    /* just open up both; easier that way */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+    if (insns != NULL) {
+        /* update both, ensuring that "insns" is observed first */
+        method->insns = insns;
+        android_atomic_release_store((int32_t) func,
+            (volatile int32_t*)(void*) &method->nativeFunc);
+    } else {
+        /* only update nativeFunc */
+        method->nativeFunc = func;
+    }
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * Add a RegisterMap to a Method.  This is done when we verify the class
+ * and compute the register maps at class initialization time (i.e. when
+ * we don't have a pre-generated map).  This means "pMap" is on the heap
+ * and should be freed when the Method is discarded.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap)
+{
+    ClassObject* clazz = method->clazz;
+
+    if (method->registerMap != NULL) {
+        /* unexpected during class loading, okay on first use (uncompress) */
+        ALOGV("NOTE: registerMap already set for %s.%s",
+            method->clazz->descriptor, method->name);
+        /* keep going */
+    }
+    assert(!dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method));
+
+    /* might be virtual or direct */
+    dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+    method->registerMap = pMap;
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+    dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * dvmHashForeach callback.  A nonzero return value causes foreach to
+ * bail out.
+ */
+static int findClassCallback(void* vclazz, void* arg)
+{
+    ClassObject* clazz = (ClassObject*)vclazz;
+    const char* descriptor = (const char*) arg;
+
+    if (strcmp(clazz->descriptor, descriptor) == 0)
+        return (int) clazz;
+    return 0;
+}
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor)
+{
+    int result;
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    result = dvmHashForeach(gDvm.loadedClasses, findClassCallback,
+            (void*) descriptor);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+
+    return (ClassObject*) result;
+}
+
+/*
+ * Retrieve the system (a/k/a application) class loader.
+ *
+ * The caller must call dvmReleaseTrackedAlloc on the result.
+ */
+Object* dvmGetSystemClassLoader()
+{
+    Thread* self = dvmThreadSelf();
+    ClassObject* clClass = gDvm.classJavaLangClassLoader;
+
+    if (!dvmIsClassInitialized(clClass) && !dvmInitClass(clClass))
+        return NULL;
+
+    JValue result;
+    dvmCallMethod(self, gDvm.methJavaLangClassLoader_getSystemClassLoader,
+        NULL, &result);
+    Object* loader = (Object*)result.l;
+    dvmAddTrackedAlloc(loader, self);
+    return loader;
+}
+
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpClass(void* vclazz, void* varg)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    const ClassObject* super;
+    int flags = (int) varg;
+    char* desc;
+    int i;
+
+    if (clazz == NULL) {
+        ALOGI("dumpClass: ignoring request to dump null class");
+        return 0;
+    }
+
+    if ((flags & kDumpClassFullDetail) == 0) {
+        bool showInit = (flags & kDumpClassInitialized) != 0;
+        bool showLoader = (flags & kDumpClassClassLoader) != 0;
+        const char* initStr;
+
+        initStr = dvmIsClassInitialized(clazz) ? "true" : "false";
+
+        if (showInit && showLoader)
+            ALOGI("%s %p %s", clazz->descriptor, clazz->classLoader, initStr);
+        else if (showInit)
+            ALOGI("%s %s", clazz->descriptor, initStr);
+        else if (showLoader)
+            ALOGI("%s %p", clazz->descriptor, clazz->classLoader);
+        else
+            ALOGI("%s", clazz->descriptor);
+
+        return 0;
+    }
+
+    /* clazz->super briefly holds the superclass index during class prep */
+    if ((u4)clazz->super > 0x10000 && (u4) clazz->super != (u4)-1)
+        super = clazz->super;
+    else
+        super = NULL;
+
+    ALOGI("----- %s '%s' cl=%p ser=0x%08x -----",
+        dvmIsInterfaceClass(clazz) ? "interface" : "class",
+        clazz->descriptor, clazz->classLoader, clazz->serialNumber);
+    ALOGI("  objectSize=%d (%d from super)", (int) clazz->objectSize,
+        super != NULL ? (int) super->objectSize : -1);
+    ALOGI("  access=0x%04x.%04x", clazz->accessFlags >> 16,
+        clazz->accessFlags & JAVA_FLAGS_MASK);
+    if (super != NULL)
+        ALOGI("  super='%s' (cl=%p)", super->descriptor, super->classLoader);
+    if (dvmIsArrayClass(clazz)) {
+        ALOGI("  dimensions=%d elementClass=%s",
+            clazz->arrayDim, clazz->elementClass->descriptor);
+    }
+    if (clazz->iftableCount > 0) {
+        ALOGI("  interfaces (%d):", clazz->iftableCount);
+        for (i = 0; i < clazz->iftableCount; i++) {
+            InterfaceEntry* ent = &clazz->iftable[i];
+            int j;
+
+            ALOGI("    %2d: %s (cl=%p)",
+                i, ent->clazz->descriptor, ent->clazz->classLoader);
+
+            /* enable when needed */
+            if (false && ent->methodIndexArray != NULL) {
+                for (j = 0; j < ent->clazz->virtualMethodCount; j++)
+                    ALOGI("      %2d: %d %s %s",
+                        j, ent->methodIndexArray[j],
+                        ent->clazz->virtualMethods[j].name,
+                        clazz->vtable[ent->methodIndexArray[j]]->name);
+            }
+        }
+    }
+    if (!dvmIsInterfaceClass(clazz)) {
+        ALOGI("  vtable (%d entries, %d in super):", clazz->vtableCount,
+            super != NULL ? super->vtableCount : 0);
+        for (i = 0; i < clazz->vtableCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(&clazz->vtable[i]->prototype);
+            ALOGI("    %s%2d: %p %20s %s",
+                (i != clazz->vtable[i]->methodIndex) ? "*** " : "",
+                (u4) clazz->vtable[i]->methodIndex, clazz->vtable[i],
+                clazz->vtable[i]->name, desc);
+            free(desc);
+        }
+        ALOGI("  direct methods (%d entries):", clazz->directMethodCount);
+        for (i = 0; i < clazz->directMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->directMethods[i].prototype);
+            ALOGI("    %2d: %20s %s", i, clazz->directMethods[i].name,
+                desc);
+            free(desc);
+        }
+    } else {
+        ALOGI("  interface methods (%d):", clazz->virtualMethodCount);
+        for (i = 0; i < clazz->virtualMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->virtualMethods[i].prototype);
+            ALOGI("    %2d: %2d %20s %s", i,
+                (u4) clazz->virtualMethods[i].methodIndex,
+                clazz->virtualMethods[i].name,
+                desc);
+            free(desc);
+        }
+    }
+    if (clazz->sfieldCount > 0) {
+        ALOGI("  static fields (%d entries):", clazz->sfieldCount);
+        for (i = 0; i < clazz->sfieldCount; i++) {
+            ALOGI("    %2d: %20s %s", i, clazz->sfields[i].name,
+                clazz->sfields[i].signature);
+        }
+    }
+    if (clazz->ifieldCount > 0) {
+        ALOGI("  instance fields (%d entries):", clazz->ifieldCount);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            ALOGI("    %2d: %20s %s", i, clazz->ifields[i].name,
+                clazz->ifields[i].signature);
+        }
+    }
+    return 0;
+}
+
+/*
+ * Dump the contents of a single class.
+ *
+ * Pass kDumpClassFullDetail into "flags" to get lots of detail.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags)
+{
+    dumpClass((void*) clazz, (void*) flags);
+}
+
+/*
+ * Dump the contents of all classes.
+ */
+void dvmDumpAllClasses(int flags)
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashForeach(gDvm.loadedClasses, dumpClass, (void*) flags);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the number of loaded classes
+ */
+int dvmGetNumLoadedClasses()
+{
+    int count;
+    dvmHashTableLock(gDvm.loadedClasses);
+    count = dvmHashTableNumEntries(gDvm.loadedClasses);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+    return count;
+}
+
+/*
+ * Write some statistics to the log file.
+ */
+void dvmDumpLoaderStats(const char* msg)
+{
+    ALOGV("VM stats (%s): cls=%d/%d meth=%d ifld=%d sfld=%d linear=%d",
+        msg, gDvm.numLoadedClasses, dvmHashTableNumEntries(gDvm.loadedClasses),
+        gDvm.numDeclaredMethods, gDvm.numDeclaredInstFields,
+        gDvm.numDeclaredStaticFields, gDvm.pBootLoaderAlloc->curOffset);
+#ifdef COUNT_PRECISE_METHODS
+    ALOGI("GC precise methods: %d",
+        dvmPointerSetGetCount(gDvm.preciseMethods));
+#endif
+}
+
+/*
+ * ===========================================================================
+ *      Method Prototypes and Descriptors
+ * ===========================================================================
+ */
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dvmCompareMethodProtos().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+        const Method* method2)
+{
+    int result = strcmp(method1->name, method2->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareMethodProtos(method1, method2);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return value. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dvmCompareMethodArgProtos().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+        const Method* method2)
+{
+    int result = strcmp(method1->name, method2->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareMethodParameterProtos(method1, method2);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+    const DexProto* proto, const Method* method)
+{
+    int result = strcmp(name, method->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dexProtoCompare(proto, &method->prototype);
+}
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+    const char* descriptor, const Method* method)
+{
+    int result = strcmp(name, method->name);
+
+    if (result != 0) {
+        return result;
+    }
+
+    return dvmCompareDescriptorAndMethodProto(descriptor, method);
+}
diff --git a/vm/oo/Class.h b/vm/oo/Class.h
new file mode 100644
index 0000000..349c666
--- /dev/null
+++ b/vm/oo/Class.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Class loader.
+ */
+#ifndef DALVIK_OO_CLASS_H_
+#define DALVIK_OO_CLASS_H_
+
+/*
+ * The classpath and bootclasspath differ in that only the latter is
+ * consulted when looking for classes needed by the VM.  When searching
+ * for an arbitrary class definition, we start with the bootclasspath,
+ * look for optional packages (a/k/a standard extensions), and then try
+ * the classpath.
+ *
+ * In Dalvik, a class can be found in one of two ways:
+ *  - in a .dex file
+ *  - in a .dex file named specifically "classes.dex", which is held
+ *    inside a jar file
+ *
+ * These two may be freely intermixed in a classpath specification.
+ * Ordering is significant.
+ */
+enum ClassPathEntryKind {
+    kCpeUnknown = 0,
+    kCpeJar,
+    kCpeDex,
+    kCpeLastEntry       /* used as sentinel at end of array */
+};
+
+struct ClassPathEntry {
+    ClassPathEntryKind kind;
+    char*   fileName;
+    void*   ptr;            /* JarFile* or DexFile* */
+};
+
+bool dvmClassStartup(void);
+void dvmClassShutdown(void);
+bool dvmPrepBootClassPath(bool isNormalStart);
+
+/*
+ * Boot class path accessors, for class loader getResources().
+ */
+int dvmGetBootPathSize(void);
+StringObject* dvmGetBootPathResource(const char* name, int idx);
+void dvmDumpBootClassPath(void);
+
+/*
+ * Determine whether "path" is a member of "cpe".
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path);
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz);
+
+/*
+ * Find the class object representing the primitive type with the
+ * given descriptor. This returns NULL if the given type character
+ * is invalid.
+ */
+ClassObject* dvmFindPrimitiveClass(char type);
+
+/*
+ * Find the class with the given descriptor.  Load it if it hasn't already
+ * been.
+ *
+ * "loader" is the initiating class loader.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader);
+ClassObject* dvmFindClassNoInit(const char* descriptor, Object* loader);
+
+/*
+ * Like dvmFindClass, but only for system classes.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor);
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor);
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor);
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+    Object* classLoader);
+
+/*
+ * Link a loaded class.  Normally done as part of one of the "find class"
+ * variations, this is only called explicitly for synthetic class
+ * generation (e.g. reflect.Proxy).
+ */
+bool dvmLinkClass(ClassObject* clazz);
+
+/*
+ * Determine if a class has been initialized.
+ */
+INLINE bool dvmIsClassInitialized(const ClassObject* clazz) {
+    return (clazz->status == CLASS_INITIALIZED);
+}
+bool dvmIsClassInitializing(const ClassObject* clazz);
+
+/*
+ * Initialize a class.
+ */
+extern "C" bool dvmInitClass(ClassObject* clazz);
+
+/*
+ * Retrieve the system class loader.
+ */
+Object* dvmGetSystemClassLoader(void);
+
+/*
+ * Utility functions.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+    bool unprepOkay);
+void dvmFreeClassInnards(ClassObject* clazz);
+bool dvmAddClassToHash(ClassObject* clazz);
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader);
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader);
+
+/*
+ * Update method's "nativeFunc" and "insns".  If "insns" is NULL, the
+ * current method->insns value is not changed.
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns);
+
+/*
+ * Set the method's "registerMap" field.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap);
+
+/*
+ * Make a method's DexCode (which includes the bytecode) read-write or
+ * read-only.  The conversion to read-write may involve making a new copy
+ * of the DexCode, and in normal operation the read-only state is not
+ * actually enforced.
+ */
+void dvmMakeCodeReadWrite(Method* meth);
+void dvmMakeCodeReadOnly(Method* meth);
+
+/*
+ * During DEX optimizing, add an extra DEX to the bootstrap class path.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex);
+
+/*
+ * Debugging.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags);
+void dvmDumpAllClasses(int flags);
+void dvmDumpLoaderStats(const char* msg);
+int  dvmGetNumLoadedClasses();
+
+/* flags for dvmDumpClass / dvmDumpAllClasses */
+#define kDumpClassFullDetail    1
+#define kDumpClassClassLoader   (1 << 1)
+#define kDumpClassInitialized   (1 << 2)
+
+
+/*
+ * Store a copy of the method prototype descriptor string
+ * for the given method into the given DexStringCache, returning the
+ * stored string for convenience.
+ */
+INLINE char* dvmCopyDescriptorStringFromMethod(const Method* method,
+        DexStringCache *pCache)
+{
+    const char* result =
+        dexProtoGetMethodDescriptor(&method->prototype, pCache);
+    return dexStringCacheEnsureCopy(pCache, result);
+}
+
+/*
+ * Compute the number of argument words (u4 units) required by the
+ * given method's prototype. For example, if the method descriptor is
+ * "(IJ)D", this would return 3 (one for the int, two for the long;
+ * return value isn't relevant).
+ */
+INLINE int dvmComputeMethodArgsSize(const Method* method)
+{
+    return dexProtoComputeArgsSize(&method->prototype);
+}
+
+/*
+ * Compare the two method prototypes. The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodProtos(const Method* method1,
+        const Method* method2)
+{
+    return dexProtoCompare(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method prototypes, considering only the parameters
+ * (i.e. ignoring the return types). The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodParameterProtos(const Method* method1,
+        const Method* method2)
+{
+    return dexProtoCompareParameters(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+        const Method* method2);
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return type. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+        const Method* method2);
+
+/*
+ * Compare a method descriptor string with the prototype of a method,
+ * as if by converting the descriptor to a DexProto and comparing it
+ * with dexProtoCompare().
+ */
+INLINE int dvmCompareDescriptorAndMethodProto(const char* descriptor,
+    const Method* method)
+{
+    // Sense is reversed.
+    return -dexProtoCompareToDescriptor(&method->prototype, descriptor);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+    const DexProto* proto, const Method* method);
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+    const char* descriptor, const Method* method);
+
+/*
+ * Returns the size of the given class object in bytes.
+ */
+size_t dvmClassObjectSize(const ClassObject *clazz);
+
+#endif  // DALVIK_OO_CLASS_H_
diff --git a/vm/oo/Object.cpp b/vm/oo/Object.cpp
new file mode 100644
index 0000000..cd3ae64
--- /dev/null
+++ b/vm/oo/Object.cpp
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Operations on an Object.
+ */
+#include "Dalvik.h"
+
+/*
+ * Find a matching field, in the current class only.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField;
+    int i;
+
+    assert(clazz != NULL);
+
+    /*
+     * Find a field with a matching name and signature.  The Java programming
+     * language does not allow you to have two fields with the same name
+     * and different types, but the Java VM spec does allow it, so we can't
+     * bail out early when the name matches.
+     */
+    pField = clazz->ifields;
+    for (i = 0; i < clazz->ifieldCount; i++, pField++) {
+        if (strcmp(fieldName, pField->name) == 0 &&
+            strcmp(signature, pField->signature) == 0)
+        {
+            return pField;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Searching through interfaces isn't necessary, because interface fields
+ * are inherently public/static/final.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField;
+
+    /*
+     * Search for a match in the current class.
+     */
+    pField = dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    if (clazz->super != NULL)
+        return dvmFindInstanceFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+
+/*
+ * Find a matching field, in this class or an interface.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    const StaticField* pField;
+    int i;
+
+    assert(clazz != NULL);
+
+    /*
+     * Find a field with a matching name and signature.  As with instance
+     * fields, the VM allows you to have two fields with the same name so
+     * long as they have different types.
+     */
+    pField = &clazz->sfields[0];
+    for (i = 0; i < clazz->sfieldCount; i++, pField++) {
+        if (strcmp(fieldName, pField->name) == 0 &&
+            strcmp(signature, pField->signature) == 0)
+        {
+            return (StaticField*) pField;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Returns NULL if the field can't be found.  (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    StaticField* pField;
+
+    /*
+     * Search for a match in the current class.
+     */
+    pField = dvmFindStaticField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    /*
+     * See if it's in any of our interfaces.  We don't check interfaces
+     * inherited from the superclass yet.
+     *
+     * (Note the set may have been stripped down because of redundancy with
+     * the superclass; see notes in createIftable.)
+     */
+    int i = 0;
+    if (clazz->super != NULL) {
+        assert(clazz->iftableCount >= clazz->super->iftableCount);
+        i = clazz->super->iftableCount;
+    }
+    for ( ; i < clazz->iftableCount; i++) {
+        ClassObject* iface = clazz->iftable[i].clazz;
+        pField = dvmFindStaticField(iface, fieldName, signature);
+        if (pField != NULL)
+            return pField;
+    }
+
+    if (clazz->super != NULL)
+        return dvmFindStaticFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * We scan both the static and instance field lists in the class.  If it's
+ * not found there, we check the direct interfaces, and then recursively
+ * scan the superclasses.  This is the order prescribed in the VM spec
+ * (v2 5.4.3.2).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ */
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+    const char* signature)
+{
+    Field* pField;
+
+    /*
+     * Search for a match in the current class.  Which set we scan first
+     * doesn't really matter.
+     */
+    pField = (Field*) dvmFindStaticField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+    pField = (Field*) dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField != NULL)
+        return pField;
+
+    /*
+     * See if it's in any of our interfaces.  We don't check interfaces
+     * inherited from the superclass yet.
+     */
+    int i = 0;
+    if (clazz->super != NULL) {
+        assert(clazz->iftableCount >= clazz->super->iftableCount);
+        i = clazz->super->iftableCount;
+    }
+    for ( ; i < clazz->iftableCount; i++) {
+        ClassObject* iface = clazz->iftable[i].clazz;
+        pField = (Field*) dvmFindStaticField(iface, fieldName, signature);
+        if (pField != NULL)
+            return pField;
+    }
+
+    if (clazz->super != NULL)
+        return dvmFindFieldHier(clazz->super, fieldName, signature);
+    else
+        return NULL;
+}
+
+
+/*
+ * Compare the given name, return type, and argument types with the contents
+ * of the given method. This returns 0 if they are equal and non-zero if not.
+ */
+static inline int compareMethodHelper(Method* method, const char* methodName,
+    const char* returnType, size_t argCount, const char** argTypes)
+{
+    DexParameterIterator iterator;
+    const DexProto* proto;
+
+    if (strcmp(methodName, method->name) != 0) {
+        return 1;
+    }
+
+    proto = &method->prototype;
+
+    if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) {
+        return 1;
+    }
+
+    if (dexProtoGetParameterCount(proto) != argCount) {
+        return 1;
+    }
+
+    dexParameterIteratorInit(&iterator, proto);
+
+    for (/*argCount*/; argCount != 0; argCount--, argTypes++) {
+        const char* argType = *argTypes;
+        const char* paramType = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (paramType == NULL) {
+            /* Param list ended early; no match */
+            break;
+        } else if (strcmp(argType, paramType) != 0) {
+            /* Types aren't the same; no match. */
+            break;
+        }
+    }
+
+    if (argCount == 0) {
+        /* We ran through all the given arguments... */
+        if (dexParameterIteratorNextDescriptor(&iterator) == NULL) {
+            /* ...and through all the method's arguments; success! */
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Get the count of arguments in the given method descriptor string,
+ * and also find a pointer to the return type.
+ */
+static inline size_t countArgsAndFindReturnType(const char* descriptor,
+    const char** pReturnType)
+{
+    size_t count = 0;
+    bool bogus = false;
+    bool done = false;
+
+    assert(*descriptor == '(');
+    descriptor++;
+
+    while (!done) {
+        switch (*descriptor) {
+            case 'B': case 'C': case 'D': case 'F':
+            case 'I': case 'J': case 'S': case 'Z': {
+                count++;
+                break;
+            }
+            case '[': {
+                do {
+                    descriptor++;
+                } while (*descriptor == '[');
+                /*
+                 * Don't increment count, as it will be taken care of
+                 * by the next iteration. Also, decrement descriptor
+                 * to compensate for the increment below the switch.
+                 */
+                descriptor--;
+                break;
+            }
+            case 'L': {
+                do {
+                    descriptor++;
+                } while ((*descriptor != ';') && (*descriptor != '\0'));
+                count++;
+                if (*descriptor == '\0') {
+                    /* Bogus descriptor. */
+                    done = true;
+                    bogus = true;
+                }
+                break;
+            }
+            case ')': {
+                /*
+                 * Note: The loop will exit after incrementing descriptor
+                 * one more time, so it then points at the return type.
+                 */
+                done = true;
+                break;
+            }
+            default: {
+                /* Bogus descriptor. */
+                done = true;
+                bogus = true;
+                break;
+            }
+        }
+
+        descriptor++;
+    }
+
+    if (bogus) {
+        *pReturnType = NULL;
+        return 0;
+    }
+
+    *pReturnType = descriptor;
+    return count;
+}
+
+/*
+ * Copy the argument types into the given array using the given buffer
+ * for the contents.
+ */
+static inline void copyTypes(char* buffer, const char** argTypes,
+    size_t argCount, const char* descriptor)
+{
+    size_t i;
+    char c;
+
+    /* Skip the '('. */
+    descriptor++;
+
+    for (i = 0; i < argCount; i++) {
+        argTypes[i] = buffer;
+
+        /* Copy all the array markers and one extra character. */
+        do {
+            c = *(descriptor++);
+            *(buffer++) = c;
+        } while (c == '[');
+
+        if (c == 'L') {
+            /* Copy the rest of a class name. */
+            do {
+                c = *(descriptor++);
+                *(buffer++) = c;
+            } while (c != ';');
+        }
+
+        *(buffer++) = '\0';
+    }
+}
+
+/*
+ * Look for a match in the given class. Returns the match if found
+ * or NULL if not.
+ */
+static Method* findMethodInListByDescriptor(const ClassObject* clazz,
+    bool findVirtual, bool isHier, const char* name, const char* descriptor)
+{
+    const char* returnType;
+    size_t argCount = countArgsAndFindReturnType(descriptor, &returnType);
+
+    if (returnType == NULL) {
+        ALOGW("Bogus method descriptor: %s", descriptor);
+        return NULL;
+    }
+
+    /*
+     * Make buffer big enough for all the argument type characters and
+     * one '\0' per argument. The "- 2" is because "returnType -
+     * descriptor" includes two parens.
+     */
+    char buffer[argCount + (returnType - descriptor) - 2];
+    const char* argTypes[argCount];
+
+    copyTypes(buffer, argTypes, argCount, descriptor);
+
+    while (clazz != NULL) {
+        Method* methods;
+        size_t methodCount;
+        size_t i;
+
+        if (findVirtual) {
+            methods = clazz->virtualMethods;
+            methodCount = clazz->virtualMethodCount;
+        } else {
+            methods = clazz->directMethods;
+            methodCount = clazz->directMethodCount;
+        }
+
+        for (i = 0; i < methodCount; i++) {
+            Method* method = &methods[i];
+            if (compareMethodHelper(method, name, returnType, argCount,
+                            argTypes) == 0) {
+                return method;
+            }
+        }
+
+        if (! isHier) {
+            break;
+        }
+
+        clazz = clazz->super;
+    }
+
+    return NULL;
+}
+
+/*
+ * Look for a match in the given clazz. Returns the match if found
+ * or NULL if not.
+ *
+ * "wantedType" should be METHOD_VIRTUAL or METHOD_DIRECT to indicate the
+ * list to search through.  If the match can come from either list, use
+ * MATCH_UNKNOWN to scan both.
+ */
+static Method* findMethodInListByProto(const ClassObject* clazz,
+    MethodType wantedType, bool isHier, const char* name, const DexProto* proto)
+{
+    while (clazz != NULL) {
+        int i;
+
+        /*
+         * Check the virtual and/or direct method lists.
+         */
+        if (wantedType == METHOD_VIRTUAL || wantedType == METHOD_UNKNOWN) {
+            for (i = 0; i < clazz->virtualMethodCount; i++) {
+                Method* method = &clazz->virtualMethods[i];
+                if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+                    return method;
+                }
+            }
+        }
+        if (wantedType == METHOD_DIRECT || wantedType == METHOD_UNKNOWN) {
+            for (i = 0; i < clazz->directMethodCount; i++) {
+                Method* method = &clazz->directMethods[i];
+                if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+                    return method;
+                }
+            }
+        }
+
+        if (! isHier) {
+            break;
+        }
+
+        clazz = clazz->super;
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, true, false,
+            methodName, descriptor);
+
+    // TODO? - throw IncompatibleClassChangeError if a match is
+    // found in the directMethods list, rather than NotFoundError.
+    // Note we could have been called by dvmFindVirtualMethodHier though.
+}
+
+
+/*
+ * Find a "virtual" method in a class, knowing only the name.  This is
+ * only useful in limited circumstances, e.g. when searching for a member
+ * of an annotation class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+    const char* methodName)
+{
+    Method* methods = clazz->virtualMethods;
+    int methodCount = clazz->virtualMethodCount;
+    int i;
+
+    for (i = 0; i < methodCount; i++) {
+        if (strcmp(methods[i].name, methodName) == 0)
+            return &methods[i];
+    }
+
+    return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_VIRTUAL, false, methodName,
+            proto);
+}
+
+/*
+ * Find a "virtual" method in a class.  If we don't find it, try the
+ * superclass.  Does not examine interfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, true, true,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "virtual" method in a class.  If we don't find it, try the
+ * superclass.  Does not examine interfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_VIRTUAL, true, methodName,
+            proto);
+}
+
+/*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor)
+{
+    Method* resMethod = dvmFindVirtualMethodByDescriptor(iface,
+        methodName, descriptor);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethodByDescriptor(iface->iftable[i].clazz,
+                methodName, descriptor);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto)
+{
+    Method* resMethod = dvmFindVirtualMethod(iface, methodName, proto);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethod(iface->iftable[i].clazz,
+                methodName, proto);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
+ * Find a "direct" method (static, private, or "<*init>").
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, false, false,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method.  If we don't find it, try the superclass.  This
+ * is only appropriate for static methods, but will work for all direct
+ * methods.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor)
+{
+    return findMethodInListByDescriptor(clazz, false, true,
+            methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method (static or "<*init>").
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName,
+            proto);
+}
+
+/*
+ * Find a "direct" method in a class.  If we don't find it, try the
+ * superclass.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName,
+            proto);
+}
+
+/*
+ * Find a virtual or static method in a class.  If we don't find it, try the
+ * superclass.  This is compatible with the VM spec (v2 5.4.3.3) method
+ * search order, but it stops short of scanning through interfaces (which
+ * should be done after this function completes).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto)
+{
+    return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName,
+            proto);
+}
+
+
+/*
+ * We have a method pointer for a method in "clazz", but it might be
+ * pointing to a method in a derived class.  We want to find the actual entry
+ * from the class' vtable.  If "clazz" is an interface, we have to do a
+ * little more digging.
+ *
+ * For "direct" methods (private / constructor), we just return the
+ * original Method.
+ *
+ * (This is used for reflection and JNI "call method" calls.)
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+    const Method* meth)
+{
+    Method* actualMeth;
+    int methodIndex;
+
+    if (dvmIsDirectMethod(meth)) {
+        /* no vtable entry for these */
+        assert(!dvmIsStaticMethod(meth));
+        return meth;
+    }
+
+    /*
+     * If the method was declared in an interface, we need to scan through
+     * the class' list of interfaces for it, and find the vtable index
+     * from that.
+     *
+     * TODO: use the interface cache.
+     */
+    if (dvmIsInterfaceClass(meth->clazz)) {
+        int i;
+
+        for (i = 0; i < clazz->iftableCount; i++) {
+            if (clazz->iftable[i].clazz == meth->clazz)
+                break;
+        }
+        if (i == clazz->iftableCount) {
+            dvmThrowIncompatibleClassChangeError(
+                "invoking method from interface not implemented by class");
+            return NULL;
+        }
+
+        methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex];
+    } else {
+        methodIndex = meth->methodIndex;
+    }
+
+    assert(methodIndex >= 0 && methodIndex < clazz->vtableCount);
+    actualMeth = clazz->vtable[methodIndex];
+
+    /*
+     * Make sure there's code to execute.
+     */
+    if (dvmIsAbstractMethod(actualMeth)) {
+        dvmThrowAbstractMethodError(NULL);
+        return NULL;
+    }
+    assert(!dvmIsMirandaMethod(actualMeth));
+
+    return actualMeth;
+}
+
+/*
+ * Get the source file for a method.
+ */
+const char* dvmGetMethodSourceFile(const Method* meth)
+{
+    /*
+     * TODO: A method's debug info can override the default source
+     * file for a class, so we should account for that possibility
+     * here.
+     */
+    return meth->clazz->sourceFile;
+}
+
+/*
+ * Dump some information about an object.
+ */
+void dvmDumpObject(const Object* obj)
+{
+    ClassObject* clazz;
+    int i;
+
+    if (obj == NULL || obj->clazz == NULL) {
+        ALOGW("Null or malformed object not dumped");
+        return;
+    }
+
+    clazz = obj->clazz;
+    ALOGD("----- Object dump: %p (%s, %d bytes) -----",
+        obj, clazz->descriptor, (int) clazz->objectSize);
+    //printHexDump(obj, clazz->objectSize);
+    ALOGD("  Fields:");
+    while (clazz != NULL) {
+        ALOGD("    -- %s", clazz->descriptor);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            const InstField* pField = &clazz->ifields[i];
+            char type = pField->signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = dvmGetFieldFloat(obj, pField->byteOffset);
+                else
+                    dval = dvmGetFieldDouble(obj, pField->byteOffset);
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%d %.3f", i,
+                    pField->name, pField->signature,
+                    pField->accessFlags, pField->byteOffset, dval);
+            } else {
+                u8 lval;
+
+                if (type == 'J')
+                    lval = dvmGetFieldLong(obj, pField->byteOffset);
+                else if (type == 'Z')
+                    lval = dvmGetFieldBoolean(obj, pField->byteOffset);
+                else
+                    lval = dvmGetFieldInt(obj, pField->byteOffset);
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i,
+                    pField->name, pField->signature,
+                    pField->accessFlags, pField->byteOffset, lval);
+            }
+        }
+
+        clazz = clazz->super;
+    }
+    if (dvmIsClassObject(obj)) {
+        ALOGD("  Static fields:");
+        const StaticField* sfields = &((ClassObject *)obj)->sfields[0];
+        for (i = 0; i < ((ClassObject *)obj)->sfieldCount; ++i) {
+            const StaticField* pField = &sfields[i];
+            size_t byteOffset = (size_t)pField - (size_t)sfields;
+            char type = pField->signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = pField->value.f;
+                else
+                    dval = pField->value.d;
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%zd %.3f", i,
+                     pField->name, pField->signature,
+                     pField->accessFlags, byteOffset, dval);
+            } else {
+                u8 lval;
+
+                if (type == 'J')
+                    lval = pField->value.j;
+                else if (type == 'Z')
+                    lval = pField->value.z;
+                else
+                    lval = pField->value.i;
+
+                ALOGD("    %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i,
+                     pField->name, pField->signature,
+                     pField->accessFlags, byteOffset, lval);
+            }
+        }
+    }
+}
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
new file mode 100644
index 0000000..92438ba
--- /dev/null
+++ b/vm/oo/Object.h
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Declaration of the fundamental Object type and refinements thereof, plus
+ * some functions for manipulating them.
+ */
+#ifndef DALVIK_OO_OBJECT_H_
+#define DALVIK_OO_OBJECT_H_
+
+#include <stddef.h>
+#include "Atomic.h"
+
+/* fwd decl */
+struct DataObject;
+struct InitiatingLoaderList;
+struct ClassObject;
+struct StringObject;
+struct ArrayObject;
+struct Method;
+struct ExceptionEntry;
+struct LineNumEntry;
+struct StaticField;
+struct InstField;
+struct Field;
+struct RegisterMap;
+
+/*
+ * Native function pointer type.
+ *
+ * "args[0]" holds the "this" pointer for virtual methods.
+ *
+ * The "Bridge" form is a super-set of the "Native" form; in many places
+ * they are used interchangeably.  Currently, all functions have all
+ * arguments passed in, but some functions only care about the first two.
+ * Passing extra arguments to a C function is (mostly) harmless.
+ */
+typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult,
+    const Method* method, struct Thread* self);
+typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult);
+
+
+/* vm-internal access flags and related definitions */
+enum AccessFlags {
+    ACC_MIRANDA         = 0x8000,       // method (internal to VM)
+    JAVA_FLAGS_MASK     = 0xffff,       // bits set from Java sources (low 16)
+};
+
+/* Use the top 16 bits of the access flags field for
+ * other class flags.  Code should use the *CLASS_FLAG*()
+ * macros to set/get these flags.
+ */
+enum ClassFlags {
+    CLASS_ISFINALIZABLE        = (1<<31), // class/ancestor overrides finalize()
+    CLASS_ISARRAY              = (1<<30), // class is a "[*"
+    CLASS_ISOBJECTARRAY        = (1<<29), // class is a "[L*" or "[[*"
+    CLASS_ISCLASS              = (1<<28), // class is *the* class Class
+
+    CLASS_ISREFERENCE          = (1<<27), // class is a soft/weak/phantom ref
+                                          // only ISREFERENCE is set --> soft
+    CLASS_ISWEAKREFERENCE      = (1<<26), // class is a weak reference
+    CLASS_ISFINALIZERREFERENCE = (1<<25), // class is a finalizer reference
+    CLASS_ISPHANTOMREFERENCE   = (1<<24), // class is a phantom reference
+
+    CLASS_MULTIPLE_DEFS        = (1<<23), // DEX verifier: defs in multiple DEXs
+
+    /* unlike the others, these can be present in the optimized DEX file */
+    CLASS_ISOPTIMIZED          = (1<<17), // class may contain opt instrs
+    CLASS_ISPREVERIFIED        = (1<<16), // class has been pre-verified
+};
+
+/*
+ * Get/set class flags.
+ */
+#define SET_CLASS_FLAG(clazz, flag) \
+    do { (clazz)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_CLASS_FLAG(clazz, flag) \
+    do { (clazz)->accessFlags &= ~(flag); } while (0)
+
+#define IS_CLASS_FLAG_SET(clazz, flag) \
+    (((clazz)->accessFlags & (flag)) != 0)
+
+#define GET_CLASS_FLAG_GROUP(clazz, flags) \
+    ((u4)((clazz)->accessFlags & (flags)))
+
+/*
+ * Use the top 16 bits of the access flags field for other method flags.
+ * Code should use the *METHOD_FLAG*() macros to set/get these flags.
+ */
+enum MethodFlags {
+    METHOD_ISWRITABLE       = (1<<31),  // the method's code is writable
+};
+
+/*
+ * Get/set method flags.
+ */
+#define SET_METHOD_FLAG(method, flag) \
+    do { (method)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_METHOD_FLAG(method, flag) \
+    do { (method)->accessFlags &= ~(flag); } while (0)
+
+#define IS_METHOD_FLAG_SET(method, flag) \
+    (((method)->accessFlags & (flag)) != 0)
+
+#define GET_METHOD_FLAG_GROUP(method, flags) \
+    ((u4)((method)->accessFlags & (flags)))
+
+/* current state of the class, increasing as we progress */
+enum ClassStatus {
+    CLASS_ERROR         = -1,
+
+    CLASS_NOTREADY      = 0,
+    CLASS_IDX           = 1,    /* loaded, DEX idx in super or ifaces */
+    CLASS_LOADED        = 2,    /* DEX idx values resolved */
+    CLASS_RESOLVED      = 3,    /* part of linking */
+    CLASS_VERIFYING     = 4,    /* in the process of being verified */
+    CLASS_VERIFIED      = 5,    /* logically part of linking; done pre-init */
+    CLASS_INITIALIZING  = 6,    /* class init in progress */
+    CLASS_INITIALIZED   = 7,    /* ready to go */
+};
+
+/*
+ * Definitions for packing refOffsets in ClassObject.
+ */
+/*
+ * A magic value for refOffsets. Ignore the bits and walk the super
+ * chain when this is the value.
+ * [This is an unlikely "natural" value, since it would be 30 non-ref instance
+ * fields followed by 2 ref instance fields.]
+ */
+#define CLASS_WALK_SUPER ((unsigned int)(3))
+#define CLASS_SMALLEST_OFFSET (sizeof(struct Object))
+#define CLASS_BITS_PER_WORD (sizeof(unsigned long int) * 8)
+#define CLASS_OFFSET_ALIGNMENT 4
+#define CLASS_HIGH_BIT ((unsigned int)1 << (CLASS_BITS_PER_WORD - 1))
+/*
+ * Given an offset, return the bit number which would encode that offset.
+ * Local use only.
+ */
+#define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \
+    (((unsigned int)(byteOffset) - CLASS_SMALLEST_OFFSET) / \
+     CLASS_OFFSET_ALIGNMENT)
+/*
+ * Is the given offset too large to be encoded?
+ */
+#define CLASS_CAN_ENCODE_OFFSET(byteOffset) \
+    (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD)
+/*
+ * Return a single bit, encoding the offset.
+ * Undefined if the offset is too large, as defined above.
+ */
+#define CLASS_BIT_FROM_OFFSET(byteOffset) \
+    (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset))
+/*
+ * Return an offset, given a bit number as returned from CLZ.
+ */
+#define CLASS_OFFSET_FROM_CLZ(rshift) \
+    (((int)(rshift) * CLASS_OFFSET_ALIGNMENT) + CLASS_SMALLEST_OFFSET)
+
+
+/*
+ * Used for iftable in ClassObject.
+ */
+struct InterfaceEntry {
+    /* pointer to interface class */
+    ClassObject*    clazz;
+
+    /*
+     * Index into array of vtable offsets.  This points into the ifviPool,
+     * which holds the vtables for all interfaces declared by this class.
+     */
+    int*            methodIndexArray;
+};
+
+
+
+/*
+ * There are three types of objects:
+ *  Class objects - an instance of java.lang.Class
+ *  Array objects - an object created with a "new array" instruction
+ *  Data objects - an object that is neither of the above
+ *
+ * We also define String objects.  At present they're equivalent to
+ * DataObject, but that may change.  (Either way, they make some of the
+ * code more obvious.)
+ *
+ * All objects have an Object header followed by type-specific data.
+ */
+struct Object {
+    /* ptr to class object */
+    ClassObject*    clazz;
+
+    /*
+     * A word containing either a "thin" lock or a "fat" monitor.  See
+     * the comments in Sync.c for a description of its layout.
+     */
+    u4              lock;
+};
+
+/*
+ * Properly initialize an Object.
+ * void DVM_OBJECT_INIT(Object *obj, ClassObject *clazz_)
+ */
+#define DVM_OBJECT_INIT(obj, clazz_) \
+    dvmSetFieldObject(obj, OFFSETOF_MEMBER(Object, clazz), clazz_)
+
+/*
+ * Data objects have an Object header followed by their instance data.
+ */
+struct DataObject : Object {
+    /* variable #of u4 slots; u8 uses 2 slots */
+    u4              instanceData[1];
+};
+
+/*
+ * Strings are used frequently enough that we may want to give them their
+ * own unique type.
+ *
+ * Using a dedicated type object to access the instance data provides a
+ * performance advantage but makes the java/lang/String.java implementation
+ * fragile.
+ *
+ * Currently this is just equal to DataObject, and we pull the fields out
+ * like we do for any other object.
+ */
+struct StringObject : Object {
+    /* variable #of u4 slots; u8 uses 2 slots */
+    u4              instanceData[1];
+
+    /** Returns this string's length in characters. */
+    int length() const;
+
+    /**
+     * Returns this string's length in bytes when encoded as modified UTF-8.
+     * Does not include a terminating NUL byte.
+     */
+    int utfLength() const;
+
+    /** Returns this string's char[] as an ArrayObject. */
+    ArrayObject* array() const;
+
+    /** Returns this string's char[] as a u2*. */
+    const u2* chars() const;
+};
+
+
+/*
+ * Array objects have these additional fields.
+ *
+ * We don't currently store the size of each element.  Usually it's implied
+ * by the instruction.  If necessary, the width can be derived from
+ * the first char of obj->clazz->descriptor.
+ */
+struct ArrayObject : Object {
+    /* number of elements; immutable after init */
+    u4              length;
+
+    /*
+     * Array contents; actual size is (length * sizeof(type)).  This is
+     * declared as u8 so that the compiler inserts any necessary padding
+     * (e.g. for EABI); the actual allocation may be smaller than 8 bytes.
+     */
+    u8              contents[1];
+};
+
+/*
+ * For classes created early and thus probably in the zygote, the
+ * InitiatingLoaderList is kept in gDvm. Later classes use the structure in
+ * Object Class. This helps keep zygote pages shared.
+ */
+struct InitiatingLoaderList {
+    /* a list of initiating loader Objects; grown and initialized on demand */
+    Object**  initiatingLoaders;
+    /* count of loaders in the above list */
+    int       initiatingLoaderCount;
+};
+
+/*
+ * Generic field header.  We pass this around when we want a generic Field
+ * pointer (e.g. for reflection stuff).  Testing the accessFlags for
+ * ACC_STATIC allows a proper up-cast.
+ */
+struct Field {
+    ClassObject*    clazz;          /* class in which the field is declared */
+    const char*     name;
+    const char*     signature;      /* e.g. "I", "[C", "Landroid/os/Debug;" */
+    u4              accessFlags;
+};
+
+u4 dvmGetFieldIdx(const Field* field);
+
+/*
+ * Static field.
+ */
+struct StaticField : Field {
+    JValue          value;          /* initially set from DEX for primitives */
+};
+
+/*
+ * Instance field.
+ */
+struct InstField : Field {
+    /*
+     * This field indicates the byte offset from the beginning of the
+     * (Object *) to the actual instance data; e.g., byteOffset==0 is
+     * the same as the object pointer (bug!), and byteOffset==4 is 4
+     * bytes farther.
+     */
+    int             byteOffset;
+};
+
+/*
+ * This defines the amount of space we leave for field slots in the
+ * java.lang.Class definition.  If we alter the class to have more than
+ * this many fields, the VM will abort at startup.
+ */
+#define CLASS_FIELD_SLOTS   4
+
+/*
+ * Class objects have many additional fields.  This is used for both
+ * classes and interfaces, including synthesized classes (arrays and
+ * primitive types).
+ *
+ * Class objects are unusual in that they have some fields allocated with
+ * the system malloc (or LinearAlloc), rather than on the GC heap.  This is
+ * handy during initialization, but does require special handling when
+ * discarding java.lang.Class objects.
+ *
+ * The separation of methods (direct vs. virtual) and fields (class vs.
+ * instance) used in Dalvik works out pretty well.  The only time it's
+ * annoying is when enumerating or searching for things with reflection.
+ */
+struct ClassObject : Object {
+    /* leave space for instance data; we could access fields directly if we
+       freeze the definition of java/lang/Class */
+    u4              instanceData[CLASS_FIELD_SLOTS];
+
+    /* UTF-8 descriptor for the class; from constant pool, or on heap
+       if generated ("[C") */
+    const char*     descriptor;
+    char*           descriptorAlloc;
+
+    /* access flags; low 16 bits are defined by VM spec */
+    u4              accessFlags;
+
+    /* VM-unique class serial number, nonzero, set very early */
+    u4              serialNumber;
+
+    /* DexFile from which we came; needed to resolve constant pool entries */
+    /* (will be NULL for VM-generated, e.g. arrays and primitive classes) */
+    DvmDex*         pDvmDex;
+
+    /* state of class initialization */
+    ClassStatus     status;
+
+    /* if class verify fails, we must return same error on subsequent tries */
+    ClassObject*    verifyErrorClass;
+
+    /* threadId, used to check for recursive <clinit> invocation */
+    u4              initThreadId;
+
+    /*
+     * Total object size; used when allocating storage on gc heap.  (For
+     * interfaces and abstract classes this will be zero.)
+     */
+    size_t          objectSize;
+
+    /* arrays only: class object for base element, for instanceof/checkcast
+       (for String[][][], this will be String) */
+    ClassObject*    elementClass;
+
+    /* arrays only: number of dimensions, e.g. int[][] is 2 */
+    int             arrayDim;
+
+    /* primitive type index, or PRIM_NOT (-1); set for generated prim classes */
+    PrimitiveType   primitiveType;
+
+    /* superclass, or NULL if this is java.lang.Object */
+    ClassObject*    super;
+
+    /* defining class loader, or NULL for the "bootstrap" system loader */
+    Object*         classLoader;
+
+    /* initiating class loader list */
+    /* NOTE: for classes with low serialNumber, these are unused, and the
+       values are kept in a table in gDvm. */
+    InitiatingLoaderList initiatingLoaderList;
+
+    /* array of interfaces this class implements directly */
+    int             interfaceCount;
+    ClassObject**   interfaces;
+
+    /* static, private, and <init> methods */
+    int             directMethodCount;
+    Method*         directMethods;
+
+    /* virtual methods defined in this class; invoked through vtable */
+    int             virtualMethodCount;
+    Method*         virtualMethods;
+
+    /*
+     * Virtual method table (vtable), for use by "invoke-virtual".  The
+     * vtable from the superclass is copied in, and virtual methods from
+     * our class either replace those from the super or are appended.
+     */
+    int             vtableCount;
+    Method**        vtable;
+
+    /*
+     * Interface table (iftable), one entry per interface supported by
+     * this class.  That means one entry for each interface we support
+     * directly, indirectly via superclass, or indirectly via
+     * superinterface.  This will be null if neither we nor our superclass
+     * implement any interfaces.
+     *
+     * Why we need this: given "class Foo implements Face", declare
+     * "Face faceObj = new Foo()".  Invoke faceObj.blah(), where "blah" is
+     * part of the Face interface.  We can't easily use a single vtable.
+     *
+     * For every interface a concrete class implements, we create a list of
+     * virtualMethod indices for the methods in the interface.
+     */
+    int             iftableCount;
+    InterfaceEntry* iftable;
+
+    /*
+     * The interface vtable indices for iftable get stored here.  By placing
+     * them all in a single pool for each class that implements interfaces,
+     * we decrease the number of allocations.
+     */
+    int             ifviPoolCount;
+    int*            ifviPool;
+
+    /* instance fields
+     *
+     * These describe the layout of the contents of a DataObject-compatible
+     * Object.  Note that only the fields directly defined by this class
+     * are listed in ifields;  fields defined by a superclass are listed
+     * in the superclass's ClassObject.ifields.
+     *
+     * All instance fields that refer to objects are guaranteed to be
+     * at the beginning of the field list.  ifieldRefCount specifies
+     * the number of reference fields.
+     */
+    int             ifieldCount;
+    int             ifieldRefCount; // number of fields that are object refs
+    InstField*      ifields;
+
+    /* bitmap of offsets of ifields */
+    u4 refOffsets;
+
+    /* source file name, if known */
+    const char*     sourceFile;
+
+    /* static fields */
+    int             sfieldCount;
+    StaticField     sfields[0]; /* MUST be last item */
+};
+
+/*
+ * A method.  We create one of these for every method in every class
+ * we load, so try to keep the size to a minimum.
+ *
+ * Much of this comes from and could be accessed in the data held in shared
+ * memory.  We hold it all together here for speed.  Everything but the
+ * pointers could be held in a shared table generated by the optimizer;
+ * if we're willing to convert them to offsets and take the performance
+ * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
+ * could move everything but "nativeFunc".
+ */
+struct Method {
+    /* the class we are a part of */
+    ClassObject*    clazz;
+
+    /* access flags; low 16 bits are defined by spec (could be u2?) */
+    u4              accessFlags;
+
+    /*
+     * For concrete virtual methods, this is the offset of the method
+     * in "vtable".
+     *
+     * For abstract methods in an interface class, this is the offset
+     * of the method in "iftable[n]->methodIndexArray".
+     */
+    u2             methodIndex;
+
+    /*
+     * Method bounds; not needed for an abstract method.
+     *
+     * For a native method, we compute the size of the argument list, and
+     * set "insSize" and "registerSize" equal to it.
+     */
+    u2              registersSize;  /* ins + locals */
+    u2              outsSize;
+    u2              insSize;
+
+    /* method name, e.g. "<init>" or "eatLunch" */
+    const char*     name;
+
+    /*
+     * Method prototype descriptor string (return and argument types).
+     *
+     * TODO: This currently must specify the DexFile as well as the proto_ids
+     * index, because generated Proxy classes don't have a DexFile.  We can
+     * remove the DexFile* and reduce the size of this struct if we generate
+     * a DEX for proxies.
+     */
+    DexProto        prototype;
+
+    /* short-form method descriptor string */
+    const char*     shorty;
+
+    /*
+     * The remaining items are not used for abstract or native methods.
+     * (JNI is currently hijacking "insns" as a function pointer, set
+     * after the first call.  For internal-native this stays null.)
+     */
+
+    /* the actual code */
+    const u2*       insns;          /* instructions, in memory-mapped .dex */
+
+    /* JNI: cached argument and return-type hints */
+    int             jniArgInfo;
+
+    /*
+     * JNI: native method ptr; could be actual function or a JNI bridge.  We
+     * don't currently discriminate between DalvikBridgeFunc and
+     * DalvikNativeFunc; the former takes an argument superset (i.e. two
+     * extra args) which will be ignored.  If necessary we can use
+     * insns==NULL to detect JNI bridge vs. internal native.
+     */
+    DalvikBridgeFunc nativeFunc;
+
+    /*
+     * JNI: true if this static non-synchronized native method (that has no
+     * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore
+     * uses this.
+     */
+    bool fastJni;
+
+    /*
+     * JNI: true if this method has no reference arguments. This lets the JNI
+     * bridge avoid scanning the shorty for direct pointers that need to be
+     * converted to local references.
+     *
+     * TODO: replace this with a list of indexes of the reference arguments.
+     */
+    bool noRef;
+
+    /*
+     * JNI: true if we should log entry and exit. This is the only way
+     * developers can log the local references that are passed into their code.
+     * Used for debugging JNI problems in third-party code.
+     */
+    bool shouldTrace;
+
+    /*
+     * Register map data, if available.  This will point into the DEX file
+     * if the data was computed during pre-verification, or into the
+     * linear alloc area if not.
+     */
+    const RegisterMap* registerMap;
+
+    /* set if method was called during method profiling */
+    bool            inProfile;
+};
+
+u4 dvmGetMethodIdx(const Method* method);
+
+
+/*
+ * Find a method within a class.  The superclass is not searched.
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+    const char* methodName);
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+
+
+/*
+ * Find a method within a class hierarchy.
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* descriptor);
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+    const char* methodName, const char* signature);
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto);
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+    const char* methodName, const DexProto* proto);
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+    const DexProto* proto);
+
+/*
+ * Find a method in an interface hierarchy.
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor);
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto);
+
+/*
+ * Find the implementation of "meth" in "clazz".
+ *
+ * Returns NULL and throws an exception if not found.
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+    const Method* meth);
+
+/*
+ * Get the source file associated with a method.
+ */
+extern "C" const char* dvmGetMethodSourceFile(const Method* meth);
+
+/*
+ * Find a field within a class.  The superclass is not searched.
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+
+/*
+ * Find a field in a class/interface hierarchy.
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+    const char* fieldName, const char* signature);
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+    const char* signature);
+
+/*
+ * Find a field and return the byte offset from the object pointer.  Only
+ * searches the specified class, not the superclass.
+ *
+ * Returns -1 on failure.
+ */
+INLINE int dvmFindFieldOffset(const ClassObject* clazz,
+    const char* fieldName, const char* signature)
+{
+    InstField* pField = dvmFindInstanceField(clazz, fieldName, signature);
+    if (pField == NULL)
+        return -1;
+    else
+        return pField->byteOffset;
+}
+
+/*
+ * Helpers.
+ */
+INLINE bool dvmIsPublicMethod(const Method* method) {
+    return (method->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsPrivateMethod(const Method* method) {
+    return (method->accessFlags & ACC_PRIVATE) != 0;
+}
+INLINE bool dvmIsStaticMethod(const Method* method) {
+    return (method->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsSynchronizedMethod(const Method* method) {
+    return (method->accessFlags & ACC_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsDeclaredSynchronizedMethod(const Method* method) {
+    return (method->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsFinalMethod(const Method* method) {
+    return (method->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsNativeMethod(const Method* method) {
+    return (method->accessFlags & ACC_NATIVE) != 0;
+}
+INLINE bool dvmIsAbstractMethod(const Method* method) {
+    return (method->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsSyntheticMethod(const Method* method) {
+    return (method->accessFlags & ACC_SYNTHETIC) != 0;
+}
+INLINE bool dvmIsMirandaMethod(const Method* method) {
+    return (method->accessFlags & ACC_MIRANDA) != 0;
+}
+INLINE bool dvmIsConstructorMethod(const Method* method) {
+    return *method->name == '<';
+}
+/* Dalvik puts private, static, and constructors into non-virtual table */
+INLINE bool dvmIsDirectMethod(const Method* method) {
+    return dvmIsPrivateMethod(method) ||
+           dvmIsStaticMethod(method) ||
+           dvmIsConstructorMethod(method);
+}
+/* Get whether the given method has associated bytecode. This is the
+ * case for methods which are neither native nor abstract. */
+INLINE bool dvmIsBytecodeMethod(const Method* method) {
+    return (method->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+}
+
+INLINE bool dvmIsProtectedField(const Field* field) {
+    return (field->accessFlags & ACC_PROTECTED) != 0;
+}
+INLINE bool dvmIsStaticField(const Field* field) {
+    return (field->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsFinalField(const Field* field) {
+    return (field->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsVolatileField(const Field* field) {
+    return (field->accessFlags & ACC_VOLATILE) != 0;
+}
+
+INLINE bool dvmIsInterfaceClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_INTERFACE) != 0;
+}
+INLINE bool dvmIsPublicClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsFinalClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsAbstractClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsAnnotationClass(const ClassObject* clazz) {
+    return (clazz->accessFlags & ACC_ANNOTATION) != 0;
+}
+INLINE bool dvmIsPrimitiveClass(const ClassObject* clazz) {
+    return clazz->primitiveType != PRIM_NOT;
+}
+
+/* linked, here meaning prepared and resolved */
+INLINE bool dvmIsClassLinked(const ClassObject* clazz) {
+    return clazz->status >= CLASS_RESOLVED;
+}
+/* has class been verified? */
+INLINE bool dvmIsClassVerified(const ClassObject* clazz) {
+    return clazz->status >= CLASS_VERIFIED;
+}
+
+/*
+ * Return whether the given object is an instance of Class.
+ */
+INLINE bool dvmIsClassObject(const Object* obj) {
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    return IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISCLASS);
+}
+
+/*
+ * Return whether the given object is the class Class (that is, the
+ * unique class which is an instance of itself).
+ */
+INLINE bool dvmIsTheClassClass(const ClassObject* clazz) {
+    assert(clazz != NULL);
+    return IS_CLASS_FLAG_SET(clazz, CLASS_ISCLASS);
+}
+
+/*
+ * Get the associated code struct for a method. This returns NULL
+ * for non-bytecode methods.
+ */
+INLINE const DexCode* dvmGetMethodCode(const Method* meth) {
+    if (dvmIsBytecodeMethod(meth)) {
+        /*
+         * The insns field for a bytecode method actually points at
+         * &(DexCode.insns), so we can subtract back to get at the
+         * DexCode in front.
+         */
+        return (const DexCode*)
+            (((const u1*) meth->insns) - offsetof(DexCode, insns));
+    } else {
+        return NULL;
+    }
+}
+
+/*
+ * Get the size of the insns associated with a method. This returns 0
+ * for non-bytecode methods.
+ */
+INLINE u4 dvmGetMethodInsnsSize(const Method* meth) {
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    return (pCode == NULL) ? 0 : pCode->insnsSize;
+}
+
+/* debugging */
+void dvmDumpObject(const Object* obj);
+
+#endif  // DALVIK_OO_OBJECT_H_
diff --git a/vm/oo/ObjectInlines.h b/vm/oo/ObjectInlines.h
new file mode 100644
index 0000000..eb2e962
--- /dev/null
+++ b/vm/oo/ObjectInlines.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Helper functions to access data fields in Objects.
+ */
+#ifndef DALVIK_OO_OBJECTINLINES_H_
+#define DALVIK_OO_OBJECTINLINES_H_
+
+/*
+ * Store a single value in the array, and if the value isn't null,
+ * note in the write barrier.
+ */
+INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index,
+                                     Object* val) {
+    ((Object **)(void *)(obj)->contents)[index] = val;
+    if (val != NULL) {
+        dvmWriteBarrierArray(obj, index, index + 1);
+    }
+}
+
+
+/*
+ * Field access functions.  Pass in the word offset from Field->byteOffset.
+ *
+ * We guarantee that long/double field data is 64-bit aligned, so it's safe
+ * to access them with ldrd/strd on ARM.
+ *
+ * The VM treats all fields as 32 or 64 bits, so the field set functions
+ * write 32 bits even if the underlying type is smaller.
+ *
+ * Setting Object types to non-null values includes a call to the
+ * write barrier.
+ */
+#define BYTE_OFFSET(_ptr, _offset)  ((void*) (((u1*)(_ptr)) + (_offset)))
+
+INLINE JValue* dvmFieldPtr(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset));
+}
+
+INLINE bool dvmGetFieldBoolean(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->z;
+}
+INLINE s1 dvmGetFieldByte(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->b;
+}
+INLINE s2 dvmGetFieldShort(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->s;
+}
+INLINE u2 dvmGetFieldChar(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->c;
+}
+INLINE s4 dvmGetFieldInt(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->i;
+}
+INLINE s8 dvmGetFieldLong(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->j;
+}
+INLINE float dvmGetFieldFloat(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->f;
+}
+INLINE double dvmGetFieldDouble(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->d;
+}
+INLINE Object* dvmGetFieldObject(const Object* obj, int offset) {
+    return ((JValue*)BYTE_OFFSET(obj, offset))->l;
+}
+INLINE bool dvmGetFieldBooleanVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (bool)android_atomic_acquire_load(ptr);
+}
+INLINE s1 dvmGetFieldByteVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (s1)android_atomic_acquire_load(ptr);
+}
+INLINE s2 dvmGetFieldShortVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (s2)android_atomic_acquire_load(ptr);
+}
+INLINE u2 dvmGetFieldCharVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return (u2)android_atomic_acquire_load(ptr);
+}
+INLINE s4 dvmGetFieldIntVolatile(const Object* obj, int offset) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    return android_atomic_acquire_load(ptr);
+}
+INLINE float dvmGetFieldFloatVolatile(const Object* obj, int offset) {
+    union { s4 ival; float fval; } alias;
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    alias.ival = android_atomic_acquire_load(ptr);
+    return alias.fval;
+}
+INLINE s8 dvmGetFieldLongVolatile(const Object* obj, int offset) {
+    const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
+    s8 val = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return val;
+}
+INLINE double dvmGetFieldDoubleVolatile(const Object* obj, int offset) {
+    union { s8 lval; double dval; } alias;
+    const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
+    alias.lval = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return alias.dval;
+}
+INLINE Object* dvmGetFieldObjectVolatile(const Object* obj, int offset) {
+    Object** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+    return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetFieldBoolean(Object* obj, int offset, bool val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldByte(Object* obj, int offset, s1 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldShort(Object* obj, int offset, s2 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldChar(Object* obj, int offset, u2 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldInt(Object* obj, int offset, s4 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldFloat(Object* obj, int offset, float val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->f = val;
+}
+INLINE void dvmSetFieldLong(Object* obj, int offset, s8 val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->j = val;
+}
+INLINE void dvmSetFieldDouble(Object* obj, int offset, double val) {
+    ((JValue*)BYTE_OFFSET(obj, offset))->d = val;
+}
+INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val) {
+    JValue* lhs = (JValue*)BYTE_OFFSET(obj, offset);
+    lhs->l = val;
+    if (val != NULL) {
+        dvmWriteBarrierField(obj, &lhs->l);
+    }
+}
+INLINE void dvmSetFieldIntVolatile(Object* obj, int offset, s4 val) {
+    s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+    /*
+     * TODO: add an android_atomic_synchronization_store() function and
+     * use it in the 32-bit volatile set handlers.  On some platforms we
+     * can use a fast atomic instruction and avoid the barriers.
+     */
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+}
+INLINE void dvmSetFieldBooleanVolatile(Object* obj, int offset, bool val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldByteVolatile(Object* obj, int offset, s1 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldShortVolatile(Object* obj, int offset, s2 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldCharVolatile(Object* obj, int offset, u2 val) {
+    dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldFloatVolatile(Object* obj, int offset, float val) {
+    union { s4 ival; float fval; } alias;
+    alias.fval = val;
+    dvmSetFieldIntVolatile(obj, offset, alias.ival);
+}
+INLINE void dvmSetFieldLongVolatile(Object* obj, int offset, s8 val) {
+    s8* addr = (s8*)BYTE_OFFSET(obj, offset);
+    dvmQuasiAtomicSwap64Sync(val, addr);
+}
+INLINE void dvmSetFieldDoubleVolatile(Object* obj, int offset, double val) {
+    union { s8 lval; double dval; } alias;
+    alias.dval = val;
+    dvmSetFieldLongVolatile(obj, offset, alias.lval);
+}
+INLINE void dvmSetFieldObjectVolatile(Object* obj, int offset, Object* val) {
+    Object** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+    if (val != NULL) {
+        dvmWriteBarrierField(obj, ptr);
+    }
+}
+
+/*
+ * Static field access functions.
+ */
+INLINE JValue* dvmStaticFieldPtr(const StaticField* sfield) {
+    return (JValue*)&sfield->value;
+}
+
+INLINE bool dvmGetStaticFieldBoolean(const StaticField* sfield) {
+    return sfield->value.z;
+}
+INLINE s1 dvmGetStaticFieldByte(const StaticField* sfield) {
+    return sfield->value.b;
+}
+INLINE s2 dvmGetStaticFieldShort(const StaticField* sfield) {
+    return sfield->value.s;
+}
+INLINE u2 dvmGetStaticFieldChar(const StaticField* sfield) {
+    return sfield->value.c;
+}
+INLINE s4 dvmGetStaticFieldInt(const StaticField* sfield) {
+    return sfield->value.i;
+}
+INLINE float dvmGetStaticFieldFloat(const StaticField* sfield) {
+    return sfield->value.f;
+}
+INLINE s8 dvmGetStaticFieldLong(const StaticField* sfield) {
+    return sfield->value.j;
+}
+INLINE double dvmGetStaticFieldDouble(const StaticField* sfield) {
+    return sfield->value.d;
+}
+INLINE Object* dvmGetStaticFieldObject(const StaticField* sfield) {
+    return sfield->value.l;
+}
+INLINE bool dvmGetStaticFieldBooleanVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (bool)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s1 dvmGetStaticFieldByteVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (s1)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s2 dvmGetStaticFieldShortVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (s2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE u2 dvmGetStaticFieldCharVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return (u2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s4 dvmGetStaticFieldIntVolatile(const StaticField* sfield) {
+    const s4* ptr = &(sfield->value.i);
+    return android_atomic_acquire_load((s4*)ptr);
+}
+INLINE float dvmGetStaticFieldFloatVolatile(const StaticField* sfield) {
+    union { s4 ival; float fval; } alias;
+    const s4* ptr = &(sfield->value.i);
+    alias.ival = android_atomic_acquire_load((s4*)ptr);
+    return alias.fval;
+}
+INLINE s8 dvmGetStaticFieldLongVolatile(const StaticField* sfield) {
+    const s8* addr = &sfield->value.j;
+    s8 val = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return val;
+}
+INLINE double dvmGetStaticFieldDoubleVolatile(const StaticField* sfield) {
+    union { s8 lval; double dval; } alias;
+    const s8* addr = &sfield->value.j;
+    alias.lval = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return alias.dval;
+}
+INLINE Object* dvmGetStaticFieldObjectVolatile(const StaticField* sfield) {
+    Object* const* ptr = &(sfield->value.l);
+    return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetStaticFieldBoolean(StaticField* sfield, bool val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldByte(StaticField* sfield, s1 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldShort(StaticField* sfield, s2 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldChar(StaticField* sfield, u2 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldInt(StaticField* sfield, s4 val) {
+    sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldFloat(StaticField* sfield, float val) {
+    sfield->value.f = val;
+}
+INLINE void dvmSetStaticFieldLong(StaticField* sfield, s8 val) {
+    sfield->value.j = val;
+}
+INLINE void dvmSetStaticFieldDouble(StaticField* sfield, double val) {
+    sfield->value.d = val;
+}
+INLINE void dvmSetStaticFieldObject(StaticField* sfield, Object* val) {
+    sfield->value.l = val;
+    if (val != NULL) {
+        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
+    }
+}
+INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) {
+    s4* ptr = &sfield->value.i;
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+}
+INLINE void dvmSetStaticFieldBooleanVolatile(StaticField* sfield, bool val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldByteVolatile(StaticField* sfield, s1 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldShortVolatile(StaticField* sfield, s2 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldCharVolatile(StaticField* sfield, u2 val) {
+    dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldFloatVolatile(StaticField* sfield, float val) {
+    union { s4 ival; float fval; } alias;
+    alias.fval = val;
+    dvmSetStaticFieldIntVolatile(sfield, alias.ival);
+}
+INLINE void dvmSetStaticFieldLongVolatile(StaticField* sfield, s8 val) {
+    s8* addr = &sfield->value.j;
+    dvmQuasiAtomicSwap64Sync(val, addr);
+}
+INLINE void dvmSetStaticFieldDoubleVolatile(StaticField* sfield, double val) {
+    union { s8 lval; double dval; } alias;
+    alias.dval = val;
+    dvmSetStaticFieldLongVolatile(sfield, alias.lval);
+}
+INLINE void dvmSetStaticFieldObjectVolatile(StaticField* sfield, Object* val) {
+    Object** ptr = &(sfield->value.l);
+    ANDROID_MEMBAR_STORE();
+    *ptr = val;
+    ANDROID_MEMBAR_FULL();
+    if (val != NULL) {
+        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
+    }
+}
+
+#endif  // DALVIK_OO_OBJECTINLINES_H_
diff --git a/vm/oo/Resolve.cpp b/vm/oo/Resolve.cpp
new file mode 100644
index 0000000..ab3de5b
--- /dev/null
+++ b/vm/oo/Resolve.cpp
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Resolve classes, methods, fields, and strings.
+ *
+ * According to the VM spec (v2 5.5), classes may be initialized by use
+ * of the "new", "getstatic", "putstatic", or "invokestatic" instructions.
+ * If we are resolving a static method or static field, we make the
+ * initialization check here.
+ *
+ * (NOTE: the verifier has its own resolve functions, which can be invoked
+ * if a class isn't pre-verified.  Those functions must not update the
+ * "resolved stuff" tables for static fields and methods, because they do
+ * not perform initialization.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Find the class corresponding to "classIdx", which maps to a class name
+ * string.  It might be in the same DEX file as "referrer", in a different
+ * DEX file, generated by a class loader, or generated by the VM (e.g.
+ * array classes).
+ *
+ * Because the DexTypeId is associated with the referring class' DEX file,
+ * we may have to resolve the same class more than once if it's referred
+ * to from classes in multiple DEX files.  This is a necessary property for
+ * DEX files associated with different class loaders.
+ *
+ * We cache a copy of the lookup in the DexFile's "resolved class" table,
+ * so future references to "classIdx" are faster.
+ *
+ * Note that "referrer" may be in the process of being linked.
+ *
+ * Traditional VMs might do access checks here, but in Dalvik the class
+ * "constant pool" is shared between all classes in the DEX file.  We rely
+ * on the verifier to do the checks for us.
+ *
+ * Does not initialize the class.
+ *
+ * "fromUnverifiedConstant" should only be set if this call is the direct
+ * result of executing a "const-class" or "instance-of" instruction, which
+ * use class constants not resolved by the bytecode verifier.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
+    bool fromUnverifiedConstant)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const char* className;
+
+    /*
+     * Check the table first -- this gets called from the other "resolve"
+     * methods.
+     */
+    resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+    if (resClass != NULL)
+        return resClass;
+
+    LOGVV("--- resolving class %u (referrer=%s cl=%p)",
+        classIdx, referrer->descriptor, referrer->classLoader);
+
+    /*
+     * Class hasn't been loaded yet, or is in the process of being loaded
+     * and initialized now.  Try to get a copy.  If we find one, put the
+     * pointer in the DexTypeId.  There isn't a race condition here --
+     * 32-bit writes are guaranteed atomic on all target platforms.  Worst
+     * case we have two threads storing the same value.
+     *
+     * If this is an array class, we'll generate it here.
+     */
+    className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+    if (className[0] != '\0' && className[1] == '\0') {
+        /* primitive type */
+        resClass = dvmFindPrimitiveClass(className[0]);
+    } else {
+        resClass = dvmFindClassNoInit(className, referrer->classLoader);
+    }
+
+    if (resClass != NULL) {
+        /*
+         * If the referrer was pre-verified, the resolved class must come
+         * from the same DEX or from a bootstrap class.  The pre-verifier
+         * makes assumptions that could be invalidated by a wacky class
+         * loader.  (See the notes at the top of oo/Class.c.)
+         *
+         * The verifier does *not* fail a class for using a const-class
+         * or instance-of instruction referring to an unresolveable class,
+         * because the result of the instruction is simply a Class object
+         * or boolean -- there's no need to resolve the class object during
+         * verification.  Instance field and virtual method accesses can
+         * break dangerously if we get the wrong class, but const-class and
+         * instance-of are only interesting at execution time.  So, if we
+         * we got here as part of executing one of the "unverified class"
+         * instructions, we skip the additional check.
+         *
+         * Ditto for class references from annotations and exception
+         * handler lists.
+         */
+        if (!fromUnverifiedConstant &&
+            IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
+        {
+            ClassObject* resClassCheck = resClass;
+            if (dvmIsArrayClass(resClassCheck))
+                resClassCheck = resClassCheck->elementClass;
+
+            if (referrer->pDvmDex != resClassCheck->pDvmDex &&
+                resClassCheck->classLoader != NULL)
+            {
+                ALOGW("Class resolved by unexpected DEX:"
+                     " %s(%p):%p ref [%s] %s(%p):%p",
+                    referrer->descriptor, referrer->classLoader,
+                    referrer->pDvmDex,
+                    resClass->descriptor, resClassCheck->descriptor,
+                    resClassCheck->classLoader, resClassCheck->pDvmDex);
+                ALOGW("(%s had used a different %s during pre-verification)",
+                    referrer->descriptor, resClass->descriptor);
+                dvmThrowIllegalAccessError(
+                    "Class ref in pre-verified class resolved to unexpected "
+                    "implementation");
+                return NULL;
+            }
+        }
+
+        LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d",
+            resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
+            referrer->classLoader, classIdx);
+
+        /*
+         * Add what we found to the list so we can skip the class search
+         * next time through.
+         *
+         * TODO: should we be doing this when fromUnverifiedConstant==true?
+         * (see comments at top of oo/Class.c)
+         */
+        dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+    } else {
+        /* not found, exception should be raised */
+        LOGVV("Class not found: %s",
+            dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+        assert(dvmCheckException(dvmThreadSelf()));
+    }
+
+    return resClass;
+}
+
+
+/*
+ * Find the method corresponding to "methodRef".
+ *
+ * We use "referrer" to find the DexFile with the constant pool that
+ * "methodRef" is an index into.  We also use its class loader.  The method
+ * being resolved may very well be in a different DEX file.
+ *
+ * If this is a static method, we ensure that the method's class is
+ * initialized.
+ */
+Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+    MethodType methodType)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexMethodId* pMethodId;
+    Method* resMethod;
+
+    assert(methodType != METHOD_INTERFACE);
+
+    LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
+        referrer->descriptor);
+    pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+    if (resClass == NULL) {
+        /* can't find the class that the method is a part of */
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    if (dvmIsInterfaceClass(resClass)) {
+        /* method is part of an interface */
+        dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                resClass->descriptor);
+        return NULL;
+    }
+
+    const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+    /*
+     * We need to chase up the class hierarchy to find methods defined
+     * in super-classes.  (We only want to check the current class
+     * if we're looking for a constructor; since DIRECT calls are only
+     * for constructors and private methods, we don't want to walk up.)
+     */
+    if (methodType == METHOD_DIRECT) {
+        resMethod = dvmFindDirectMethod(resClass, name, &proto);
+    } else if (methodType == METHOD_STATIC) {
+        resMethod = dvmFindDirectMethodHier(resClass, name, &proto);
+    } else {
+        resMethod = dvmFindVirtualMethodHier(resClass, name, &proto);
+    }
+
+    if (resMethod == NULL) {
+        std::string msg;
+        msg += resClass->descriptor;
+        msg += ".";
+        msg += name;
+        dvmThrowNoSuchMethodError(msg.c_str());
+        return NULL;
+    }
+
+    LOGVV("--- found method %d (%s.%s)",
+        methodIdx, resClass->descriptor, resMethod->name);
+
+    /* see if this is a pure-abstract method */
+    if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+        dvmThrowAbstractMethodError(name);
+        return NULL;
+    }
+
+    /*
+     * If we're the first to resolve this class, we need to initialize
+     * it now.  Only necessary for METHOD_STATIC.
+     */
+    if (methodType == METHOD_STATIC) {
+        if (!dvmIsClassInitialized(resMethod->clazz) &&
+            !dvmInitClass(resMethod->clazz))
+        {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        } else {
+            assert(!dvmCheckException(dvmThreadSelf()));
+        }
+    } else {
+        /*
+         * Edge case: if the <clinit> for a class creates an instance
+         * of itself, we will call <init> on a class that is still being
+         * initialized by us.
+         */
+        assert(dvmIsClassInitialized(resMethod->clazz) ||
+               dvmIsClassInitializing(resMethod->clazz));
+    }
+
+    /*
+     * If the class has been initialized, add a pointer to our data structure
+     * so we don't have to jump through the hoops again.  If this is a
+     * static method and the defining class is still initializing (i.e. this
+     * thread is executing <clinit>), don't do the store, otherwise other
+     * threads could call the method without waiting for class init to finish.
+     */
+    if (methodType == METHOD_STATIC && !dvmIsClassInitialized(resMethod->clazz))
+    {
+        LOGVV("--- not caching resolved method %s.%s (class init=%d/%d)",
+            resMethod->clazz->descriptor, resMethod->name,
+            dvmIsClassInitializing(resMethod->clazz),
+            dvmIsClassInitialized(resMethod->clazz));
+    } else {
+        dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+    }
+
+    return resMethod;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexMethodId* pMethodId;
+    Method* resMethod;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)",
+        methodIdx, referrer->descriptor);
+    pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+    if (resClass == NULL) {
+        /* can't find the class that the method is a part of */
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    if (!dvmIsInterfaceClass(resClass)) {
+        /* whoops */
+        dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+                resClass->descriptor);
+        return NULL;
+    }
+
+    /*
+     * This is the first time the method has been resolved.  Set it in our
+     * resolved-method structure.  It always resolves to the same thing,
+     * so looking it up and storing it doesn't create a race condition.
+     *
+     * If we scan into the interface's superclass -- which is always
+     * java/lang/Object -- we will catch things like:
+     *   interface I ...
+     *   I myobj = (something that implements I)
+     *   myobj.hashCode()
+     * However, the Method->methodIndex will be an offset into clazz->vtable,
+     * rather than an offset into clazz->iftable.  The invoke-interface
+     * code can test to see if the method returned is abstract or concrete,
+     * and use methodIndex accordingly.  I'm not doing this yet because
+     * (a) we waste time in an unusual case, and (b) we're probably going
+     * to fix it in the DEX optimizer.
+     *
+     * We do need to scan the superinterfaces, in case we're invoking a
+     * superinterface method on an interface reference.  The class in the
+     * DexTypeId is for the static type of the object, not the class in
+     * which the method is first defined.  We have the full, flattened
+     * list in "iftable".
+     */
+    const char* methodName =
+        dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+    LOGVV("+++ looking for '%s' in resClass='%s'", methodName, resClass->descriptor);
+    resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
+    if (resMethod == NULL) {
+        std::string msg;
+        msg += resClass->descriptor;
+        msg += ".";
+        msg += methodName;
+        dvmThrowNoSuchMethodError(msg.c_str());
+        return NULL;
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)",
+        methodIdx, resClass->descriptor, resMethod->name);
+
+    /* we're expecting this to be abstract */
+    assert(dvmIsAbstractMethod(resMethod));
+
+    /* interface methods are always public; no need to check access */
+
+    /*
+     * The interface class *may* be initialized.  According to VM spec
+     * v2 2.17.4, the interfaces a class refers to "need not" be initialized
+     * when the class is initialized.
+     *
+     * It isn't necessary for an interface class to be initialized before
+     * we resolve methods on that interface.
+     *
+     * We choose not to do the initialization now.
+     */
+    //assert(dvmIsClassInitialized(resMethod->clazz));
+
+    /*
+     * Add a pointer to our data structure so we don't have to jump
+     * through the hoops again.
+     *
+     * As noted above, no need to worry about whether the interface that
+     * defines the method has been or is currently executing <clinit>.
+     */
+    dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+
+    return resMethod;
+}
+
+/*
+ * Resolve an instance field reference.
+ *
+ * Returns NULL and throws an exception on error (no such field, illegal
+ * access).
+ */
+InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexFieldId* pFieldId;
+    InstField* resField;
+
+    LOGVV("--- resolving field %u (referrer=%s cl=%p)",
+        ifieldIdx, referrer->descriptor, referrer->classLoader);
+
+    pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+    /*
+     * Find the field's class.
+     */
+    resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+    if (resClass == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    resField = dvmFindInstanceFieldHier(resClass,
+        dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+        dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+    if (resField == NULL) {
+        dvmThrowNoSuchFieldError(
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+        return NULL;
+    }
+
+    /*
+     * Class must be initialized by now (unless verifier is buggy).  We
+     * could still be in the process of initializing it if the field
+     * access is from a static initializer.
+     */
+    assert(dvmIsClassInitialized(resField->clazz) ||
+           dvmIsClassInitializing(resField->clazz));
+
+    /*
+     * The class is initialized (or initializing), the field has been
+     * found.  Add a pointer to our data structure so we don't have to
+     * jump through the hoops again.
+     *
+     * Anything that uses the resolved table entry must have an instance
+     * of the class, so any class init activity has already happened (or
+     * been deliberately bypassed when <clinit> created an instance).
+     * So it's always okay to update the table.
+     */
+    dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField);
+    LOGVV("    field %u is %s.%s",
+        ifieldIdx, resField->clazz->descriptor, resField->name);
+
+    return resField;
+}
+
+/*
+ * Resolve a static field reference.  The DexFile format doesn't distinguish
+ * between static and instance field references, so the "resolved" pointer
+ * in the Dex struct will have the wrong type.  We trivially cast it here.
+ *
+ * Causes the field's class to be initialized.
+ */
+StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    ClassObject* resClass;
+    const DexFieldId* pFieldId;
+    StaticField* resField;
+
+    pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+    /*
+     * Find the field's class.
+     */
+    resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+    if (resClass == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    resField = dvmFindStaticFieldHier(resClass,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+                dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+    if (resField == NULL) {
+        dvmThrowNoSuchFieldError(
+            dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+        return NULL;
+    }
+
+    /*
+     * If we're the first to resolve the field in which this class resides,
+     * we need to do it now.  Note that, if the field was inherited from
+     * a superclass, it is not necessarily the same as "resClass".
+     */
+    if (!dvmIsClassInitialized(resField->clazz) &&
+        !dvmInitClass(resField->clazz))
+    {
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    /*
+     * If the class has been initialized, add a pointer to our data structure
+     * so we don't have to jump through the hoops again.  If it's still
+     * initializing (i.e. this thread is executing <clinit>), don't do
+     * the store, otherwise other threads could use the field without waiting
+     * for class init to finish.
+     */
+    if (dvmIsClassInitialized(resField->clazz)) {
+        dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    } else {
+        LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)",
+            resField->clazz->descriptor, resField->name,
+            dvmIsClassInitializing(resField->clazz),
+            dvmIsClassInitialized(resField->clazz));
+    }
+
+    return resField;
+}
+
+
+/*
+ * Resolve a string reference.
+ *
+ * Finding the string is easy.  We need to return a reference to a
+ * java/lang/String object, not a bunch of characters, which means the
+ * first time we get here we need to create an interned string.
+ */
+StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
+{
+    DvmDex* pDvmDex = referrer->pDvmDex;
+    StringObject* strObj;
+    StringObject* internStrObj;
+    const char* utf8;
+    u4 utf16Size;
+
+    LOGVV("+++ resolving string, referrer is %s", referrer->descriptor);
+
+    /*
+     * Create a UTF-16 version so we can trivially compare it to what's
+     * already interned.
+     */
+    utf8 = dexStringAndSizeById(pDvmDex->pDexFile, stringIdx, &utf16Size);
+    strObj = dvmCreateStringFromCstrAndLength(utf8, utf16Size);
+    if (strObj == NULL) {
+        /* ran out of space in GC heap? */
+        assert(dvmCheckException(dvmThreadSelf()));
+        goto bail;
+    }
+
+    /*
+     * Add it to the intern list.  The return value is the one in the
+     * intern list, which (due to race conditions) may or may not be
+     * the one we just created.  The intern list is synchronized, so
+     * there will be only one "live" version.
+     *
+     * By requesting an immortal interned string, we guarantee that
+     * the returned object will never be collected by the GC.
+     *
+     * A NULL return here indicates some sort of hashing failure.
+     */
+    internStrObj = dvmLookupImmortalInternedString(strObj);
+    dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+    strObj = internStrObj;
+    if (strObj == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        goto bail;
+    }
+
+    /* save a reference so we can go straight to the object next time */
+    dvmDexSetResolvedString(pDvmDex, stringIdx, strObj);
+
+bail:
+    return strObj;
+}
+
+/*
+ * For debugging: return a string representing the methodType.
+ */
+const char* dvmMethodTypeStr(MethodType methodType)
+{
+    switch (methodType) {
+    case METHOD_DIRECT:         return "direct";
+    case METHOD_STATIC:         return "static";
+    case METHOD_VIRTUAL:        return "virtual";
+    case METHOD_INTERFACE:      return "interface";
+    case METHOD_UNKNOWN:        return "UNKNOWN";
+    }
+    assert(false);
+    return "BOGUS";
+}
diff --git a/vm/oo/Resolve.h b/vm/oo/Resolve.h
new file mode 100644
index 0000000..56a35b7
--- /dev/null
+++ b/vm/oo/Resolve.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Resolve "constant pool" references into pointers to VM structs.
+ */
+#ifndef DALVIK_OO_RESOLVE_H_
+#define DALVIK_OO_RESOLVE_H_
+
+/*
+ * "Direct" and "virtual" methods are stored independently.  The type of call
+ * used to invoke the method determines which list we search, and whether
+ * we travel up into superclasses.
+ *
+ * (<clinit>, <init>, and methods declared "private" or "static" are stored
+ * in the "direct" list.  All others are stored in the "virtual" list.)
+ */
+enum MethodType {
+    METHOD_UNKNOWN  = 0,
+    METHOD_DIRECT,      // <init>, private
+    METHOD_STATIC,      // static
+    METHOD_VIRTUAL,     // virtual, super
+    METHOD_INTERFACE    // interface
+};
+
+/*
+ * Resolve a class, given the referring class and a constant pool index
+ * for the DexTypeId.
+ *
+ * Does not initialize the class.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" ClassObject* dvmResolveClass(const ClassObject* referrer,
+                                        u4 classIdx,
+                                        bool fromUnverifiedConstant);
+
+/*
+ * Resolve a direct, static, or virtual method.
+ *
+ * Can cause the method's class to be initialized if methodType is
+ * METHOD_STATIC.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+                                    MethodType methodType);
+
+/*
+ * Resolve an interface method.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx);
+
+/*
+ * Resolve an instance field.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" InstField* dvmResolveInstField(const ClassObject* referrer,
+                                          u4 ifieldIdx);
+
+/*
+ * Resolve a static field.
+ *
+ * Causes the field's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" StaticField* dvmResolveStaticField(const ClassObject* referrer,
+                                              u4 sfieldIdx);
+
+/*
+ * Resolve a "const-string" reference.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+extern "C" StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx);
+
+/*
+ * Return debug string constant for enum.
+ */
+const char* dvmMethodTypeStr(MethodType methodType);
+
+#endif  // DALVIK_OO_RESOLVE_H_
diff --git a/vm/oo/TypeCheck.cpp b/vm/oo/TypeCheck.cpp
new file mode 100644
index 0000000..5228512
--- /dev/null
+++ b/vm/oo/TypeCheck.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1.  If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x)  (x)
+//#define BOOL_TO_INT(x)  ((x) ? 1 : 0)
+
+/*
+ * Number of entries in instanceof cache.  MUST be a power of 2.
+ */
+#define INSTANCEOF_CACHE_SIZE   1024
+
+
+/*
+ * Allocate cache.
+ */
+bool dvmInstanceofStartup()
+{
+    gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
+    if (gDvm.instanceofCache == NULL)
+        return false;
+    return true;
+}
+
+/*
+ * Discard the cache.
+ */
+void dvmInstanceofShutdown()
+{
+    dvmFreeAtomicCache(gDvm.instanceofCache);
+}
+
+
+/*
+ * Determine whether "sub" is an instance of "clazz", where both of these
+ * are array classes.
+ *
+ * Consider an array class, e.g. Y[][], where Y is a subclass of X.
+ *   Y[][] instanceof Y[][]        --> true (identity)
+ *   Y[][] instanceof X[][]        --> true (element superclass)
+ *   Y[][] instanceof Y            --> false
+ *   Y[][] instanceof Y[]          --> false
+ *   Y[][] instanceof Object       --> true (everything is an object)
+ *   Y[][] instanceof Object[]     --> true
+ *   Y[][] instanceof Object[][]   --> true
+ *   Y[][] instanceof Object[][][] --> false (too many []s)
+ *   Y[][] instanceof Serializable     --> true (all arrays are Serializable)
+ *   Y[][] instanceof Serializable[]   --> true
+ *   Y[][] instanceof Serializable[][] --> false (unless Y is Serializable)
+ *
+ * Don't forget about primitive types.
+ *   int[] instanceof Object[]     --> false
+ *
+ * "subElemClass" is sub->elementClass.
+ *
+ * "subDim" is usually just sub->dim, but for some kinds of checks we want
+ * to pass in a non-array class and pretend that it's an array.
+ */
+static int isArrayInstanceOfArray(const ClassObject* subElemClass, int subDim,
+    const ClassObject* clazz)
+{
+    //assert(dvmIsArrayClass(sub));
+    assert(dvmIsArrayClass(clazz));
+
+    /* "If T is an array type TC[]... one of the following must be true:
+     *   TC and SC are the same primitive type.
+     *   TC and SC are reference types and type SC can be cast to TC [...]."
+     *
+     * We need the class objects for the array elements.  For speed we
+     * tucked them into the class object.
+     */
+    assert(subDim > 0 && clazz->arrayDim > 0);
+    if (subDim == clazz->arrayDim) {
+        /*
+         * See if "sub" is an instance of "clazz".  This handles the
+         * interfaces, java.lang.Object, superclassing, etc.
+         */
+        return dvmInstanceof(subElemClass, clazz->elementClass);
+    } else if (subDim > clazz->arrayDim) {
+        /*
+         * The thing we might be an instance of has fewer dimensions.  It
+         * must be an Object or array of Object, or a standard array
+         * interface or array of standard array interfaces (the standard
+         * interfaces being java/lang/Cloneable and java/io/Serializable).
+         */
+        if (dvmIsInterfaceClass(clazz->elementClass)) {
+            /*
+             * See if the class implements its base element.  We know the
+             * base element is an interface; if the array class implements
+             * it, we know it's a standard array interface.
+             */
+            return dvmImplements(clazz, clazz->elementClass);
+        } else {
+            /*
+             * See if this is an array of Object, Object[], etc.  We know
+             * that the superclass of an array is always Object, so we
+             * just compare the element type to that.
+             */
+            return (clazz->elementClass == clazz->super);
+        }
+    } else {
+        /*
+         * Too many []s.
+         */
+        return false;
+    }
+}
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz", where "sub" is an
+ * array class.
+ *
+ * "clazz" could be an array class, interface, or simple class.
+ */
+static int isArrayInstanceOf(const ClassObject* sub, const ClassObject* clazz)
+{
+    assert(dvmIsArrayClass(sub));
+
+    /* "If T is an interface type, T must be one of the interfaces
+     * implemented by arrays."
+     *
+     * I'm not checking that here, because dvmInstanceof tests for
+     * interfaces first, and the generic dvmImplements stuff should
+     * work correctly.
+     */
+    assert(!dvmIsInterfaceClass(clazz));     /* make sure */
+
+    /* "If T is a class type, then T must be Object."
+     *
+     * The superclass of an array is always java.lang.Object, so just
+     * compare against that.
+     */
+    if (!dvmIsArrayClass(clazz))
+        return BOOL_TO_INT(clazz == sub->super);
+
+    /*
+     * If T is an array type TC[] ...
+     */
+    return isArrayInstanceOfArray(sub->elementClass, sub->arrayDim, clazz);
+}
+
+
+/*
+ * Returns 1 (true) if "clazz" is an implementation of "interface".
+ *
+ * "clazz" could be a class or an interface.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface)
+{
+    int i;
+
+    assert(dvmIsInterfaceClass(interface));
+
+    /*
+     * All interfaces implemented directly and by our superclass, and
+     * recursively all super-interfaces of those interfaces, are listed
+     * in "iftable", so we can just do a linear scan through that.
+     */
+    for (i = 0; i < clazz->iftableCount; i++) {
+        if (clazz->iftable[i].clazz == interface)
+            return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Determine whether or not we can put an object into an array, based on
+ * the class hierarchy.  The object might itself by an array, which means
+ * we have to pay attention to the array instanceof rules.
+ *
+ * Note that "objectClass" could be an array, but objectClass->elementClass
+ * is always a non-array type.
+ */
+bool dvmCanPutArrayElement(const ClassObject* objectClass,
+    const ClassObject* arrayClass)
+{
+    if (dvmIsArrayClass(objectClass)) {
+        /*
+         * We're stuffing an array into an array.  We want to see if the
+         * elements of "arrayClass" are compatible with "objectClass".
+         * We bump up the number of dimensions in "objectClass" so that we
+         * can compare the two directly.
+         */
+        return isArrayInstanceOfArray(objectClass->elementClass,
+                    objectClass->arrayDim + 1, arrayClass);
+    } else {
+        /*
+         * We're putting a non-array element into an array.  We need to
+         * test to see if the elements are compatible.  The easiest way
+         * to do that is to "arrayify" it and use the standard array
+         * compatibility check.
+         */
+        return isArrayInstanceOfArray(objectClass, 1, arrayClass);
+    }
+}
+
+
+/*
+ * Perform the instanceof calculation.
+ */
+static inline int isInstanceof(const ClassObject* instance,
+    const ClassObject* clazz)
+{
+    if (dvmIsInterfaceClass(clazz)) {
+        return dvmImplements(instance, clazz);
+    } else if (dvmIsArrayClass(instance)) {
+        return isArrayInstanceOf(instance, clazz);
+    } else {
+        return dvmIsSubClass(instance, clazz);
+    }
+}
+
+
+/*
+ * Do the instanceof calculation, pulling the result from the cache if
+ * possible.
+ */
+int dvmInstanceofNonTrivial(const ClassObject* instance,
+    const ClassObject* clazz)
+{
+#define ATOMIC_CACHE_CALC isInstanceof(instance, clazz)
+#define ATOMIC_CACHE_NULL_ALLOWED true
+    return ATOMIC_CACHE_LOOKUP(gDvm.instanceofCache,
+                INSTANCEOF_CACHE_SIZE, instance, clazz);
+#undef ATOMIC_CACHE_CALC
+}
diff --git a/vm/oo/TypeCheck.h b/vm/oo/TypeCheck.h
new file mode 100644
index 0000000..c3f19ea
--- /dev/null
+++ b/vm/oo/TypeCheck.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#ifndef DALVIK_OO_TYPECHECK_H_
+#define DALVIK_OO_TYPECHECK_H_
+
+/* VM startup/shutdown */
+bool dvmInstanceofStartup(void);
+void dvmInstanceofShutdown(void);
+
+
+/* used by dvmInstanceof; don't call */
+extern "C" int dvmInstanceofNonTrivial(const ClassObject* instance,
+                                       const ClassObject* clazz);
+
+/*
+ * Determine whether "instance" is an instance of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmInstanceof(const ClassObject* instance, const ClassObject* clazz)
+{
+    if (instance == clazz) {
+        if (CALC_CACHE_STATS)
+            gDvm.instanceofCache->trivial++;
+        return 1;
+    } else
+        return dvmInstanceofNonTrivial(instance, clazz);
+}
+
+/*
+ * Determine whether a class implements an interface.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface);
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmIsSubClass(const ClassObject* sub, const ClassObject* clazz) {
+    do {
+        /*printf("###### sub='%s' clazz='%s'\n", sub->name, clazz->name);*/
+        if (sub == clazz)
+            return 1;
+        sub = sub->super;
+    } while (sub != NULL);
+
+    return 0;
+}
+
+/*
+ * Determine whether or not we can store an object into an array, based
+ * on the classes of the two.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+extern "C" bool dvmCanPutArrayElement(const ClassObject* elemClass,
+    const ClassObject* arrayClass);
+
+#endif  // DALVIK_OO_TYPECHECK_H_
diff --git a/vm/os/android.cpp b/vm/os/android.cpp
new file mode 100644
index 0000000..b37bb70
--- /dev/null
+++ b/vm/os/android.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "os.h"
+
+#include "Dalvik.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <system/thread_defs.h>
+#include <cutils/sched_policy.h>
+
+/*
+ * Conversion map for "nice" values.
+ *
+ * We use Android thread priority constants to be consistent with the rest
+ * of the system.  In some cases adjacent entries may overlap.
+ */
+static const int kNiceValues[10] = {
+    ANDROID_PRIORITY_LOWEST,                /* 1 (MIN_PRIORITY) */
+    ANDROID_PRIORITY_BACKGROUND + 6,
+    ANDROID_PRIORITY_BACKGROUND + 3,
+    ANDROID_PRIORITY_BACKGROUND,
+    ANDROID_PRIORITY_NORMAL,                /* 5 (NORM_PRIORITY) */
+    ANDROID_PRIORITY_NORMAL - 2,
+    ANDROID_PRIORITY_NORMAL - 4,
+    ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+    ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+    ANDROID_PRIORITY_URGENT_DISPLAY         /* 10 (MAX_PRIORITY) */
+};
+
+void os_changeThreadPriority(Thread* thread, int newPriority)
+{
+    if (newPriority < 1 || newPriority > 10) {
+        ALOGW("bad priority %d", newPriority);
+        newPriority = 5;
+    }
+
+    int newNice = kNiceValues[newPriority-1];
+    pid_t pid = thread->systemTid;
+
+    if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+    } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+    }
+
+    if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
+        std::string threadName(dvmGetThreadName(thread));
+        ALOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s",
+        pid, threadName.c_str(), newPriority, newNice, strerror(errno));
+    } else {
+        ALOGV("setPriority(%d) to prio=%d(n=%d)", pid, newPriority, newNice);
+    }
+}
+
+int os_getThreadPriorityFromSystem()
+{
+    errno = 0;
+    int sysprio = getpriority(PRIO_PROCESS, 0);
+    if (sysprio == -1 && errno != 0) {
+        ALOGW("getpriority() failed: %s", strerror(errno));
+        return THREAD_NORM_PRIORITY;
+    }
+
+    int jprio = THREAD_MIN_PRIORITY;
+    for (int i = 0; i < NELEM(kNiceValues); i++) {
+        if (sysprio >= kNiceValues[i]) {
+            break;
+        }
+        jprio++;
+    }
+    if (jprio > THREAD_MAX_PRIORITY) {
+        jprio = THREAD_MAX_PRIORITY;
+    }
+    return jprio;
+}
+
+int os_raiseThreadPriority()
+{
+    /* Get the priority (the "nice" value) of the current thread.  The
+     * getpriority() call can legitimately return -1, so we have to
+     * explicitly test errno.
+     */
+    errno = 0;
+    int oldThreadPriority = getpriority(PRIO_PROCESS, 0);
+    if (errno != 0) {
+        ALOGI("getpriority(self) failed: %s", strerror(errno));
+    } else if (oldThreadPriority > ANDROID_PRIORITY_NORMAL) {
+        /* Current value is numerically greater than "normal", which
+         * in backward UNIX terms means lower priority.
+         */
+        if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+            set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+        }
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
+            ALOGI("Unable to elevate priority from %d to %d",
+                    oldThreadPriority, ANDROID_PRIORITY_NORMAL);
+        } else {
+            /*
+             * The priority has been elevated.  Return the old value
+             * so the caller can restore it later.
+             */
+            ALOGV("Elevating priority from %d to %d",
+                    oldThreadPriority, ANDROID_PRIORITY_NORMAL);
+            return oldThreadPriority;
+        }
+    }
+    return INT_MAX;
+}
+
+void os_lowerThreadPriority(int oldThreadPriority)
+{
+    if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
+        ALOGW("Unable to reset priority to %d: %s",
+                oldThreadPriority, strerror(errno));
+    } else {
+        ALOGV("Reset priority to %d", oldThreadPriority);
+    }
+    if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+        set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+    }
+}
diff --git a/vm/os/linux.cpp b/vm/os/linux.cpp
new file mode 100644
index 0000000..172cd05
--- /dev/null
+++ b/vm/os/linux.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "os.h"
+
+#include "Dalvik.h"
+
+int os_raiseThreadPriority()
+{
+    return 0;
+}
+
+void os_lowerThreadPriority(int oldThreadPriority)
+{
+    // Do nothing.
+}
+
+void os_changeThreadPriority(Thread* thread, int newPriority)
+{
+    // Do nothing.
+}
+
+int os_getThreadPriorityFromSystem()
+{
+    return THREAD_NORM_PRIORITY;
+}
diff --git a/vm/os/os.h b/vm/os/os.h
new file mode 100644
index 0000000..19e2a61
--- /dev/null
+++ b/vm/os/os.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+struct Thread;
+
+/*
+ * Raises the scheduling priority of the current thread.  Returns the
+ * original priority if successful, or INT_MAX on failure.
+ * Use os_lowerThreadPriority to undo.
+ *
+ * TODO: does the GC really need this?
+ */
+int os_raiseThreadPriority();
+
+/*
+ * Sets the current thread scheduling priority. Used to undo the effects
+ * of an earlier call to os_raiseThreadPriority.
+ *
+ * TODO: does the GC really need this?
+ */
+void os_lowerThreadPriority(int oldThreadPriority);
+
+/*
+ * Changes the priority of a system thread to match that of the Thread object.
+ *
+ * We map a priority value from 1-10 to Linux "nice" values, where lower
+ * numbers indicate higher priority.
+ */
+void os_changeThreadPriority(Thread* thread, int newPriority);
+
+/*
+ * Returns the thread priority for the current thread by querying the system.
+ * This is useful when attaching a thread through JNI.
+ *
+ * Returns a value from 1 to 10 (compatible with java.lang.Thread values).
+ */
+int os_getThreadPriorityFromSystem();
diff --git a/vm/reflect/Annotation.cpp b/vm/reflect/Annotation.cpp
new file mode 100644
index 0000000..bbd58e9
--- /dev/null
+++ b/vm/reflect/Annotation.cpp
@@ -0,0 +1,2299 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+/*
+ * Annotations.
+ *
+ * We're not expecting to make much use of runtime annotations, so speed vs.
+ * space choices are weighted heavily toward small size.
+ *
+ * It would have been nice to treat "system" annotations in the same way
+ * we do "real" annotations, but that doesn't work.  The chief difficulty
+ * is that some of them have member types that are not legal in annotations,
+ * such as Method and Annotation.  Another source of pain comes from the
+ * AnnotationDefault annotation, which by virtue of being an annotation
+ * could itself have default values, requiring some additional checks to
+ * prevent recursion.
+ *
+ * It's simpler, and more efficient, to handle the system annotations
+ * entirely inside the VM.  There are empty classes defined for the system
+ * annotation types, but their only purpose is to allow the system
+ * annotations to share name space with standard annotations.
+ */
+#include "Dalvik.h"
+
+// fwd
+static Object* processEncodedAnnotation(const ClassObject* clazz,\
+    const u1** pPtr);
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr);
+
+/*
+ * System annotation descriptors.
+ */
+static const char* kDescrAnnotationDefault
+                                    = "Ldalvik/annotation/AnnotationDefault;";
+static const char* kDescrEnclosingClass
+                                    = "Ldalvik/annotation/EnclosingClass;";
+static const char* kDescrEnclosingMethod
+                                    = "Ldalvik/annotation/EnclosingMethod;";
+static const char* kDescrInnerClass = "Ldalvik/annotation/InnerClass;";
+static const char* kDescrMemberClasses
+                                    = "Ldalvik/annotation/MemberClasses;";
+static const char* kDescrSignature  = "Ldalvik/annotation/Signature;";
+static const char* kDescrThrows     = "Ldalvik/annotation/Throws;";
+
+/*
+ * Read an unsigned LEB128 value from a buffer.  Advances "pBuf".
+ */
+static u4 readUleb128(const u1** pBuf)
+{
+    u4 result = 0;
+    int shift = 0;
+    const u1* buf = *pBuf;
+    u1 val;
+
+    do {
+        /*
+         * Worst-case on bad data is we read too much data and return a bogus
+         * result.  Safe to assume that we will encounter a byte with its
+         * high bit clear before the end of the mapped file.
+         */
+        assert(shift < 32);
+
+        val = *buf++;
+        result |= (val & 0x7f) << shift;
+        shift += 7;
+    } while ((val & 0x80) != 0);
+
+    *pBuf = buf;
+    return result;
+}
+
+/*
+ * Get the annotations directory item.
+ */
+static const DexAnnotationsDirectoryItem* getAnnoDirectory(DexFile* pDexFile,
+    const ClassObject* clazz)
+{
+    const DexClassDef* pClassDef;
+
+    /*
+     * Find the class def in the DEX file.  For better performance we should
+     * stash this in the ClassObject.
+     */
+    pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+    assert(pClassDef != NULL);
+    return dexGetAnnotationsDirectoryItem(pDexFile, pClassDef);
+}
+
+/*
+ * Return a zero-length array of Annotation objects.
+ *
+ * TODO: this currently allocates a new array each time, but I think we
+ * can get away with returning a canonical copy.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArray()
+{
+    return dvmAllocArrayByClass(
+        gDvm.classJavaLangAnnotationAnnotationArray, 0, ALLOC_DEFAULT);
+}
+
+/*
+ * Return an array of empty arrays of Annotation objects.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArrayArray(int numElements)
+{
+    Thread* self = dvmThreadSelf();
+    ArrayObject* arr;
+    int i;
+
+    arr = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArrayArray,
+            numElements, ALLOC_DEFAULT);
+    if (arr != NULL) {
+        ArrayObject** elems = (ArrayObject**)(void*)arr->contents;
+        for (i = 0; i < numElements; i++) {
+            elems[i] = emptyAnnoArray();
+            dvmReleaseTrackedAlloc((Object*)elems[i], self);
+        }
+    }
+
+    return arr;
+}
+
+/*
+ * Read a signed integer.  "zwidth" is the zero-based byte count.
+ */
+static s4 readSignedInt(const u1* ptr, int zwidth)
+{
+    s4 val = 0;
+    int i;
+
+    for (i = zwidth; i >= 0; --i)
+        val = ((u4)val >> 8) | (((s4)*ptr++) << 24);
+    val >>= (3 - zwidth) * 8;
+
+    return val;
+}
+
+/*
+ * Read an unsigned integer.  "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u4 readUnsignedInt(const u1* ptr, int zwidth, bool fillOnRight)
+{
+    u4 val = 0;
+    int i;
+
+    if (!fillOnRight) {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u4)*ptr++) << 24);
+        val >>= (3 - zwidth) * 8;
+    } else {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u4)*ptr++) << 24);
+    }
+    return val;
+}
+
+/*
+ * Read a signed long.  "zwidth" is the zero-based byte count.
+ */
+static s8 readSignedLong(const u1* ptr, int zwidth)
+{
+    s8 val = 0;
+    int i;
+
+    for (i = zwidth; i >= 0; --i)
+        val = ((u8)val >> 8) | (((s8)*ptr++) << 56);
+    val >>= (7 - zwidth) * 8;
+
+    return val;
+}
+
+/*
+ * Read an unsigned long.  "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u8 readUnsignedLong(const u1* ptr, int zwidth, bool fillOnRight)
+{
+    u8 val = 0;
+    int i;
+
+    if (!fillOnRight) {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u8)*ptr++) << 56);
+        val >>= (7 - zwidth) * 8;
+    } else {
+        for (i = zwidth; i >= 0; --i)
+            val = (val >> 8) | (((u8)*ptr++) << 56);
+    }
+    return val;
+}
+
+
+/*
+ * ===========================================================================
+ *      Element extraction
+ * ===========================================================================
+ */
+
+/*
+ * An annotation in "clazz" refers to a method by index.  This just gives
+ * us the name of the class and the name and signature of the method.  We
+ * need to find the method's class, and then find the method within that
+ * class.  If the method has been resolved before, we can just use the
+ * results of the previous lookup.
+ *
+ * Normally we do this as part of method invocation in the interpreter, which
+ * provides us with a bit of context: is it virtual or direct, do we need
+ * to initialize the class because it's a static method, etc.  We don't have
+ * that information here, so we have to do a bit of searching.
+ *
+ * Returns NULL if the method was not found (exception may be pending).
+ */
+static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx)
+{
+    DexFile* pDexFile;
+    ClassObject* resClass;
+    Method* resMethod;
+    const DexMethodId* pMethodId;
+    const char* name;
+
+    /* if we've already resolved this method, return it */
+    resMethod = dvmDexGetResolvedMethod(referrer->pDvmDex, methodIdx);
+    if (resMethod != NULL)
+        return resMethod;
+
+    pDexFile = referrer->pDvmDex->pDexFile;
+    pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    resClass = dvmResolveClass(referrer, pMethodId->classIdx, true);
+    if (resClass == NULL) {
+        /* note exception will be pending */
+        ALOGD("resolveAmbiguousMethod: unable to find class %d", methodIdx);
+        return NULL;
+    }
+    if (dvmIsInterfaceClass(resClass)) {
+        /* method is part of an interface -- not expecting that */
+        ALOGD("resolveAmbiguousMethod: method in interface?");
+        return NULL;
+    }
+
+    // TODO - consider a method access flag that indicates direct vs. virtual
+    name = dexStringById(pDexFile, pMethodId->nameIdx);
+
+    DexProto proto;
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+    if (name[0] == '<') {
+        /*
+         * Constructor or class initializer.  Only need to examine the
+         * "direct" list, and don't need to look up the class hierarchy.
+         */
+        resMethod = dvmFindDirectMethod(resClass, name, &proto);
+    } else {
+        /*
+         * Do a hierarchical scan for direct and virtual methods.
+         *
+         * This uses the search order from the VM spec (v2 5.4.3.3), which
+         * seems appropriate here.
+         */
+        resMethod = dvmFindMethodHier(resClass, name, &proto);
+    }
+
+    return resMethod;
+}
+
+/*
+ * constants for processAnnotationValue indicating what style of
+ * result is wanted
+ */
+enum AnnotationResultStyle {
+    kAllObjects,         /* return everything as an object */
+    kAllRaw,             /* return everything as a raw value or index */
+    kPrimitivesOrObjects /* return primitives as-is but the rest as objects */
+};
+
+/*
+ * Recursively process an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined.  It may be
+ * NULL when "resultStyle" is "kAllRaw".
+ *
+ * If "resultStyle" is "kAllObjects", the result will always be an Object of an
+ * appropriate type (in pValue->value.l).  For primitive types, the usual
+ * wrapper objects will be created.
+ *
+ * If "resultStyle" is "kAllRaw", numeric constants are stored directly into
+ * "pValue", and indexed values like String and Method are returned as
+ * indexes.  Complex values like annotations and arrays are not handled.
+ *
+ * If "resultStyle" is "kPrimitivesOrObjects", numeric constants are stored
+ * directly into "pValue", and everything else is constructed as an Object
+ * of appropriate type (in pValue->value.l).
+ *
+ * The caller must call dvmReleaseTrackedAlloc on returned objects, when
+ * using "kAllObjects" or "kPrimitivesOrObjects".
+ *
+ * Returns "true" on success, "false" if the value could not be processed
+ * or an object could not be allocated.  On allocation failure an exception
+ * will be raised.
+ */
+static bool processAnnotationValue(const ClassObject* clazz,
+    const u1** pPtr, AnnotationValue* pValue,
+    AnnotationResultStyle resultStyle)
+{
+    Thread* self = dvmThreadSelf();
+    Object* elemObj = NULL;
+    bool setObject = false;
+    const u1* ptr = *pPtr;
+    u1 valueType, valueArg;
+    int width;
+    u4 idx;
+
+    valueType = *ptr++;
+    valueArg = valueType >> kDexAnnotationValueArgShift;
+    width = valueArg + 1;       /* assume, correct later */
+
+    ALOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]",
+        valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+        (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+    pValue->type = valueType & kDexAnnotationValueTypeMask;
+
+    switch (valueType & kDexAnnotationValueTypeMask) {
+    case kDexAnnotationByte:
+        pValue->value.i = (s1) readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('B'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationShort:
+        pValue->value.i = (s2) readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('S'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationChar:
+        pValue->value.i = (u2) readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('C'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationInt:
+        pValue->value.i = readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('I'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationLong:
+        pValue->value.j = readSignedLong(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('J'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationFloat:
+        pValue->value.i = readUnsignedInt(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('F'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationDouble:
+        pValue->value.j = readUnsignedLong(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('D'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationBoolean:
+        pValue->value.i = (valueArg != 0);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmBoxPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('Z'));
+            setObject = true;
+        }
+        width = 0;
+        break;
+
+    case kDexAnnotationString:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            elemObj = (Object*) dvmResolveString(clazz, idx);
+            setObject = true;
+            if (elemObj == NULL)
+                return false;
+            dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+        }
+        break;
+    case kDexAnnotationType:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            elemObj = (Object*) dvmResolveClass(clazz, idx, true);
+            setObject = true;
+            if (elemObj == NULL) {
+                /* we're expected to throw a TypeNotPresentException here */
+                DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+                const char* desc = dexStringByTypeIdx(pDexFile, idx);
+                dvmClearException(self);
+                dvmThrowTypeNotPresentException(desc);
+                return false;
+            } else {
+                dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+            }
+        }
+        break;
+    case kDexAnnotationMethod:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            Method* meth = resolveAmbiguousMethod(clazz, idx);
+            if (meth == NULL)
+                return false;
+            elemObj = dvmCreateReflectObjForMethod(clazz, meth);
+            setObject = true;
+            if (elemObj == NULL)
+                return false;
+        }
+        break;
+    case kDexAnnotationField:
+        idx = readUnsignedInt(ptr, valueArg, false);
+        assert(false);      // TODO
+        break;
+    case kDexAnnotationEnum:
+        /* enum values are the contents of a static field */
+        idx = readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = idx;
+        } else {
+            StaticField* sfield;
+
+            sfield = dvmResolveStaticField(clazz, idx);
+            if (sfield == NULL) {
+                return false;
+            } else {
+                assert(sfield->clazz->descriptor[0] == 'L');
+                elemObj = sfield->value.l;
+                setObject = true;
+                dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+            }
+        }
+        break;
+    case kDexAnnotationArray:
+        /*
+         * encoded_array format, which is a size followed by a stream
+         * of annotation_value.
+         *
+         * We create an array of Object, populate it, and return it.
+         */
+        if (resultStyle == kAllRaw) {
+            return false;
+        } else {
+            ArrayObject* newArray;
+            u4 size, count;
+
+            size = readUleb128(&ptr);
+            LOGVV("--- annotation array, size is %u at %p", size, ptr);
+            newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+                size, ALLOC_DEFAULT);
+            if (newArray == NULL) {
+                ALOGE("annotation element array alloc failed (%d)", size);
+                return false;
+            }
+
+            AnnotationValue avalue;
+            for (count = 0; count < size; count++) {
+                if (!processAnnotationValue(clazz, &ptr, &avalue,
+                                kAllObjects)) {
+                    dvmReleaseTrackedAlloc((Object*)newArray, self);
+                    return false;
+                }
+                Object* obj = (Object*)avalue.value.l;
+                dvmSetObjectArrayElement(newArray, count, obj);
+                dvmReleaseTrackedAlloc(obj, self);
+            }
+
+            elemObj = (Object*) newArray;
+            setObject = true;
+        }
+        width = 0;
+        break;
+    case kDexAnnotationAnnotation:
+        /* encoded_annotation format */
+        if (resultStyle == kAllRaw)
+            return false;
+        elemObj = processEncodedAnnotation(clazz, &ptr);
+        setObject = true;
+        if (elemObj == NULL)
+            return false;
+        dvmAddTrackedAlloc(elemObj, self);      // balance the Release
+        width = 0;
+        break;
+    case kDexAnnotationNull:
+        if (resultStyle == kAllRaw) {
+            pValue->value.i = 0;
+        } else {
+            assert(elemObj == NULL);
+            setObject = true;
+        }
+        width = 0;
+        break;
+    default:
+        ALOGE("Bad annotation element value byte 0x%02x (0x%02x)",
+            valueType, valueType & kDexAnnotationValueTypeMask);
+        assert(false);
+        return false;
+    }
+
+    ptr += width;
+
+    *pPtr = ptr;
+    if (setObject)
+        pValue->value.l = elemObj;
+    return true;
+}
+
+
+/*
+ * For most object types, we have nothing to do here, and we just return
+ * "valueObj".
+ *
+ * For an array annotation, the type of the extracted object will always
+ * be java.lang.Object[], but we want it to match the type that the
+ * annotation member is expected to return.  In some cases this may
+ * involve un-boxing primitive values.
+ *
+ * We allocate a second array with the correct type, then copy the data
+ * over.  This releases the tracked allocation on "valueObj" and returns
+ * a new, tracked object.
+ *
+ * On failure, this releases the tracking on "valueObj" and returns NULL
+ * (allowing the call to say "foo = convertReturnType(foo, ..)").
+ */
+static Object* convertReturnType(Object* valueObj, ClassObject* methodReturn)
+{
+    if (valueObj == NULL ||
+        !dvmIsArray((ArrayObject*)valueObj) || !dvmIsArrayClass(methodReturn))
+    {
+        return valueObj;
+    }
+
+    Thread* self = dvmThreadSelf();
+    ClassObject* srcElemClass;
+    ClassObject* dstElemClass;
+
+    /*
+     * We always extract kDexAnnotationArray into Object[], so we expect to
+     * find that here.  This means we can skip the FindClass on
+     * (valueObj->clazz->descriptor+1, valueObj->clazz->classLoader).
+     */
+    if (strcmp(valueObj->clazz->descriptor, "[Ljava/lang/Object;") != 0) {
+        ALOGE("Unexpected src type class (%s)", valueObj->clazz->descriptor);
+        return NULL;
+    }
+    srcElemClass = gDvm.classJavaLangObject;
+
+    /*
+     * Skip past the '[' to get element class name.  Note this is not always
+     * the same as methodReturn->elementClass.
+     */
+    char firstChar = methodReturn->descriptor[1];
+    if (firstChar == 'L' || firstChar == '[') {
+        dstElemClass = dvmFindClass(methodReturn->descriptor+1,
+            methodReturn->classLoader);
+    } else {
+        dstElemClass = dvmFindPrimitiveClass(firstChar);
+    }
+    ALOGV("HEY: converting valueObj from [%s to [%s",
+        srcElemClass->descriptor, dstElemClass->descriptor);
+
+    ArrayObject* srcArray = (ArrayObject*) valueObj;
+    u4 length = srcArray->length;
+    ArrayObject* newArray;
+
+    newArray = dvmAllocArrayByClass(methodReturn, length, ALLOC_DEFAULT);
+    if (newArray == NULL) {
+        ALOGE("Failed creating duplicate annotation class (%s %d)",
+            methodReturn->descriptor, length);
+        goto bail;
+    }
+
+    bool success;
+    if (dstElemClass->primitiveType == PRIM_NOT) {
+        success = dvmCopyObjectArray(newArray, srcArray, dstElemClass);
+    } else {
+        success = dvmUnboxObjectArray(newArray, srcArray, dstElemClass);
+    }
+    if (!success) {
+        ALOGE("Annotation array copy failed");
+        dvmReleaseTrackedAlloc((Object*)newArray, self);
+        newArray = NULL;
+        goto bail;
+    }
+
+bail:
+    /* replace old, return new */
+    dvmReleaseTrackedAlloc(valueObj, self);
+    return (Object*) newArray;
+}
+
+/*
+ * Create a new AnnotationMember.
+ *
+ * "clazz" is the class on which the annotations are defined.  "pPtr"
+ * points to a pointer into the annotation data.  "annoClass" is the
+ * annotation's class.
+ *
+ * We extract the annotation's value, create a new AnnotationMember object,
+ * and construct it.
+ *
+ * Returns NULL on failure; an exception may or may not be raised.
+ */
+static Object* createAnnotationMember(const ClassObject* clazz,
+    const ClassObject* annoClass, const u1** pPtr)
+{
+    Thread* self = dvmThreadSelf();
+    const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    StringObject* nameObj = NULL;
+    Object* valueObj = NULL;
+    Object* newMember = NULL;
+    Object* methodObj = NULL;
+    ClassObject* methodReturn = NULL;
+    u4 elementNameIdx;
+    const char* name;
+    AnnotationValue avalue;
+    JValue unused;
+    bool failed = true;
+
+    elementNameIdx = readUleb128(pPtr);
+
+    if (!processAnnotationValue(clazz, pPtr, &avalue, kAllObjects)) {
+        ALOGW("Failed processing annotation value");
+        goto bail;
+    }
+    valueObj = (Object*)avalue.value.l;
+
+    /* new member to hold the element */
+    newMember = dvmAllocObject(gDvm.classLibcoreReflectAnnotationMember, ALLOC_DEFAULT);
+    name = dexStringById(pDexFile, elementNameIdx);
+    nameObj = dvmCreateStringFromCstr(name);
+
+    /* find the method in the annotation class, given only the name */
+    if (name != NULL) {
+        Method* annoMeth = dvmFindVirtualMethodByName(annoClass, name);
+        if (annoMeth == NULL) {
+            ALOGW("WARNING: could not find annotation member %s in %s",
+                name, annoClass->descriptor);
+        } else {
+            methodObj = dvmCreateReflectObjForMethod(annoClass, annoMeth);
+            methodReturn = dvmGetBoxedReturnType(annoMeth);
+        }
+    }
+    if (newMember == NULL || nameObj == NULL || methodObj == NULL ||
+        methodReturn == NULL)
+    {
+        ALOGE("Failed creating annotation element (m=%p n=%p a=%p r=%p)",
+            newMember, nameObj, methodObj, methodReturn);
+        goto bail;
+    }
+
+    /* convert the return type, if necessary */
+    valueObj = convertReturnType(valueObj, methodReturn);
+    if (valueObj == NULL)
+        goto bail;
+
+    /* call 4-argument constructor */
+    dvmCallMethod(self, gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
+        newMember, &unused, nameObj, valueObj, methodReturn, methodObj);
+    if (dvmCheckException(self)) {
+        ALOGD("Failed constructing annotation element");
+        goto bail;
+    }
+
+    failed = false;
+
+bail:
+    /* release tracked allocations */
+    dvmReleaseTrackedAlloc(newMember, self);
+    dvmReleaseTrackedAlloc((Object*)nameObj, self);
+    dvmReleaseTrackedAlloc(valueObj, self);
+    dvmReleaseTrackedAlloc(methodObj, self);
+    if (failed)
+        return NULL;
+    else
+        return newMember;
+}
+
+/*
+ * Create a new Annotation object from what we find in the annotation item.
+ *
+ * "clazz" is the class on which the annotations are defined.  "pPtr"
+ * points to a pointer into the annotation data.
+ *
+ * We use the AnnotationFactory class to create the annotation for us.  The
+ * method we call is:
+ *
+ *  public static Annotation createAnnotation(
+ *      Class<? extends Annotation> annotationType,
+ *      AnnotationMember[] elements)
+ *
+ * Returns a new Annotation, which will NOT be in the local ref table and
+ * not referenced elsewhere, so store it away soon.  On failure, returns NULL
+ * with an exception raised.
+ */
+static Object* processEncodedAnnotation(const ClassObject* clazz,
+    const u1** pPtr)
+{
+    Thread* self = dvmThreadSelf();
+    Object* newAnno = NULL;
+    ArrayObject* elementArray = NULL;
+    const ClassObject* annoClass;
+    const u1* ptr;
+    u4 typeIdx, size, count;
+
+    ptr = *pPtr;
+    typeIdx = readUleb128(&ptr);
+    size = readUleb128(&ptr);
+
+    LOGVV("----- processEnc ptr=%p type=%d size=%d", ptr, typeIdx, size);
+
+    annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+    if (annoClass == NULL) {
+        annoClass = dvmResolveClass(clazz, typeIdx, true);
+        if (annoClass == NULL) {
+            ALOGE("Unable to resolve %s annotation class %d",
+                clazz->descriptor, typeIdx);
+            assert(dvmCheckException(self));
+            dvmClearException(self);
+            return NULL;
+        }
+    }
+
+    ALOGV("----- processEnc ptr=%p [0x%06x]  typeIdx=%d size=%d class=%s",
+        *pPtr, *pPtr - (u1*) clazz->pDvmDex->pDexFile->baseAddr,
+        typeIdx, size, annoClass->descriptor);
+
+    /*
+     * Elements are parsed out and stored in an array.  The Harmony
+     * constructor wants an array with just the declared elements --
+     * default values get merged in later.
+     */
+    JValue result;
+
+    if (size > 0) {
+        elementArray = dvmAllocArrayByClass(gDvm.classLibcoreReflectAnnotationMemberArray,
+                                            size, ALLOC_DEFAULT);
+        if (elementArray == NULL) {
+            ALOGE("failed to allocate annotation member array (%d elements)",
+                size);
+            goto bail;
+        }
+    }
+
+    /*
+     * "ptr" points to a byte stream with "size" occurrences of
+     * annotation_element.
+     */
+    for (count = 0; count < size; count++) {
+        Object* newMember = createAnnotationMember(clazz, annoClass, &ptr);
+        if (newMember == NULL)
+            goto bail;
+
+        /* add it to the array */
+        dvmSetObjectArrayElement(elementArray, count, newMember);
+    }
+
+    dvmCallMethod(self,
+        gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
+        NULL, &result, annoClass, elementArray);
+    if (dvmCheckException(self)) {
+        ALOGD("Failed creating an annotation");
+        //dvmLogExceptionStackTrace();
+        goto bail;
+    }
+
+    newAnno = (Object*)result.l;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
+    *pPtr = ptr;
+    if (newAnno == NULL && !dvmCheckException(self)) {
+        /* make sure an exception is raised */
+        dvmThrowRuntimeException("failure in processEncodedAnnotation");
+    }
+    return newAnno;
+}
+
+/*
+ * Run through an annotation set and convert each entry into an Annotation
+ * object.
+ *
+ * Returns an array of Annotation objects, or NULL with an exception raised
+ * on alloc failure.
+ */
+static ArrayObject* processAnnotationSet(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet, int visibility)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* pAnnoItem;
+
+    /* we need these later; make sure they're initialized */
+    if (!dvmIsClassInitialized(gDvm.classLibcoreReflectAnnotationFactory))
+        dvmInitClass(gDvm.classLibcoreReflectAnnotationFactory);
+    if (!dvmIsClassInitialized(gDvm.classLibcoreReflectAnnotationMember))
+        dvmInitClass(gDvm.classLibcoreReflectAnnotationMember);
+
+    /* count up the number of visible elements */
+    size_t count = 0;
+    for (size_t i = 0; i < pAnnoSet->size; ++i) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility == visibility) {
+            count++;
+        }
+    }
+
+    ArrayObject* annoArray = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
+                                                  count, ALLOC_DEFAULT);
+    if (annoArray == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Generate Annotation objects.  We must put them into the array
+     * immediately (or add them to the tracked ref table).
+     * We may not be able to resolve all annotations, and should just
+     * ignore those we can't.
+     */
+    u4 dstIndex = 0;
+    for (int i = 0; i < (int) pAnnoSet->size; i++) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+        const u1* ptr = pAnnoItem->annotation;
+        Object *anno = processEncodedAnnotation(clazz, &ptr);
+        if (anno != NULL) {
+            dvmSetObjectArrayElement(annoArray, dstIndex, anno);
+            ++dstIndex;
+        }
+    }
+
+    // If we got as many as we expected, we're done...
+    if (dstIndex == count) {
+        return annoArray;
+    }
+
+    // ...otherwise we need to trim the trailing nulls.
+    ArrayObject* trimmedArray = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
+                                                     dstIndex, ALLOC_DEFAULT);
+    if (trimmedArray == NULL) {
+        return NULL;
+    }
+    for (size_t i = 0; i < dstIndex; ++i) {
+        Object** src = (Object**)(void*) annoArray->contents;
+        dvmSetObjectArrayElement(trimmedArray, i, src[i]);
+    }
+    dvmReleaseTrackedAlloc((Object*) annoArray, NULL);
+    return trimmedArray;
+}
+
+/*
+ * Return the annotation item of the specified type in the annotation set, or
+ * NULL if the set contains no annotation of that type.
+ */
+static const DexAnnotationItem* getAnnotationItemFromAnnotationSet(
+        const ClassObject* clazz, const DexAnnotationSetItem* pAnnoSet,
+        int visibility, const ClassObject* annotationClazz)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* pAnnoItem;
+    int i;
+    const ClassObject* annoClass;
+    const u1* ptr;
+    u4 typeIdx;
+
+    /* we need these later; make sure they're initialized */
+    if (!dvmIsClassInitialized(gDvm.classLibcoreReflectAnnotationFactory))
+        dvmInitClass(gDvm.classLibcoreReflectAnnotationFactory);
+    if (!dvmIsClassInitialized(gDvm.classLibcoreReflectAnnotationMember))
+        dvmInitClass(gDvm.classLibcoreReflectAnnotationMember);
+
+    for (i = 0; i < (int) pAnnoSet->size; i++) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+
+        ptr = pAnnoItem->annotation;
+        typeIdx = readUleb128(&ptr);
+
+        annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+        if (annoClass == NULL) {
+            annoClass = dvmResolveClass(clazz, typeIdx, true);
+            if (annoClass == NULL) {
+                ALOGE("Unable to resolve %s annotation class %d",
+                      clazz->descriptor, typeIdx);
+                Thread* self = dvmThreadSelf();
+                assert(dvmCheckException(self));
+                dvmClearException(self);
+                continue;
+            }
+        }
+
+        if (annoClass == annotationClazz) {
+            return pAnnoItem;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Return the Annotation object of the specified type in the annotation set, or
+ * NULL if the set contains no annotation of that type.
+ */
+static Object* getAnnotationObjectFromAnnotationSet(const ClassObject* clazz,
+        const DexAnnotationSetItem* pAnnoSet, int visibility,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, visibility, annotationClazz);
+    if (pAnnoItem == NULL) {
+        return NULL;
+    }
+    const u1* ptr = pAnnoItem->annotation;
+    return processEncodedAnnotation(clazz, &ptr);
+}
+
+/*
+ * ===========================================================================
+ *      Skipping and scanning
+ * ===========================================================================
+ */
+
+/*
+ * Skip past an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined.
+ *
+ * Returns "true" on success, "false" on parsing failure.
+ */
+static bool skipAnnotationValue(const ClassObject* clazz, const u1** pPtr)
+{
+    const u1* ptr = *pPtr;
+    u1 valueType, valueArg;
+    int width;
+
+    valueType = *ptr++;
+    valueArg = valueType >> kDexAnnotationValueArgShift;
+    width = valueArg + 1;       /* assume */
+
+    ALOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]",
+        valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+        (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+    switch (valueType & kDexAnnotationValueTypeMask) {
+    case kDexAnnotationByte:        break;
+    case kDexAnnotationShort:       break;
+    case kDexAnnotationChar:        break;
+    case kDexAnnotationInt:         break;
+    case kDexAnnotationLong:        break;
+    case kDexAnnotationFloat:       break;
+    case kDexAnnotationDouble:      break;
+    case kDexAnnotationString:      break;
+    case kDexAnnotationType:        break;
+    case kDexAnnotationMethod:      break;
+    case kDexAnnotationField:       break;
+    case kDexAnnotationEnum:        break;
+
+    case kDexAnnotationArray:
+        /* encoded_array format */
+        {
+            u4 size = readUleb128(&ptr);
+            while (size--) {
+                if (!skipAnnotationValue(clazz, &ptr))
+                    return false;
+            }
+        }
+        width = 0;
+        break;
+    case kDexAnnotationAnnotation:
+        /* encoded_annotation format */
+        if (!skipEncodedAnnotation(clazz, &ptr))
+            return false;
+        width = 0;
+        break;
+    case kDexAnnotationBoolean:
+    case kDexAnnotationNull:
+        width = 0;
+        break;
+    default:
+        ALOGE("Bad annotation element value byte 0x%02x", valueType);
+        assert(false);
+        return false;
+    }
+
+    ptr += width;
+
+    *pPtr = ptr;
+    return true;
+}
+
+/*
+ * Skip past an encoded annotation.  Mainly useful for annotations embedded
+ * in other annotations.
+ */
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr)
+{
+    const u1* ptr;
+    u4 size;
+
+    ptr = *pPtr;
+    (void) readUleb128(&ptr);
+    size = readUleb128(&ptr);
+
+    /*
+     * "ptr" points to a byte stream with "size" occurrences of
+     * annotation_element.
+     */
+    while (size--) {
+        (void) readUleb128(&ptr);
+
+        if (!skipAnnotationValue(clazz, &ptr))
+            return false;
+    }
+
+    *pPtr = ptr;
+    return true;
+}
+
+
+/*
+ * Compare the name of the class in the DEX file to the supplied descriptor.
+ * Return value is equivalent to strcmp.
+ */
+static int compareClassDescriptor(DexFile* pDexFile, u4 typeIdx,
+    const char* descriptor)
+{
+    const char* str = dexStringByTypeIdx(pDexFile, typeIdx);
+
+    return strcmp(str, descriptor);
+}
+
+/*
+ * Search through the annotation set for an annotation with a matching
+ * descriptor.
+ *
+ * Comparing the string descriptor is slower than comparing an integer class
+ * index.  If annotation lists are expected to be long, we could look up
+ * the class' index by name from the DEX file, rather than doing a class
+ * lookup and string compare on each entry.  (Note the index will be
+ * different for each DEX file, so we can't cache annotation class indices
+ * globally.)
+ */
+static const DexAnnotationItem* searchAnnotationSet(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet, const char* descriptor,
+    int visibility)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationItem* result = NULL;
+    u4 typeIdx;
+    int i;
+
+    //printf("##### searchAnnotationSet %s %d\n", descriptor, visibility);
+
+    for (i = 0; i < (int) pAnnoSet->size; i++) {
+        const DexAnnotationItem* pAnnoItem;
+
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility != visibility)
+            continue;
+        const u1* ptr = pAnnoItem->annotation;
+        typeIdx = readUleb128(&ptr);
+
+        if (compareClassDescriptor(pDexFile, typeIdx, descriptor) == 0) {
+            //printf("#####  match on %x/%p at %d\n", typeIdx, pDexFile, i);
+            result = pAnnoItem;
+            break;
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Find an annotation value in the annotation_item whose name matches "name".
+ * A pointer to the annotation_value is returned, or NULL if it's not found.
+ */
+static const u1* searchEncodedAnnotation(const ClassObject* clazz,
+    const u1* ptr, const char* name)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    u4 typeIdx, size;
+
+    typeIdx = readUleb128(&ptr);
+    size = readUleb128(&ptr);
+    //printf("#####   searching ptr=%p type=%u size=%u\n", ptr, typeIdx, size);
+
+    while (size--) {
+        u4 elementNameIdx;
+        const char* elemName;
+
+        elementNameIdx = readUleb128(&ptr);
+        elemName = dexStringById(pDexFile, elementNameIdx);
+        if (strcmp(name, elemName) == 0) {
+            //printf("#####   item match on %s\n", name);
+            return ptr;     /* points to start of value */
+        }
+
+        skipAnnotationValue(clazz, &ptr);
+    }
+
+    //printf("#####   no item match on %s\n", name);
+    return NULL;
+}
+
+#define GAV_FAILED  ((Object*) 0x10000001)
+
+/*
+ * Extract an encoded annotation value from the field specified by "annoName".
+ *
+ * "expectedType" is an annotation value type, e.g. kDexAnnotationString.
+ * "debugAnnoName" is only used in debug messages.
+ *
+ * Returns GAV_FAILED on failure.  If an allocation failed, an exception
+ * will be raised.
+ */
+static Object* getAnnotationValue(const ClassObject* clazz,
+    const DexAnnotationItem* pAnnoItem, const char* annoName,
+    int expectedType, const char* debugAnnoName)
+{
+    const u1* ptr;
+    AnnotationValue avalue;
+
+    /* find the annotation */
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, annoName);
+    if (ptr == NULL) {
+        ALOGW("%s annotation lacks '%s' member", debugAnnoName, annoName);
+        return GAV_FAILED;
+    }
+
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects))
+        return GAV_FAILED;
+
+    /* make sure it has the expected format */
+    if (avalue.type != expectedType) {
+        ALOGW("%s %s has wrong type (0x%02x, expected 0x%02x)",
+            debugAnnoName, annoName, avalue.type, expectedType);
+        return GAV_FAILED;
+    }
+
+    return (Object*)avalue.value.l;
+}
+
+
+/*
+ * Find the Signature attribute and extract its value.  (Signatures can
+ * be found in annotations on classes, constructors, methods, and fields.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if not found.  On memory alloc failure, returns NULL with an
+ * exception raised.
+ */
+static ArrayObject* getSignatureValue(const ClassObject* clazz,
+    const DexAnnotationSetItem* pAnnoSet)
+{
+    const DexAnnotationItem* pAnnoItem;
+    Object* obj;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrSignature,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The Signature annotation has one member, "String value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationArray,
+            "Signature");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(obj->clazz == gDvm.classJavaLangObjectArray);
+
+    return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Class
+ * ===========================================================================
+ */
+
+/*
+ * Find the DexAnnotationSetItem for this class.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForClass(
+    const ClassObject* clazz)
+{
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+
+    pDexFile = clazz->pDvmDex->pDexFile;
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL)
+        return dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+    else
+        return NULL;
+}
+
+/*
+ * Return an array of Annotation objects for the class.  Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz)
+{
+    ArrayObject* annoArray;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        /* no annotations for anything in class, or no class annotations */
+        annoArray = emptyAnnoArray();
+    } else {
+        annoArray = processAnnotationSet(clazz, pAnnoSet,
+                        kDexVisibilityRuntime);
+    }
+
+    return annoArray;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetClassAnnotation(const ClassObject* clazz,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsClassAnnotationPresent(const ClassObject* clazz,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL) {
+        return false;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz)
+{
+    ArrayObject* signature = NULL;
+    const DexAnnotationSetItem* pAnnoSet;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+/*
+ * Get the EnclosingMethod attribute from an annotation.  Returns a Method
+ * object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The EnclosingMethod annotation has one member, "Method value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationMethod,
+            "EnclosingMethod");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(obj->clazz == gDvm.classJavaLangReflectConstructor ||
+           obj->clazz == gDvm.classJavaLangReflectMethod);
+
+    return obj;
+}
+
+/*
+ * Find a class' enclosing class.  We return what we find in the
+ * EnclosingClass attribute.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The EnclosingClass annotation has one member, "Class value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+            "EnclosingClass");
+    if (obj == GAV_FAILED)
+        return NULL;
+
+    assert(dvmIsClassObject(obj));
+    return (ClassObject*)obj;
+}
+
+/*
+ * Find a class' enclosing class.  We first search for an EnclosingClass
+ * attribute, and if that's not found we look for an EnclosingMethod.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem != NULL) {
+        /*
+         * The EnclosingClass annotation has one member, "Class value".
+         */
+        obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+                "EnclosingClass");
+        if (obj != GAV_FAILED) {
+            assert(dvmIsClassObject(obj));
+            return (ClassObject*)obj;
+        }
+    }
+
+    /*
+     * That didn't work.  Look for an EnclosingMethod.
+     *
+     * We could create a java.lang.reflect.Method object and extract the
+     * declaringClass from it, but that's more work than we want to do.
+     * Instead, we find the "value" item and parse the index out ourselves.
+     */
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /* find the value member */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+    if (ptr == NULL) {
+        ALOGW("EnclosingMethod annotation lacks 'value' member");
+        return NULL;
+    }
+
+    /* parse it, verify the type */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        ALOGW("EnclosingMethod parse failed");
+        return NULL;
+    }
+    if (avalue.type != kDexAnnotationMethod) {
+        ALOGW("EnclosingMethod value has wrong type (0x%02x, expected 0x%02x)",
+            avalue.type, kDexAnnotationMethod);
+        return NULL;
+    }
+
+    /* pull out the method index and resolve the method */
+    Method* meth = resolveAmbiguousMethod(clazz, avalue.value.i);
+    if (meth == NULL)
+        return NULL;
+
+    ClassObject* methClazz = meth->clazz;
+    dvmAddTrackedAlloc((Object*) methClazz, NULL);      // balance the Release
+    return methClazz;
+}
+
+/*
+ * Get the EnclosingClass attribute from an annotation.  If found, returns
+ * "true".  A String with the original name of the class and the original
+ * access flags are returned through the arguments.  (The name will be NULL
+ * for an anonymous inner class.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+    int* pAccessFlags)
+{
+    const DexAnnotationItem* pAnnoItem;
+    const DexAnnotationSetItem* pAnnoSet;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return false;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrInnerClass,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return false;
+
+    /*
+     * The InnerClass annotation has two members, "String name" and
+     * "int accessFlags".  We don't want to get the access flags as an
+     * Integer, so we process that as a simple value.
+     */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "name");
+    if (ptr == NULL) {
+        ALOGW("InnerClass annotation lacks 'name' member");
+        return false;
+    }
+
+    /* parse it into an Object */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+        ALOGD("processAnnotationValue failed on InnerClass member 'name'");
+        return false;
+    }
+
+    /* make sure it has the expected format */
+    if (avalue.type != kDexAnnotationNull &&
+        avalue.type != kDexAnnotationString)
+    {
+        ALOGW("InnerClass name has bad type (0x%02x, expected STRING or NULL)",
+            avalue.type);
+        return false;
+    }
+
+    *pName = (StringObject*) avalue.value.l;
+    assert(*pName == NULL || (*pName)->clazz == gDvm.classJavaLangString);
+
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
+    if (ptr == NULL) {
+        ALOGW("InnerClass annotation lacks 'accessFlags' member");
+        return false;
+    }
+
+    /* parse it, verify the type */
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        ALOGW("InnerClass accessFlags parse failed");
+        return false;
+    }
+    if (avalue.type != kDexAnnotationInt) {
+        ALOGW("InnerClass value has wrong type (0x%02x, expected 0x%02x)",
+            avalue.type, kDexAnnotationInt);
+        return false;
+    }
+
+    *pAccessFlags = avalue.value.i;
+
+    return true;
+}
+
+/*
+ * Extract an array of Class objects from the MemberClasses annotation
+ * for this class.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any member classes.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz)
+{
+    const DexAnnotationSetItem* pAnnoSet;
+    const DexAnnotationItem* pAnnoItem;
+    Object* obj;
+
+    pAnnoSet = findAnnotationSetForClass(clazz);
+    if (pAnnoSet == NULL)
+        return NULL;
+
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrMemberClasses,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;
+
+    /*
+     * The MemberClasses annotation has one member, "Class[] value".
+     */
+    obj = getAnnotationValue(clazz, pAnnoItem, "value",
+            kDexAnnotationArray, "MemberClasses");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(dvmIsArray((ArrayObject*)obj));
+    obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+    return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Method (and Constructor)
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, method name, method signature) of
+ * the specified method to "method".
+ */
+static int compareMethodStr(DexFile* pDexFile, u4 methodIdx,
+    const Method* method)
+{
+    const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    const char* str = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+    int result = strcmp(str, method->clazz->descriptor);
+
+    if (result == 0) {
+        str = dexStringById(pDexFile, pMethodId->nameIdx);
+        result = strcmp(str, method->name);
+        if (result == 0) {
+            DexProto proto;
+            dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+            result = dexProtoCompare(&proto, &method->prototype);
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Given a method, determine the method's index.
+ *
+ * We could simply store this in the Method*, but that would cost 4 bytes
+ * per method.  Instead we plow through the DEX data.
+ *
+ * We have two choices: look through the class method data, or look through
+ * the global method_ids table.  The former is awkward because the method
+ * could have been defined in a superclass or interface.  The latter works
+ * out reasonably well because it's in sorted order, though we're still left
+ * doing a fair number of string comparisons.
+ */
+u4 dvmGetMethodIdx(const Method* method)
+{
+    if (method->clazz->pDvmDex == NULL) return 0;
+
+    DexFile* pDexFile = method->clazz->pDvmDex->pDexFile;
+    u4 hi = pDexFile->pHeader->methodIdsSize -1;
+    u4 lo = 0;
+    u4 cur;
+
+    while (hi >= lo) {
+        int cmp;
+        cur = (lo + hi) / 2;
+
+        cmp = compareMethodStr(pDexFile, cur, method);
+        if (cmp < 0) {
+            lo = cur + 1;
+        } else if (cmp > 0) {
+            hi = cur - 1;
+        } else {
+            break;
+        }
+    }
+
+    if (hi < lo) {
+        /* this should be impossible -- the method came out of this DEX */
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGE("Unable to find method %s.%s %s in DEX file!",
+            method->clazz->descriptor, method->name, desc);
+        free(desc);
+        dvmAbort();
+    }
+
+    return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this method.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForMethod(
+    const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexMethodAnnotationsItem* pMethodList;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+    pDexFile = clazz->pDvmDex->pDexFile;
+
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL) {
+        pMethodList = dexGetMethodAnnotations(pDexFile, pAnnoDir);
+        if (pMethodList != NULL) {
+            /*
+             * Run through the list and find a matching method.  We compare the
+             * method ref indices in the annotation list with the method's DEX
+             * method_idx value.
+             *
+             * TODO: use a binary search for long lists
+             *
+             * Alternate approach: for each entry in the annotations list,
+             * find the method definition in the DEX file and perform string
+             * comparisons on class name, method name, and signature.
+             */
+            u4 methodIdx = dvmGetMethodIdx(method);
+            u4 count = dexGetMethodAnnotationsSize(pDexFile, pAnnoDir);
+            u4 idx;
+
+            for (idx = 0; idx < count; idx++) {
+                if (pMethodList[idx].methodIdx == methodIdx) {
+                    /* found! */
+                    pAnnoSet = dexGetMethodAnnotationSetItem(pDexFile,
+                                    &pMethodList[idx]);
+                    break;
+                }
+            }
+        }
+    }
+
+    return pAnnoSet;
+}
+
+/*
+ * Return an array of Annotation objects for the method.  Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetMethodAnnotations(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    ArrayObject* annoArray = NULL;
+
+    pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL) {
+        /* no matching annotations found */
+        annoArray = emptyAnnoArray();
+    } else {
+        annoArray = processAnnotationSet(clazz, pAnnoSet,kDexVisibilityRuntime);
+    }
+
+    return annoArray;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetMethodAnnotation(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsMethodAnnotationPresent(const ClassObject* clazz,
+        const Method* method, const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL) {
+        return false;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    ArrayObject* signature = NULL;
+
+    pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+/*
+ * Extract an array of exception classes from the "system" annotation list
+ * for this method.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any exceptions for this method.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    const DexAnnotationItem* pAnnoItem;
+
+    /* find the set for this method */
+    pAnnoSet = findAnnotationSetForMethod(method);
+    if (pAnnoSet == NULL)
+        return NULL;        /* nothing for this method */
+
+    /* find the "Throws" annotation, if any */
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrThrows,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL)
+        return NULL;        /* no Throws */
+
+    /*
+     * The Throws annotation has one member, "Class[] value".
+     */
+    Object* obj = getAnnotationValue(clazz, pAnnoItem, "value",
+        kDexAnnotationArray, "Throws");
+    if (obj == GAV_FAILED)
+        return NULL;
+    assert(dvmIsArray((ArrayObject*)obj));
+    obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+    return (ArrayObject*)obj;
+}
+
+/*
+ * Given an Annotation's method, find the default value, if any.
+ *
+ * If this is a CLASS annotation, and we can't find a match for the
+ * default class value, we need to throw a TypeNotPresentException.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method)
+{
+    const ClassObject* clazz = method->clazz;
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    /*
+     * The method's declaring class (the annotation) will have an
+     * AnnotationDefault "system" annotation associated with it if any
+     * of its methods have default values.  Start by finding the
+     * DexAnnotationItem associated with the class.
+     */
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir != NULL)
+        pAnnoSet = dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+    if (pAnnoSet == NULL) {
+        /* no annotations for anything in class, or no class annotations */
+        return NULL;
+    }
+
+    /* find the "AnnotationDefault" annotation, if any */
+    const DexAnnotationItem* pAnnoItem;
+    pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrAnnotationDefault,
+        kDexVisibilitySystem);
+    if (pAnnoItem == NULL) {
+        /* no default values for any member in this annotation */
+        //printf("##### no default annotations for %s.%s\n",
+        //    method->clazz->descriptor, method->name);
+        return NULL;
+    }
+
+    /*
+     * The AnnotationDefault annotation has one member, "Annotation value".
+     * We need to pull that out.
+     */
+    const u1* ptr;
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+    if (ptr == NULL) {
+        ALOGW("AnnotationDefault annotation lacks 'value'");
+        return NULL;
+    }
+    if ((*ptr & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) {
+        ALOGW("AnnotationDefault value has wrong type (0x%02x)",
+            *ptr & kDexAnnotationValueTypeMask);
+        return NULL;
+    }
+
+    /*
+     * The value_type byte for VALUE_ANNOTATION is followed by
+     * encoded_annotation data.  We want to scan through it to find an
+     * entry whose name matches our method name.
+     */
+    ptr++;
+    ptr = searchEncodedAnnotation(clazz, ptr, method->name);
+    if (ptr == NULL)
+        return NULL;        /* no default annotation for this method */
+
+    /* got it, pull it out */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+        ALOGD("processAnnotationValue failed on default for '%s'",
+            method->name);
+        return NULL;
+    }
+
+    /* convert the return type, if necessary */
+    ClassObject* methodReturn = dvmGetBoxedReturnType(method);
+    Object* obj = (Object*)avalue.value.l;
+    obj = convertReturnType(obj, methodReturn);
+
+    return obj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Field
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, field name, field signature) of
+ * the specified field to "field".
+ */
+static int compareFieldStr(DexFile* pDexFile, u4 idx, const Field* field)
+{
+    const DexFieldId* pFieldId = dexGetFieldId(pDexFile, idx);
+    const char* str = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+    int result = strcmp(str, field->clazz->descriptor);
+
+    if (result == 0) {
+        str = dexStringById(pDexFile, pFieldId->nameIdx);
+        result = strcmp(str, field->name);
+        if (result == 0) {
+            str = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+            result = strcmp(str, field->signature);
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Given a field, determine the field's index.
+ *
+ * This has the same tradeoffs as dvmGetMethodIdx.
+ */
+u4 dvmGetFieldIdx(const Field* field)
+{
+    if (field->clazz->pDvmDex == NULL) return 0;
+
+    DexFile* pDexFile = field->clazz->pDvmDex->pDexFile;
+    u4 hi = pDexFile->pHeader->fieldIdsSize -1;
+    u4 lo = 0;
+    u4 cur;
+
+    while (hi >= lo) {
+        int cmp;
+        cur = (lo + hi) / 2;
+
+        cmp = compareFieldStr(pDexFile, cur, field);
+        if (cmp < 0) {
+            lo = cur + 1;
+        } else if (cmp > 0) {
+            hi = cur - 1;
+        } else {
+            break;
+        }
+    }
+
+    if (hi < lo) {
+        /* this should be impossible -- the field came out of this DEX */
+        ALOGE("Unable to find field %s.%s %s in DEX file!",
+            field->clazz->descriptor, field->name, field->signature);
+        dvmAbort();
+    }
+
+    return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this field.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForField(const Field* field)
+{
+    ClassObject* clazz = field->clazz;
+    DvmDex* pDvmDex = clazz->pDvmDex;
+    if (pDvmDex == NULL) {
+        return NULL;
+    }
+
+    DexFile* pDexFile = pDvmDex->pDexFile;
+
+    const DexAnnotationsDirectoryItem* pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir == NULL) {
+        return NULL;
+    }
+
+    const DexFieldAnnotationsItem* pFieldList = dexGetFieldAnnotations(pDexFile, pAnnoDir);
+    if (pFieldList == NULL) {
+        return NULL;
+    }
+
+    /*
+     * Run through the list and find a matching field.  We compare the
+     * field ref indices in the annotation list with the field's DEX
+     * field_idx value.
+     *
+     * TODO: use a binary search for long lists
+     *
+     * Alternate approach: for each entry in the annotations list,
+     * find the field definition in the DEX file and perform string
+     * comparisons on class name, field name, and signature.
+     */
+    u4 fieldIdx = dvmGetFieldIdx(field);
+    u4 count = dexGetFieldAnnotationsSize(pDexFile, pAnnoDir);
+    u4 idx;
+
+    for (idx = 0; idx < count; idx++) {
+        if (pFieldList[idx].fieldIdx == fieldIdx) {
+            /* found! */
+            return dexGetFieldAnnotationSetItem(pDexFile, &pFieldList[idx]);
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Return an array of Annotation objects for the field.  Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetFieldAnnotations(const Field* field)
+{
+    ClassObject* clazz = field->clazz;
+    ArrayObject* annoArray = NULL;
+    const DexAnnotationSetItem* pAnnoSet = NULL;
+
+    pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet == NULL) {
+        /* no matching annotations found */
+        annoArray = emptyAnnoArray();
+    } else {
+        annoArray = processAnnotationSet(clazz, pAnnoSet,
+                        kDexVisibilityRuntime);
+    }
+
+    return annoArray;
+}
+
+/*
+ * Returns the annotation or NULL if it doesn't exist.
+ */
+Object* dvmGetFieldAnnotation(const ClassObject* clazz, const Field* field,
+        const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet == NULL) {
+        return NULL;
+    }
+    return getAnnotationObjectFromAnnotationSet(clazz, pAnnoSet,
+            kDexVisibilityRuntime, annotationClazz);
+}
+
+/*
+ * Returns true if the annotation exists.
+ */
+bool dvmIsFieldAnnotationPresent(const ClassObject* clazz,
+        const Field* field, const ClassObject* annotationClazz)
+{
+    const DexAnnotationSetItem* pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet == NULL) {
+        return false;
+    }
+    const DexAnnotationItem* pAnnoItem = getAnnotationItemFromAnnotationSet(
+            clazz, pAnnoSet, kDexVisibilityRuntime, annotationClazz);
+    return (pAnnoItem != NULL);
+}
+
+/*
+ * Retrieve the Signature annotation, if any.  Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field)
+{
+    ClassObject* clazz = field->clazz;
+    const DexAnnotationSetItem* pAnnoSet;
+    ArrayObject* signature = NULL;
+
+    pAnnoSet = findAnnotationSetForField(field);
+    if (pAnnoSet != NULL)
+        signature = getSignatureValue(clazz, pAnnoSet);
+
+    return signature;
+}
+
+
+/*
+ * ===========================================================================
+ *      Parameter
+ * ===========================================================================
+ */
+
+/*
+ * We have an annotation_set_ref_list, which is essentially a list of
+ * entries that we pass to processAnnotationSet().
+ *
+ * The returned object must be released with dvmReleaseTrackedAlloc.
+ */
+static ArrayObject* processAnnotationSetRefList(const ClassObject* clazz,
+    const DexAnnotationSetRefList* pAnnoSetList, u4 count)
+{
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    ArrayObject* annoArrayArray = NULL;
+    u4 idx;
+
+    /* allocate an array of Annotation arrays to hold results */
+    annoArrayArray = dvmAllocArrayByClass(
+        gDvm.classJavaLangAnnotationAnnotationArrayArray, count, ALLOC_DEFAULT);
+    if (annoArrayArray == NULL) {
+        ALOGW("annotation set ref array alloc failed");
+        goto bail;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        Thread* self = dvmThreadSelf();
+        const DexAnnotationSetRefItem* pItem;
+        const DexAnnotationSetItem* pAnnoSet;
+        Object *annoSet;
+        DexAnnotationSetItem emptySet;
+        emptySet.size = 0;
+
+        pItem = dexGetParameterAnnotationSetRef(pAnnoSetList, idx);
+        pAnnoSet = dexGetSetRefItemItem(pDexFile, pItem);
+        if (pAnnoSet == NULL) {
+            pAnnoSet = &emptySet;
+        }
+
+        annoSet = (Object *)processAnnotationSet(clazz,
+                                                 pAnnoSet,
+                                                 kDexVisibilityRuntime);
+        if (annoSet == NULL) {
+            ALOGW("processAnnotationSet failed");
+            annoArrayArray = NULL;
+            goto bail;
+        }
+        dvmSetObjectArrayElement(annoArrayArray, idx, annoSet);
+        dvmReleaseTrackedAlloc((Object*) annoSet, self);
+    }
+
+bail:
+    return annoArrayArray;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this parameter.
+ *
+ * Returns NULL if none found.
+ */
+static const DexParameterAnnotationsItem* findAnnotationsItemForMethod(
+    const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    DexFile* pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexParameterAnnotationsItem* pParameterList;
+
+    if (clazz->pDvmDex == NULL)         /* generated class (Proxy, array) */
+        return NULL;
+
+    pDexFile = clazz->pDvmDex->pDexFile;
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir == NULL)
+        return NULL;
+
+    pParameterList = dexGetParameterAnnotations(pDexFile, pAnnoDir);
+    if (pParameterList == NULL)
+        return NULL;
+
+    /*
+     * Run through the list and find a matching method.  We compare the
+     * method ref indices in the annotation list with the method's DEX
+     * method_idx value.
+     *
+     * TODO: use a binary search for long lists
+     *
+     * Alternate approach: for each entry in the annotations list,
+     * find the method definition in the DEX file and perform string
+     * comparisons on class name, method name, and signature.
+     */
+    u4 methodIdx = dvmGetMethodIdx(method);
+    u4 count = dexGetParameterAnnotationsSize(pDexFile, pAnnoDir);
+    u4 idx;
+
+    for (idx = 0; idx < count; idx++) {
+        if (pParameterList[idx].methodIdx == methodIdx) {
+            /* found! */
+            return &pParameterList[idx];
+        }
+    }
+
+    return NULL;
+}
+
+
+/*
+ * Count up the number of arguments the method takes.  The "this" pointer
+ * doesn't count.
+ */
+static int countMethodArguments(const Method* method)
+{
+    /* method->shorty[0] is the return type */
+    return strlen(method->shorty + 1);
+}
+
+/*
+ * Return an array of arrays of Annotation objects.  The outer array has
+ * one entry per method parameter, the inner array has the list of annotations
+ * associated with that parameter.
+ *
+ * If the method has no parameters, we return an array of length zero.  If
+ * the method has one or more parameters, we return an array whose length
+ * is equal to the number of parameters; if a given parameter does not have
+ * an annotation, the corresponding entry will be null.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetParameterAnnotations(const Method* method)
+{
+    ClassObject* clazz = method->clazz;
+    const DexParameterAnnotationsItem* pItem;
+    ArrayObject* annoArrayArray = NULL;
+
+    pItem = findAnnotationsItemForMethod(method);
+    if (pItem != NULL) {
+        DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+        const DexAnnotationSetRefList* pAnnoSetList;
+        u4 size;
+
+        size = dexGetParameterAnnotationSetRefSize(pDexFile, pItem);
+        pAnnoSetList = dexGetParameterAnnotationSetRefList(pDexFile, pItem);
+        annoArrayArray = processAnnotationSetRefList(clazz, pAnnoSetList, size);
+    } else {
+        /* no matching annotations found */
+        annoArrayArray = emptyAnnoArrayArray(countMethodArguments(method));
+    }
+
+    return annoArrayArray;
+}
+
+
+/*
+ * ===========================================================================
+ *      DexEncodedArray interpretation
+ * ===========================================================================
+ */
+
+/**
+ * Initializes an encoded array iterator.
+ *
+ * @param iterator iterator to initialize
+ * @param encodedArray encoded array to iterate over
+ * @param clazz class to use when resolving strings and types
+ */
+void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
+        const DexEncodedArray* encodedArray, const ClassObject* clazz) {
+    iterator->encodedArray = encodedArray;
+    iterator->cursor = encodedArray->array;
+    iterator->size = readUleb128(&iterator->cursor);
+    iterator->elementsLeft = iterator->size;
+    iterator->clazz = clazz;
+}
+
+/**
+ * Returns whether there are more elements to be read.
+ */
+bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator) {
+    return (iterator->elementsLeft != 0);
+}
+
+/**
+ * Returns the next decoded value from the iterator, advancing its
+ * cursor. This returns primitive values in their corresponding union
+ * slots, and returns everything else (including nulls) as object
+ * references in the "l" union slot.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on any returned reference.
+ *
+ * @param value pointer to store decoded value into
+ * @returns true if a value was decoded and the cursor advanced; false if
+ * the last value had already been decoded or if there was a problem decoding
+ */
+bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
+        AnnotationValue* value) {
+    bool processed;
+
+    if (iterator->elementsLeft == 0) {
+        return false;
+    }
+
+    processed = processAnnotationValue(iterator->clazz, &iterator->cursor,
+            value, kPrimitivesOrObjects);
+
+    if (! processed) {
+        ALOGE("Failed to process array element %d from %p",
+                iterator->size - iterator->elementsLeft,
+                iterator->encodedArray);
+        iterator->elementsLeft = 0;
+        return false;
+    }
+
+    iterator->elementsLeft--;
+    return true;
+}
diff --git a/vm/reflect/Proxy.cpp b/vm/reflect/Proxy.cpp
new file mode 100644
index 0000000..57d32e7
--- /dev/null
+++ b/vm/reflect/Proxy.cpp
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Implementation of java.lang.reflect.Proxy.
+ *
+ * Traditionally this is implemented entirely in interpreted code,
+ * generating bytecode that defines the proxy class.  Dalvik doesn't
+ * currently support this approach, so we generate the class directly.  If
+ * we add support for DefineClass with standard classfiles we can
+ * eliminate this.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+// fwd
+static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod);
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\
+    ArrayObject** pThrows, int* pMethodCount);
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+    Method** outMethods, ArrayObject* throws);
+static bool createExceptionClassList(const Method* method,
+    PointerSet** pThrows);
+static void updateExceptionClassList(const Method* method, PointerSet* throws);
+static void createConstructor(ClassObject* clazz, Method* meth);
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+    const Method* srcMeth);
+static void proxyConstructor(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+static void proxyInvoker(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+static bool mustWrapException(const Method* method, const Object* throwable);
+
+/* private static fields in the Proxy class */
+#define kThrowsField    0
+#define kProxySFieldCount 1
+
+/*
+ * Generate a proxy class with the specified name, interfaces, and loader.
+ * "interfaces" is an array of class objects.
+ *
+ * The Proxy.getProxyClass() code has done the following:
+ *  - Verified that "interfaces" contains only interfaces
+ *  - Verified that no interface appears twice
+ *  - Prepended the package name to the class name if one or more
+ *    interfaces are non-public
+ *  - Searched for an existing instance of an appropriate Proxy class
+ *
+ * On failure we leave a partially-created class object sitting around,
+ * but the garbage collector will take care of it.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+    Object* loader)
+{
+    ClassObject* result = NULL;
+    ArrayObject* throws = NULL;
+
+    char* nameStr = dvmCreateCstrFromString(str);
+    if (nameStr == NULL) {
+        dvmThrowIllegalArgumentException("missing name");
+        return NULL;
+    }
+
+    ALOGV("+++ Generate proxy class '%s' %p from %d interface classes",
+        nameStr, loader, interfaces->length);
+
+
+    /*
+     * Characteristics of a Proxy class:
+     * - concrete class, public and final
+     * - superclass is java.lang.reflect.Proxy
+     * - implements all listed interfaces (req'd for instanceof)
+     * - has one method for each method in the interfaces (for duplicates,
+     *   the method in the earliest interface wins)
+     * - has one constructor (takes an InvocationHandler arg)
+     * - has overrides for hashCode, equals, and toString (these come first)
+     * - has one field, a reference to the InvocationHandler object, inherited
+     *   from Proxy
+     *
+     * TODO: set protection domain so it matches bootstrap classes.
+     *
+     * The idea here is to create a class object and fill in the details
+     * as we would in loadClassFromDex(), and then call dvmLinkClass() to do
+     * all the heavy lifting (notably populating the virtual and interface
+     * method tables).
+     */
+
+    /*
+     * Allocate storage for the class object and set some basic fields.
+     */
+    size_t newClassSize =
+        sizeof(ClassObject) + kProxySFieldCount * sizeof(StaticField);
+    ClassObject* newClass =
+        (ClassObject*) dvmMalloc(newClassSize, ALLOC_NON_MOVING);
+    if (newClass == NULL)
+        goto bail;
+    DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
+    newClass->descriptor = newClass->descriptorAlloc;
+    SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL);
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, super),
+                      (Object *)gDvm.classJavaLangReflectProxy);
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      OFFSETOF_MEMBER(ClassObject, classLoader),
+                      (Object *)loader);
+
+    /*
+     * Add direct method definitions.  We have one (the constructor).
+     */
+    newClass->directMethodCount = 1;
+    newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
+            1 * sizeof(Method));
+    createConstructor(newClass, &newClass->directMethods[0]);
+    dvmLinearReadOnly(newClass->classLoader, newClass->directMethods);
+
+    /*
+     * Add virtual method definitions.
+     */
+    {
+        /*
+         * Generate a temporary list of virtual methods.
+         */
+        int methodCount;
+        Method **methods;
+        if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) {
+            goto bail;
+        }
+        newClass->virtualMethodCount = methodCount;
+        size_t virtualMethodsSize = methodCount * sizeof(Method);
+        newClass->virtualMethods =
+            (Method*)dvmLinearAlloc(newClass->classLoader, virtualMethodsSize);
+        for (int i = 0; i < newClass->virtualMethodCount; i++) {
+            createHandlerMethod(newClass, &newClass->virtualMethods[i], methods[i]);
+        }
+        free(methods);
+        dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
+    }
+
+    /*
+     * Add interface list.
+     */
+    {
+        size_t interfaceCount = interfaces->length;
+        ClassObject** ifArray = (ClassObject**)(void*)interfaces->contents;
+        newClass->interfaceCount = interfaceCount;
+        size_t interfacesSize = sizeof(ClassObject*) * interfaceCount;
+        newClass->interfaces =
+            (ClassObject**)dvmLinearAlloc(newClass->classLoader, interfacesSize);
+        for (size_t i = 0; i < interfaceCount; i++)
+          newClass->interfaces[i] = ifArray[i];
+        dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+    }
+
+    /*
+     * Static field list.  We have one private field, for our list of
+     * exceptions declared for each method.
+     */
+    assert(kProxySFieldCount == 1);
+    newClass->sfieldCount = kProxySFieldCount;
+    {
+        StaticField* sfield = &newClass->sfields[kThrowsField];
+        sfield->clazz = newClass;
+        sfield->name = "throws";
+        sfield->signature = "[[Ljava/lang/Throwable;";
+        sfield->accessFlags = ACC_STATIC | ACC_PRIVATE;
+        dvmSetStaticFieldObject(sfield, (Object*)throws);
+    }
+
+    /*
+     * Everything is ready. This class didn't come out of a DEX file
+     * so we didn't tuck any indexes into the class object.  We can
+     * advance to LOADED state immediately.
+     */
+    newClass->status = CLASS_LOADED;
+    if (!dvmLinkClass(newClass)) {
+        ALOGD("Proxy class link failed");
+        goto bail;
+    }
+
+    /*
+     * All good.  Add it to the hash table.  We should NOT see a collision
+     * here; if we do, it means the caller has screwed up and provided us
+     * with a duplicate name.
+     */
+    if (!dvmAddClassToHash(newClass)) {
+        ALOGE("ERROR: attempted to generate %s more than once",
+            newClass->descriptor);
+        goto bail;
+    }
+
+    result = newClass;
+
+bail:
+    free(nameStr);
+    if (result == NULL) {
+        /* must free innards explicitly if we didn't finish linking */
+        dvmFreeClassInnards(newClass);
+        if (!dvmCheckException(dvmThreadSelf())) {
+            /* throw something */
+            dvmThrowRuntimeException(NULL);
+        }
+    }
+
+    /* allow the GC to free these when nothing else has a reference */
+    dvmReleaseTrackedAlloc((Object*) throws, NULL);
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    return result;
+}
+
+
+/*
+ * Generate a list of methods.  The Method pointers returned point to the
+ * abstract method definition from the appropriate interface, or to the
+ * virtual method definition in java.lang.Object.
+ *
+ * We also allocate an array of arrays of throwable classes, one for each
+ * method,so we can do some special handling of checked exceptions.  The
+ * caller must call ReleaseTrackedAlloc() on *pThrows.
+ */
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
+    ArrayObject** pThrows, int* pMethodCount)
+{
+    ClassObject** classes;
+    ArrayObject* throws = NULL;
+    Method** methods = NULL;
+    Method** allMethods = NULL;
+    int numInterfaces, maxCount, actualCount, allCount;
+    bool result = false;
+    int i;
+
+    /*
+     * Get a maximum count so we can allocate storage.  We need the
+     * methods declared by each interface and all of its superinterfaces.
+     */
+    maxCount = 3;       // 3 methods in java.lang.Object
+    numInterfaces = interfaces->length;
+    classes = (ClassObject**)(void*)interfaces->contents;
+
+    for (i = 0; i < numInterfaces; i++, classes++) {
+        ClassObject* clazz = *classes;
+
+        LOGVV("---  %s virtualMethodCount=%d",
+            clazz->descriptor, clazz->virtualMethodCount);
+        maxCount += clazz->virtualMethodCount;
+
+        int j;
+        for (j = 0; j < clazz->iftableCount; j++) {
+            ClassObject* iclass = clazz->iftable[j].clazz;
+
+            LOGVV("---  +%s %d",
+                iclass->descriptor, iclass->virtualMethodCount);
+            maxCount += iclass->virtualMethodCount;
+        }
+    }
+
+    methods = (Method**) malloc(maxCount * sizeof(*methods));
+    allMethods = (Method**) malloc(maxCount * sizeof(*methods));
+    if (methods == NULL || allMethods == NULL)
+        goto bail;
+
+    /*
+     * First three entries are the java.lang.Object methods.
+     */
+    {
+      ClassObject* obj = gDvm.classJavaLangObject;
+      allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
+      allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
+      allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
+      allCount = 3;
+    }
+
+    /*
+     * Add the methods from each interface, in order.
+     */
+    classes = (ClassObject**)(void*)interfaces->contents;
+    for (i = 0; i < numInterfaces; i++, classes++) {
+        ClassObject* clazz = *classes;
+        int j;
+
+        for (j = 0; j < clazz->virtualMethodCount; j++) {
+            allMethods[allCount++] = &clazz->virtualMethods[j];
+        }
+
+        for (j = 0; j < clazz->iftableCount; j++) {
+            ClassObject* iclass = clazz->iftable[j].clazz;
+            int k;
+
+            for (k = 0; k < iclass->virtualMethodCount; k++) {
+                allMethods[allCount++] = &iclass->virtualMethods[k];
+            }
+        }
+    }
+    assert(allCount == maxCount);
+
+    /*
+     * Allocate some storage to hold the lists of throwables.  We need
+     * one entry per unique method, but it's convenient to allocate it
+     * ahead of the duplicate processing.
+     */
+    ClassObject* arrArrClass;
+    arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
+    if (arrArrClass == NULL)
+        goto bail;
+    throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
+
+    /*
+     * Identify and remove duplicates.
+     */
+    actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
+    if (actualCount < 0)
+        goto bail;
+
+    //ALOGI("gathered methods:");
+    //for (i = 0; i < actualCount; i++) {
+    //    ALOGI(" %d: %s.%s",
+    //        i, methods[i]->clazz->descriptor, methods[i]->name);
+    //}
+
+    *pMethods = methods;
+    *pMethodCount = actualCount;
+    *pThrows = throws;
+    result = true;
+
+bail:
+    free(allMethods);
+    if (!result) {
+        free(methods);
+        dvmReleaseTrackedAlloc((Object*)throws, NULL);
+    }
+    return result;
+}
+
+/*
+ * Identify and remove duplicates, where "duplicate" means it has the
+ * same name and arguments, but not necessarily the same return type.
+ *
+ * If duplicate methods have different return types, we want to use the
+ * first method whose return type is assignable from all other duplicate
+ * methods.  That is, if we have:
+ *   class base {...}
+ *   class sub extends base {...}
+ *   class subsub extends sub {...}
+ * Then we want to return the method that returns subsub, since callers
+ * to any form of the method will get a usable object back.
+ *
+ * All other duplicate methods are stripped out.
+ *
+ * This also populates the "throwLists" array with arrays of Class objects,
+ * one entry per method in "outMethods".  Methods that don't declare any
+ * throwables (or have no common throwables with duplicate methods) will
+ * have NULL entries.
+ *
+ * Returns the number of methods copied into "methods", or -1 on failure.
+ */
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+    Method** outMethods, ArrayObject* throwLists)
+{
+    int outCount = 0;
+    int i, j;
+
+    /*
+     * The plan is to run through all methods, checking all other methods
+     * for a duplicate.  If we find a match, we see if the other methods'
+     * return type is compatible/assignable with ours.  If the current
+     * method is assignable from all others, we copy it to the new list,
+     * and NULL out all other entries.  If not, we keep looking for a
+     * better version.
+     *
+     * If there are no duplicates, we copy the method and NULL the entry.
+     *
+     * At the end of processing, if we have any non-NULL entries, then we
+     * have bad duplicates and must exit with an exception.
+     */
+    for (i = 0; i < allCount; i++) {
+        bool best, dupe;
+
+        if (allMethods[i] == NULL)
+            continue;
+
+        /*
+         * Find all duplicates.  If any of the return types is not
+         * assignable to our return type, then we're not the best.
+         *
+         * We start from 0, not i, because we need to compare assignability
+         * the other direction even if we've compared these before.
+         */
+        dupe = false;
+        best = true;
+        for (j = 0; j < allCount; j++) {
+            if (i == j)
+                continue;
+            if (allMethods[j] == NULL)
+                continue;
+
+            if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+                    allMethods[j]) == 0)
+            {
+                /*
+                 * Duplicate method, check return type.  If it's a primitive
+                 * type or void, the types must match exactly, or we throw
+                 * an exception now.
+                 */
+                ALOGV("MATCH on %s.%s and %s.%s",
+                    allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                    allMethods[j]->clazz->descriptor, allMethods[j]->name);
+                dupe = true;
+                if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
+                    best = false;
+            }
+        }
+
+        /*
+         * If this is the best of a set of duplicates, copy it over and
+         * nuke all duplicates.
+         *
+         * While we do this, we create the set of exceptions declared to
+         * be thrown by all occurrences of the method.
+         */
+        if (dupe) {
+            if (best) {
+                ALOGV("BEST %d %s.%s -> %d", i,
+                    allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                    outCount);
+
+                /* if we have exceptions, make a local copy */
+                PointerSet* commonThrows = NULL;
+                if (!createExceptionClassList(allMethods[i], &commonThrows))
+                    return -1;
+
+                /*
+                 * Run through one more time, erasing the duplicates.  (This
+                 * would go faster if we had marked them somehow.)
+                 */
+                for (j = 0; j < allCount; j++) {
+                    if (i == j)
+                        continue;
+                    if (allMethods[j] == NULL)
+                        continue;
+                    if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+                            allMethods[j]) == 0)
+                    {
+                        ALOGV("DEL %d %s.%s", j,
+                            allMethods[j]->clazz->descriptor,
+                            allMethods[j]->name);
+
+                        /*
+                         * Update set to hold the intersection of method[i]'s
+                         * and method[j]'s throws.
+                         */
+                        if (commonThrows != NULL) {
+                            updateExceptionClassList(allMethods[j],
+                                commonThrows);
+                        }
+
+                        allMethods[j] = NULL;
+                    }
+                }
+
+                /*
+                 * If the set of Throwable classes isn't empty, create an
+                 * array of Class, copy them into it, and put the result
+                 * into the "throwLists" array.
+                 */
+                if (commonThrows != NULL &&
+                    dvmPointerSetGetCount(commonThrows) > 0)
+                {
+                    int commonCount = dvmPointerSetGetCount(commonThrows);
+                    ArrayObject* throwArray;
+                    Object** contents;
+                    int ent;
+
+                    throwArray = dvmAllocArrayByClass(
+                            gDvm.classJavaLangClassArray, commonCount,
+                            ALLOC_DEFAULT);
+                    if (throwArray == NULL) {
+                        ALOGE("common-throw array alloc failed");
+                        return -1;
+                    }
+
+                    contents = (Object**)(void*)throwArray->contents;
+                    for (ent = 0; ent < commonCount; ent++) {
+                        contents[ent] = (Object*)
+                            dvmPointerSetGetEntry(commonThrows, ent);
+                    }
+
+                    /* add it to the array of arrays */
+                    contents = (Object**)(void*)throwLists->contents;
+                    contents[outCount] = (Object*) throwArray;
+                    dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
+                }
+
+                /* copy the winner and NULL it out */
+                outMethods[outCount++] = allMethods[i];
+                allMethods[i] = NULL;
+
+                dvmPointerSetFree(commonThrows);
+            } else {
+                ALOGV("BEST not %d", i);
+            }
+        } else {
+            /*
+             * Singleton.  Copy the entry and NULL it out.
+             */
+            ALOGV("COPY singleton %d %s.%s -> %d", i,
+                allMethods[i]->clazz->descriptor, allMethods[i]->name,
+                outCount);
+
+            /* keep track of our throwables */
+            ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
+            if (exceptionArray != NULL) {
+                Object** contents;
+
+                contents = (Object**)(void*)throwLists->contents;
+                contents[outCount] = (Object*) exceptionArray;
+                dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+            }
+
+            outMethods[outCount++] = allMethods[i];
+            allMethods[i] = NULL;
+        }
+    }
+
+    /*
+     * Check for stragglers.  If we find any, throw an exception.
+     */
+    for (i = 0; i < allCount; i++) {
+        if (allMethods[i] != NULL) {
+            ALOGV("BAD DUPE: %d %s.%s", i,
+                allMethods[i]->clazz->descriptor, allMethods[i]->name);
+            dvmThrowIllegalArgumentException(
+                "incompatible return types in proxied interfaces");
+            return -1;
+        }
+    }
+
+    return outCount;
+}
+
+
+/*
+ * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
+ * IOException and FileNotFoundException.  Since we're only interested in
+ * knowing the set that can be thrown without requiring an extra wrapper,
+ * we can remove anything that is a subclass of something else in the list.
+ *
+ * The "mix" step we do next reduces things toward the most-derived class,
+ * so it's important that we start with the least-derived classes.
+ */
+static void reduceExceptionClassList(ArrayObject* exceptionArray)
+{
+    const ClassObject** classes =
+        (const ClassObject**)(void*)exceptionArray->contents;
+
+    /*
+     * Consider all pairs of classes.  If one is the subclass of the other,
+     * null out the subclass.
+     */
+    size_t len = exceptionArray->length;
+    for (size_t i = 0; i < len - 1; i++) {
+        if (classes[i] == NULL)
+            continue;
+        for (size_t j = i + 1; j < len; j++) {
+            if (classes[j] == NULL)
+                continue;
+
+            if (dvmInstanceof(classes[i], classes[j])) {
+                classes[i] = NULL;
+                break;      /* no more comparisons against classes[i] */
+            } else if (dvmInstanceof(classes[j], classes[i])) {
+                classes[j] = NULL;
+            }
+        }
+    }
+}
+
+/*
+ * Create a local array with a copy of the throwable classes declared by
+ * "method".  If no throws are declared, "*pSet" will be NULL.
+ *
+ * Returns "false" on allocation failure.
+ */
+static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
+{
+    ArrayObject* exceptionArray = NULL;
+    bool result = false;
+
+    exceptionArray = dvmGetMethodThrows(method);
+    if (exceptionArray != NULL && exceptionArray->length > 0) {
+        /* reduce list, nulling out redundant entries */
+        reduceExceptionClassList(exceptionArray);
+
+        *pThrows = dvmPointerSetAlloc(exceptionArray->length);
+        if (*pThrows == NULL)
+            goto bail;
+
+        const ClassObject** contents;
+
+        contents = (const ClassObject**)(void*)exceptionArray->contents;
+        for (size_t i = 0; i < exceptionArray->length; i++) {
+            if (contents[i] != NULL)
+                dvmPointerSetAddEntry(*pThrows, contents[i]);
+        }
+    } else {
+        *pThrows = NULL;
+    }
+
+    result = true;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+    return result;
+}
+
+/*
+ * We need to compute the intersection of the arguments, i.e. remove
+ * anything from "throws" that isn't in the method's list of throws.
+ *
+ * If one class is a subclass of another, we want to keep just the subclass,
+ * moving toward the most-restrictive set.
+ *
+ * We assume these are all classes, and don't try to filter out interfaces.
+ */
+static void updateExceptionClassList(const Method* method, PointerSet* throws)
+{
+    int setSize = dvmPointerSetGetCount(throws);
+    if (setSize == 0)
+        return;
+
+    ArrayObject* exceptionArray = dvmGetMethodThrows(method);
+    if (exceptionArray == NULL) {
+        /* nothing declared, so intersection is empty */
+        dvmPointerSetClear(throws);
+        return;
+    }
+
+    /* reduce list, nulling out redundant entries */
+    reduceExceptionClassList(exceptionArray);
+
+    size_t mixLen = dvmPointerSetGetCount(throws);
+    const ClassObject* mixSet[mixLen];
+
+    size_t declLen = exceptionArray->length;
+    const ClassObject** declSet = (const ClassObject**)(void*)exceptionArray->contents;
+
+    /* grab a local copy to work on */
+    for (size_t i = 0; i < mixLen; i++) {
+        mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i);
+    }
+
+    for (size_t i = 0; i < mixLen; i++) {
+        size_t j;
+        for (j = 0; j < declLen; j++) {
+            if (declSet[j] == NULL)
+                continue;
+
+            if (mixSet[i] == declSet[j]) {
+                /* match, keep this one */
+                break;
+            } else if (dvmInstanceof(mixSet[i], declSet[j])) {
+                /* mix is a subclass of a declared throwable, keep it */
+                break;
+            } else if (dvmInstanceof(declSet[j], mixSet[i])) {
+                /* mix is a superclass, replace it */
+                mixSet[i] = declSet[j];
+                break;
+            }
+        }
+
+        if (j == declLen) {
+            /* no match, remove entry by nulling it out */
+            mixSet[i] = NULL;
+        }
+    }
+
+    /* copy results back out; this eliminates duplicates as we go */
+    dvmPointerSetClear(throws);
+    for (size_t i = 0; i < mixLen; i++) {
+        if (mixSet[i] != NULL)
+            dvmPointerSetAddEntry(throws, mixSet[i]);
+    }
+
+    dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+}
+
+
+/*
+ * Check to see if the return types are compatible.
+ *
+ * If the return type is primitive or void, it must match exactly.
+ *
+ * If not, the type in "subMethod" must be assignable to the type in
+ * "baseMethod".
+ */
+static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
+{
+    const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
+    const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
+    ClassObject* baseClass;
+    ClassObject* subClass;
+
+    if (baseSig[1] == '\0' || subSig[1] == '\0') {
+        /* at least one is primitive type */
+        return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
+    }
+
+    baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
+    subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
+    bool result = dvmInstanceof(subClass, baseClass);
+    return result;
+}
+
+/*
+ * Create a constructor for our Proxy class.  The constructor takes one
+ * argument, a java.lang.reflect.InvocationHandler.
+ */
+static void createConstructor(ClassObject* clazz, Method* meth)
+{
+    /*
+     * The constructor signatures (->prototype and ->shorty) need to
+     * be cloned from a method in a "real" DEX file. We declared the
+     * otherwise unused method Proxy.constructorPrototype() just for
+     * this purpose.
+     */
+
+    meth->clazz = clazz;
+    meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+    meth->name = "<init>";
+    meth->prototype =
+        gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
+    meth->shorty =
+        gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
+    // no pDexCode or pDexMethod
+
+    int argsSize = dvmComputeMethodArgsSize(meth) + 1;
+    meth->registersSize = meth->insSize = argsSize;
+
+    meth->nativeFunc = proxyConstructor;
+}
+
+/*
+ * Create a method in our Proxy class with the name and signature of
+ * the interface method it implements.
+ */
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+    const Method* srcMeth)
+{
+    dstMeth->clazz = clazz;
+    dstMeth->insns = (u2*) srcMeth;
+    dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+    dstMeth->name = srcMeth->name;
+    dstMeth->prototype = srcMeth->prototype;
+    dstMeth->shorty = srcMeth->shorty;
+    // no pDexCode or pDexMethod
+
+    int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
+    dstMeth->registersSize = dstMeth->insSize = argsSize;
+
+    dstMeth->nativeFunc = proxyInvoker;
+}
+
+/*
+ * Return a new Object[] array with the contents of "args".  We determine
+ * the number and types of values in "args" based on the method signature.
+ * Primitive types are boxed.
+ *
+ * Returns NULL if the method takes no arguments.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * On failure, returns with an appropriate exception raised.
+ */
+static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
+{
+    const char* desc = &method->shorty[1]; // [0] is the return type.
+
+    /* count args */
+    size_t argCount = dexProtoGetParameterCount(&method->prototype);
+
+    /* allocate storage */
+    ArrayObject* argArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+        argCount, ALLOC_DEFAULT);
+    if (argArray == NULL)
+        return NULL;
+    Object** argObjects = (Object**)(void*)argArray->contents;
+
+    /*
+     * Fill in the array.
+     */
+
+    size_t srcIndex = 0;
+    size_t dstIndex = 0;
+    while (*desc != '\0') {
+        char descChar = *(desc++);
+        JValue value;
+
+        switch (descChar) {
+        case 'Z':
+        case 'C':
+        case 'F':
+        case 'B':
+        case 'S':
+        case 'I':
+            value.i = args[srcIndex++];
+            argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            /* argObjects is tracked, don't need to hold this too */
+            dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
+            dstIndex++;
+            break;
+        case 'D':
+        case 'J':
+            value.j = dvmGetArgLong(args, srcIndex);
+            srcIndex += 2;
+            argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
+            dstIndex++;
+            break;
+        case '[':
+        case 'L':
+            argObjects[dstIndex++] = (Object*) args[srcIndex++];
+            break;
+        }
+    }
+
+    return argArray;
+}
+
+/*
+ * This is the constructor for a generated proxy object.  All we need to
+ * do is stuff "handler" into "h".
+ */
+static void proxyConstructor(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* obj = (Object*) args[0];
+    Object* handler = (Object*) args[1];
+
+    dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
+}
+
+/*
+ * This is the common message body for proxy methods.
+ *
+ * The method we're calling looks like:
+ *   public Object invoke(Object proxy, Method method, Object[] args)
+ *
+ * This means we have to create a Method object, box our arguments into
+ * a new Object[] array, make the call, and unbox the return value if
+ * necessary.
+ */
+static void proxyInvoker(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* thisObj = (Object*) args[0];
+    Object* methodObj = NULL;
+    ArrayObject* argArray = NULL;
+    Object* handler;
+    Method* invoke;
+    ClassObject* returnType;
+    JValue invokeResult;
+
+    /*
+     * Retrieve handler object for this proxy instance.  The field is
+     * defined in the superclass (Proxy).
+     */
+    handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
+
+    /*
+     * Find the invoke() method, looking in "this"s class.  (Because we
+     * start here we don't have to convert it to a vtable index and then
+     * index into this' vtable.)
+     */
+    invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
+            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+    if (invoke == NULL) {
+        ALOGE("Unable to find invoke()");
+        dvmAbort();
+    }
+
+    ALOGV("invoke: %s.%s, this=%p, handler=%s",
+        method->clazz->descriptor, method->name,
+        thisObj, handler->clazz->descriptor);
+
+    /*
+     * Create a java.lang.reflect.Method object for this method.
+     *
+     * We don't want to use "method", because that's the concrete
+     * implementation in the proxy class.  We want the abstract Method
+     * from the declaring interface.  We have a pointer to it tucked
+     * away in the "insns" field.
+     *
+     * TODO: this could be cached for performance.
+     */
+    methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
+    if (methodObj == NULL) {
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+
+    /*
+     * Determine the return type from the signature.
+     *
+     * TODO: this could be cached for performance.
+     */
+    returnType = dvmGetBoxedReturnType(method);
+    if (returnType == NULL) {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        ALOGE("Could not determine return type for '%s'", desc);
+        free(desc);
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+    ALOGV("  return type will be %s", returnType->descriptor);
+
+    /*
+     * Convert "args" array into Object[] array, using the method
+     * signature to determine types.  If the method takes no arguments,
+     * we must pass null.
+     */
+    argArray = boxMethodArgs(method, args+1);
+    if (dvmCheckException(self))
+        goto bail;
+
+    /*
+     * Call h.invoke(proxy, method, args).
+     *
+     * We don't need to repackage exceptions, so if one has been thrown
+     * just jump to the end.
+     *
+     * We're not adding invokeResult.l to the tracked allocation list, but
+     * since we're just unboxing it or returning it to interpreted code
+     * that shouldn't be a problem.
+     */
+    dvmCallMethod(self, invoke, handler, &invokeResult,
+        thisObj, methodObj, argArray);
+    if (dvmCheckException(self)) {
+        Object* excep = dvmGetException(self);
+        if (mustWrapException(method, excep)) {
+            /* wrap with UndeclaredThrowableException */
+            dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
+        }
+        goto bail;
+    }
+
+    /*
+     * Unbox the return value.  If it's the wrong type, throw a
+     * ClassCastException.  If it's a null pointer and we need a
+     * primitive type, throw a NullPointerException.
+     */
+    if (returnType->primitiveType == PRIM_VOID) {
+        LOGVV("+++ ignoring return to void");
+    } else if (invokeResult.l == NULL) {
+        if (dvmIsPrimitiveClass(returnType)) {
+            dvmThrowNullPointerException(
+                "null result when primitive expected");
+            goto bail;
+        }
+        pResult->l = NULL;
+    } else {
+        if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) {
+            dvmThrowClassCastException(((Object*)invokeResult.l)->clazz,
+                    returnType);
+            goto bail;
+        }
+    }
+
+bail:
+    dvmReleaseTrackedAlloc(methodObj, self);
+    dvmReleaseTrackedAlloc((Object*)argArray, self);
+}
+
+/*
+ * Determine if it's okay for this method to throw this exception.  If
+ * an unchecked exception was thrown we immediately return false.  If
+ * checked, we have to ensure that this method and all of its duplicates
+ * have declared that they throw it.
+ */
+static bool mustWrapException(const Method* method, const Object* throwable)
+{
+    if (!dvmIsCheckedException(throwable))
+        return false;
+
+    const StaticField* sfield = &method->clazz->sfields[kThrowsField];
+    const ArrayObject* throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
+
+    int methodIndex = method - method->clazz->virtualMethods;
+    assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
+
+    const Object** contents = (const Object**)(void*)throws->contents;
+    const ArrayObject* methodThrows = (ArrayObject*) contents[methodIndex];
+
+    if (methodThrows == NULL) {
+        /* no throws declared, must wrap all checked exceptions */
+        return true;
+    }
+
+    size_t throwCount = methodThrows->length;
+    const ClassObject** classes =
+        (const ClassObject**)(void*)methodThrows->contents;
+
+    for (size_t i = 0; i < throwCount; i++) {
+        if (dvmInstanceof(throwable->clazz, classes[i])) {
+            /* this was declared, okay to throw */
+            return false;
+        }
+    }
+
+    /* no match in declared throws */
+    return true;
+}
diff --git a/vm/reflect/Reflect.cpp b/vm/reflect/Reflect.cpp
new file mode 100644
index 0000000..9cb9fc7
--- /dev/null
+++ b/vm/reflect/Reflect.cpp
@@ -0,0 +1,1278 @@
+/*
+ * Copyright (C) 2008 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 reflection calls and utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * For some of the reflection stuff we need to un-box primitives, e.g.
+ * convert a java/lang/Integer to int or even a float.  We assume that
+ * the first instance field holds the value.
+ *
+ * To verify this, we either need to ensure that the class has only one
+ * instance field, or we need to look up the field by name and verify
+ * that it comes first.  The former is simpler, and should work.
+ */
+bool dvmValidateBoxClasses()
+{
+    static const char* classes[] = {
+        "Ljava/lang/Boolean;",
+        "Ljava/lang/Character;",
+        "Ljava/lang/Float;",
+        "Ljava/lang/Double;",
+        "Ljava/lang/Byte;",
+        "Ljava/lang/Short;",
+        "Ljava/lang/Integer;",
+        "Ljava/lang/Long;",
+        NULL
+    };
+    const char** ccp;
+
+    for (ccp = classes; *ccp != NULL; ccp++) {
+        ClassObject* clazz;
+
+        clazz = dvmFindClassNoInit(*ccp, NULL);
+        if (clazz == NULL) {
+            ALOGE("Couldn't find '%s'", *ccp);
+            return false;
+        }
+
+        if (clazz->ifieldCount != 1) {
+            ALOGE("Found %d instance fields in '%s'",
+                clazz->ifieldCount, *ccp);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Find the named class object.  We have to trim "*pSignature" down to just
+ * the first token, do the lookup, and then restore anything important
+ * that we've stomped on.
+ *
+ * "pSig" will be advanced to the start of the next token.
+ */
+static ClassObject* convertSignaturePartToClass(char** pSignature,
+    const ClassObject* defClass)
+{
+    ClassObject* clazz = NULL;
+    char* signature = *pSignature;
+
+    if (*signature == '[') {
+        /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */
+        char savedChar;
+
+        while (*++signature == '[')
+            ;
+        if (*signature == 'L') {
+            while (*++signature != ';')
+                ;
+        }
+
+        /* advance past ';', and stomp on whatever comes next */
+        savedChar = *++signature;
+        *signature = '\0';
+        clazz = dvmFindArrayClass(*pSignature, defClass->classLoader);
+        *signature = savedChar;
+    } else if (*signature == 'L') {
+        /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */
+        char savedChar;
+        while (*++signature != ';')
+            ;
+        savedChar = *++signature;
+        *signature = '\0';
+        clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader);
+        *signature = savedChar;
+    } else {
+        clazz = dvmFindPrimitiveClass(*signature++);
+    }
+
+    if (clazz == NULL) {
+        ALOGW("Unable to match class for part: '%s'", *pSignature);
+    }
+    *pSignature = signature;
+    return clazz;
+}
+
+/*
+ * Convert the method signature to an array of classes.
+ *
+ * The tokenization process may mangle "*pSignature".  On return, it will
+ * be pointing at the closing ')'.
+ *
+ * "defClass" is the method's class, which is needed to make class loaders
+ * happy.
+ */
+static ArrayObject* convertSignatureToClassArray(char** pSignature,
+    ClassObject* defClass)
+{
+    char* signature = *pSignature;
+
+    assert(*signature == '(');
+    signature++;
+
+    /* count up the number of parameters */
+    size_t count = 0;
+    char* cp = signature;
+    while (*cp != ')') {
+        count++;
+
+        if (*cp == '[') {
+            while (*++cp == '[')
+                ;
+        }
+        if (*cp == 'L') {
+            while (*++cp != ';')
+                ;
+        }
+        cp++;
+    }
+    LOGVV("REFLECT found %d parameters in '%s'", count, *pSignature);
+
+    /* create an array to hold them */
+    ArrayObject* classArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+                     count, ALLOC_DEFAULT);
+    if (classArray == NULL)
+        return NULL;
+
+    /* fill it in */
+    cp = signature;
+    for (size_t i = 0; i < count; i++) {
+        ClassObject* clazz = convertSignaturePartToClass(&cp, defClass);
+        if (clazz == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+        LOGVV("REFLECT  %d: '%s'", i, clazz->descriptor);
+        dvmSetObjectArrayElement(classArray, i, (Object *)clazz);
+    }
+
+    *pSignature = cp;
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return classArray;
+}
+
+
+/*
+ * Convert a field pointer to a slot number.
+ *
+ * We use positive values starting from 0 for instance fields, negative
+ * values starting from -1 for static fields.
+ */
+static int fieldToSlot(const Field* field, const ClassObject* clazz)
+{
+    int slot;
+
+    if (dvmIsStaticField(field)) {
+        slot = (StaticField*)field - &clazz->sfields[0];
+        assert(slot >= 0 && slot < clazz->sfieldCount);
+        slot = -(slot+1);
+    } else {
+        slot = (InstField*)field - clazz->ifields;
+        assert(slot >= 0 && slot < clazz->ifieldCount);
+    }
+
+    return slot;
+}
+
+/*
+ * Convert a slot number to a field pointer.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot)
+{
+    if (slot < 0) {
+        slot = -(slot+1);
+        assert(slot < clazz->sfieldCount);
+        return (Field*)(void*)&clazz->sfields[slot];
+    } else {
+        assert(slot < clazz->ifieldCount);
+        return (Field*)(void*)&clazz->ifields[slot];
+    }
+}
+
+/*
+ * Create a new java.lang.reflect.Field object from "field".
+ *
+ * The Field spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Field(Class declaringClass, Class type, String name, int slot)
+ */
+static Object* createFieldObject(Field* field, const ClassObject* clazz)
+{
+    Object* result = NULL;
+    Object* fieldObj = NULL;
+    StringObject* nameObj = NULL;
+    ClassObject* type;
+    char* mangle;
+    char* cp;
+    int slot, field_idx;
+
+    assert(dvmIsClassInitialized(gDvm.classJavaLangReflectField));
+
+    fieldObj = dvmAllocObject(gDvm.classJavaLangReflectField, ALLOC_DEFAULT);
+    if (fieldObj == NULL)
+        goto bail;
+
+    cp = mangle = strdup(field->signature);
+    type = convertSignaturePartToClass(&cp, clazz);
+    free(mangle);
+    if (type == NULL)
+        goto bail;
+
+    nameObj = dvmCreateStringFromCstr(field->name);
+    if (nameObj == NULL)
+        goto bail;
+
+    slot = fieldToSlot(field, clazz);
+    field_idx = dvmGetFieldIdx(field);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init,
+        fieldObj, &unused, clazz, type, nameObj, slot, field_idx);
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGD("Field class init threw exception");
+        goto bail;
+    }
+
+    result = fieldObj;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    if (result == NULL)
+        dvmReleaseTrackedAlloc((Object*) fieldObj, NULL);
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return result;
+}
+
+/*
+ *
+ * Get an array with all fields declared by a class.
+ *
+ * This includes both static and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    /* count #of fields */
+    size_t count;
+    if (!publicOnly)
+        count = clazz->sfieldCount + clazz->ifieldCount;
+    else {
+        count = 0;
+        for (int i = 0; i < clazz->sfieldCount; i++) {
+            if ((clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+        for (int i = 0; i < clazz->ifieldCount; i++) {
+            if ((clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+    }
+
+    /* create the Field[] array */
+    ArrayObject* fieldArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangReflectFieldArray, count, ALLOC_DEFAULT);
+    if (fieldArray == NULL)
+        return NULL;
+
+    /* populate */
+    size_t fieldCount = 0;
+    for (int i = 0; i < clazz->sfieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->sfields[i], clazz);
+            if (field == NULL) {
+                goto fail;
+            }
+            dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+            dvmReleaseTrackedAlloc(field, NULL);
+            ++fieldCount;
+        }
+    }
+    for (int i = 0; i < clazz->ifieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->ifields[i], clazz);
+            if (field == NULL) {
+                goto fail;
+            }
+            dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+            dvmReleaseTrackedAlloc(field, NULL);
+            ++fieldCount;
+        }
+    }
+
+    assert(fieldCount == fieldArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return fieldArray;
+
+fail:
+    dvmReleaseTrackedAlloc((Object*) fieldArray, NULL);
+    return NULL;
+}
+
+
+/*
+ * Convert a method pointer to a slot number.
+ *
+ * We use positive values starting from 0 for virtual methods, negative
+ * values starting from -1 for static methods.
+ */
+static int methodToSlot(const Method* meth)
+{
+    ClassObject* clazz = meth->clazz;
+    int slot;
+
+    if (dvmIsDirectMethod(meth)) {
+        slot = meth - clazz->directMethods;
+        assert(slot >= 0 && slot < clazz->directMethodCount);
+        slot = -(slot+1);
+    } else {
+        slot = meth - clazz->virtualMethods;
+        assert(slot >= 0 && slot < clazz->virtualMethodCount);
+    }
+
+    return slot;
+}
+
+/*
+ * Convert a slot number to a method pointer.
+ */
+Method* dvmSlotToMethod(ClassObject* clazz, int slot)
+{
+    if (slot < 0) {
+        slot = -(slot+1);
+        assert(slot < clazz->directMethodCount);
+        return &clazz->directMethods[slot];
+    } else {
+        assert(slot < clazz->virtualMethodCount);
+        return &clazz->virtualMethods[slot];
+    }
+}
+
+/*
+ * Create a new java/lang/reflect/Constructor object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Constructor (Class declaringClass, Class[] ptypes, Class[] extypes,
+ *      int slot)
+ */
+static Object* createConstructorObject(Method* meth)
+{
+    Object* result = NULL;
+    ArrayObject* params = NULL;
+    ArrayObject* exceptions = NULL;
+    Object* consObj;
+    DexStringCache mangle;
+    char* cp;
+    int slot, method_idx;
+
+    dexStringCacheInit(&mangle);
+
+    /* parent should guarantee init so we don't have to check on every call */
+    assert(dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor));
+
+    consObj = dvmAllocObject(gDvm.classJavaLangReflectConstructor,
+                ALLOC_DEFAULT);
+    if (consObj == NULL)
+        goto bail;
+
+    /*
+     * Convert the signature string into an array of classes representing
+     * the arguments.
+     */
+    cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
+    params = convertSignatureToClassArray(&cp, meth->clazz);
+    if (params == NULL)
+        goto bail;
+    assert(*cp == ')');
+    assert(*(cp+1) == 'V');
+
+    /*
+     * Create an array with one entry for every exception that the class
+     * is declared to throw.
+     */
+    exceptions = dvmGetMethodThrows(meth);
+    if (dvmCheckException(dvmThreadSelf()))
+        goto bail;
+
+    slot = methodToSlot(meth);
+    method_idx = dvmGetMethodIdx(meth);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectConstructor_init,
+        consObj, &unused, meth->clazz, params, exceptions, slot, method_idx);
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGD("Constructor class init threw exception");
+        goto bail;
+    }
+
+    result = consObj;
+
+bail:
+    dexStringCacheRelease(&mangle);
+    dvmReleaseTrackedAlloc((Object*) params, NULL);
+    dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+    if (result == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+        dvmReleaseTrackedAlloc(consObj, NULL);
+    }
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return result;
+}
+
+/*
+ * Get an array with all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+        dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+    /*
+     * Ordinarily we init the class the first time we resolve a method.
+     * We're bypassing the normal resolution mechanism, so we init it here.
+     */
+    if (!dvmIsClassInitialized(clazz))
+        dvmInitClass(clazz);
+
+    /*
+     * Count up the #of relevant methods.
+     */
+    size_t count = 0;
+    for (int i = 0; i < clazz->directMethodCount; ++i) {
+        Method* meth = &clazz->directMethods[i];
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Constructor objects.
+     */
+    ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray;
+    ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
+    if (ctorArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    size_t ctorObjCount = 0;
+    for (int i = 0; i < clazz->directMethodCount; ++i) {
+        Method* meth = &clazz->directMethods[i];
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            Object* ctorObj = createConstructorObject(meth);
+            if (ctorObj == NULL) {
+              dvmReleaseTrackedAlloc((Object*) ctorArray, NULL);
+              return NULL;
+            }
+            dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj);
+            ++ctorObjCount;
+            dvmReleaseTrackedAlloc(ctorObj, NULL);
+        }
+    }
+
+    assert(ctorObjCount == ctorArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return ctorArray;
+}
+
+/*
+ * Create a new java/lang/reflect/Method object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor.  We're going to use the
+ * one from our existing class libs:
+ *
+ *  private Method(Class declaring, Class[] paramTypes, Class[] exceptTypes,
+ *      Class returnType, String name, int slot)
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the result.
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth)
+{
+    Object* result = NULL;
+    ArrayObject* params = NULL;
+    ArrayObject* exceptions = NULL;
+    StringObject* nameObj = NULL;
+    Object* methObj;
+    ClassObject* returnType;
+    DexStringCache mangle;
+    char* cp;
+    int slot, method_idx;
+
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGW("WARNING: dvmCreateReflectMethodObject called with "
+             "exception pending");
+        return NULL;
+    }
+
+    dexStringCacheInit(&mangle);
+
+    /* parent should guarantee init so we don't have to check on every call */
+    assert(dvmIsClassInitialized(gDvm.classJavaLangReflectMethod));
+
+    methObj = dvmAllocObject(gDvm.classJavaLangReflectMethod, ALLOC_DEFAULT);
+    if (methObj == NULL)
+        goto bail;
+
+    /*
+     * Convert the signature string into an array of classes representing
+     * the arguments, and a class for the return type.
+     */
+    cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
+    params = convertSignatureToClassArray(&cp, meth->clazz);
+    if (params == NULL)
+        goto bail;
+    assert(*cp == ')');
+    cp++;
+    returnType = convertSignaturePartToClass(&cp, meth->clazz);
+    if (returnType == NULL)
+        goto bail;
+
+    /*
+     * Create an array with one entry for every exception that the class
+     * is declared to throw.
+     */
+    exceptions = dvmGetMethodThrows(meth);
+    if (dvmCheckException(dvmThreadSelf()))
+        goto bail;
+
+    /* method name */
+    nameObj = dvmCreateStringFromCstr(meth->name);
+    if (nameObj == NULL)
+        goto bail;
+
+    slot = methodToSlot(meth);
+    method_idx = dvmGetMethodIdx(meth);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init,
+        methObj, &unused, meth->clazz, params, exceptions, returnType,
+        nameObj, slot, method_idx);
+    if (dvmCheckException(dvmThreadSelf())) {
+        ALOGD("Method class init threw exception");
+        goto bail;
+    }
+
+    result = methObj;
+
+bail:
+    dexStringCacheRelease(&mangle);
+    if (result == NULL) {
+        assert(dvmCheckException(dvmThreadSelf()));
+    }
+    dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+    dvmReleaseTrackedAlloc((Object*) params, NULL);
+    dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+    if (result == NULL)
+        dvmReleaseTrackedAlloc(methObj, NULL);
+    return result;
+}
+
+/*
+ * Get an array with all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false.  It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Count up the #of relevant methods.
+     *
+     * Ignore virtual Miranda methods and direct class/object constructors.
+     */
+    size_t count = 0;
+    Method* meth = clazz->virtualMethods;
+    for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            !dvmIsMirandaMethod(meth))
+        {
+            count++;
+        }
+    }
+    meth = clazz->directMethods;
+    for (int i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Method objects.
+     */
+    ArrayObject* methodArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangReflectMethodArray, count, ALLOC_DEFAULT);
+    if (methodArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    meth = clazz->virtualMethods;
+    size_t methObjCount = 0;
+    for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            !dvmIsMirandaMethod(meth))
+        {
+            Object* methObj = dvmCreateReflectMethodObject(meth);
+            if (methObj == NULL)
+                goto fail;
+            dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+            ++methObjCount;
+            dvmReleaseTrackedAlloc(methObj, NULL);
+        }
+    }
+    meth = clazz->directMethods;
+    for (int i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            meth->name[0] != '<')
+        {
+            Object* methObj = dvmCreateReflectMethodObject(meth);
+            if (methObj == NULL)
+                goto fail;
+            dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+            ++methObjCount;
+            dvmReleaseTrackedAlloc(methObj, NULL);
+        }
+    }
+
+    assert(methObjCount == methodArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return methodArray;
+
+fail:
+    dvmReleaseTrackedAlloc((Object*) methodArray, NULL);
+    return NULL;
+}
+
+/*
+ * Fills targetDescriptorCache with the descriptors of the classes in args.
+ * This is the concatenation of the descriptors with no other adornment,
+ * consistent with dexProtoGetParameterDescriptors.
+ */
+static void createTargetDescriptor(ArrayObject* args,
+    DexStringCache* targetDescriptorCache)
+{
+    ClassObject** argsArray = (ClassObject**)(void*)args->contents;
+    size_t length = 1; /* +1 for the terminating '\0' */
+    for (size_t i = 0; i < args->length; ++i) {
+        length += strlen(argsArray[i]->descriptor);
+    }
+
+    dexStringCacheAlloc(targetDescriptorCache, length);
+
+    char* at = (char*) targetDescriptorCache->value;
+    for (size_t i = 0; i < args->length; ++i) {
+        const char* descriptor = argsArray[i]->descriptor;
+        strcpy(at, descriptor);
+        at += strlen(descriptor);
+    }
+}
+
+static Object* findConstructorOrMethodInArray(int methodsCount, Method* methods,
+    const char* name, const char* parameterDescriptors)
+{
+    Method* method = NULL;
+    Method* result = NULL;
+    int i;
+
+    for (i = 0; i < methodsCount; ++i) {
+        method = &methods[i];
+        if (strcmp(name, method->name) != 0
+            || dvmIsMirandaMethod(method)
+            || dexProtoCompareToParameterDescriptors(&method->prototype,
+                    parameterDescriptors) != 0) {
+            continue;
+        }
+
+        result = method;
+
+        /*
+         * Covariant return types permit the class to define multiple
+         * methods with the same name and parameter types. Prefer to return
+         * a non-synthetic method in such situations. We may still return
+         * a synthetic method to handle situations like escalated visibility.
+         */
+        if (!dvmIsSyntheticMethod(method)) {
+            break;
+        }
+    }
+
+    if (result != NULL) {
+        return dvmCreateReflectObjForMethod(result->clazz, result);
+    }
+
+    return NULL;
+}
+
+/*
+ * Get the named method.
+ */
+Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz,
+    StringObject* nameObj, ArrayObject* args)
+{
+    Object* result = NULL;
+    DexStringCache targetDescriptorCache;
+    char* name;
+    const char* targetDescriptor;
+
+    dexStringCacheInit(&targetDescriptorCache);
+
+    name = dvmCreateCstrFromString(nameObj);
+    createTargetDescriptor(args, &targetDescriptorCache);
+    targetDescriptor = targetDescriptorCache.value;
+
+    result = findConstructorOrMethodInArray(clazz->directMethodCount,
+        clazz->directMethods, name, targetDescriptor);
+    if (result == NULL) {
+        result = findConstructorOrMethodInArray(clazz->virtualMethodCount,
+            clazz->virtualMethods, name, targetDescriptor);
+    }
+
+    free(name);
+    dexStringCacheRelease(&targetDescriptorCache);
+    return result;
+}
+
+/*
+ * Get the named field.
+ */
+Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj)
+{
+    int i;
+    Object* fieldObj = NULL;
+    char* name = dvmCreateCstrFromString(nameObj);
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    for (i = 0; i < clazz->sfieldCount; i++) {
+        Field* field = &clazz->sfields[i];
+        if (strcmp(name, field->name) == 0) {
+            fieldObj = createFieldObject(field, clazz);
+            break;
+        }
+    }
+    if (fieldObj == NULL) {
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            Field* field = &clazz->ifields[i];
+            if (strcmp(name, field->name) == 0) {
+                fieldObj = createFieldObject(field, clazz);
+                break;
+            }
+        }
+    }
+
+    free(name);
+    return fieldObj;
+}
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Create an array of Class objects.
+     */
+    size_t count = clazz->interfaceCount;
+    ArrayObject* interfaceArray =
+        dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT);
+    if (interfaceArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    memcpy(interfaceArray->contents, clazz->interfaces,
+           count * sizeof(Object *));
+    dvmWriteBarrierArray(interfaceArray, 0, count);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return interfaceArray;
+}
+
+/*
+ * Given a boxed primitive type, such as java/lang/Integer, return the
+ * primitive type index.
+ *
+ * Returns PRIM_NOT for void, since we never "box" that.
+ */
+static PrimitiveType getBoxedType(DataObject* arg)
+{
+    static const int kJavaLangLen = 11;     // strlen("Ljava/lang/")
+
+    if (arg == NULL)
+        return PRIM_NOT;
+
+    const char* name = arg->clazz->descriptor;
+
+    if (strncmp(name, "Ljava/lang/", kJavaLangLen) != 0)
+        return PRIM_NOT;
+
+    if (strcmp(name + kJavaLangLen, "Boolean;") == 0)
+        return PRIM_BOOLEAN;
+    if (strcmp(name + kJavaLangLen, "Character;") == 0)
+        return PRIM_CHAR;
+    if (strcmp(name + kJavaLangLen, "Float;") == 0)
+        return PRIM_FLOAT;
+    if (strcmp(name + kJavaLangLen, "Double;") == 0)
+        return PRIM_DOUBLE;
+    if (strcmp(name + kJavaLangLen, "Byte;") == 0)
+        return PRIM_BYTE;
+    if (strcmp(name + kJavaLangLen, "Short;") == 0)
+        return PRIM_SHORT;
+    if (strcmp(name + kJavaLangLen, "Integer;") == 0)
+        return PRIM_INT;
+    if (strcmp(name + kJavaLangLen, "Long;") == 0)
+        return PRIM_LONG;
+    return PRIM_NOT;
+}
+
+/*
+ * Convert primitive, boxed data from "srcPtr" to "dstPtr".
+ *
+ * Section v2 2.6 lists the various conversions and promotions.  We
+ * allow the "widening" and "identity" conversions, but don't allow the
+ * "narrowing" conversions.
+ *
+ * Allowed:
+ *  byte to short, int, long, float, double
+ *  short to int, long, float double
+ *  char to int, long, float, double
+ *  int to long, float, double
+ *  long to float, double
+ *  float to double
+ * Values of types byte, char, and short are "internally" widened to int.
+ *
+ * Returns the width in 32-bit words of the destination primitive, or
+ * -1 if the conversion is not allowed.
+ *
+ * TODO? use JValue rather than u4 pointers
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+    PrimitiveType dstType, const s4* srcPtr, s4* dstPtr)
+{
+    enum Conversion {
+        OK4, OK8, ItoJ, ItoD, JtoD, FtoD, ItoF, JtoF, bad
+    };
+
+    enum Conversion conv;
+#ifdef ARCH_HAVE_ALIGNED_DOUBLES
+    double ret;
+#endif
+
+    assert((srcType != PRIM_VOID) && (srcType != PRIM_NOT));
+    assert((dstType != PRIM_VOID) && (dstType != PRIM_NOT));
+
+    switch (dstType) {
+        case PRIM_BOOLEAN:
+        case PRIM_CHAR:
+        case PRIM_BYTE: {
+            conv = (srcType == dstType) ? OK4 : bad;
+            break;
+        }
+        case PRIM_SHORT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_SHORT: conv = OK4; break;
+                default:         conv = bad; break;
+            }
+            break;
+        }
+        case PRIM_INT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = OK4; break;
+                default:         conv = bad; break;
+            }
+            break;
+        }
+        case PRIM_LONG: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = ItoJ; break;
+                case PRIM_LONG:  conv = OK8;  break;
+                default:         conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_FLOAT: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:   conv = ItoF; break;
+                case PRIM_LONG:  conv = JtoF; break;
+                case PRIM_FLOAT: conv = OK4;  break;
+                default:         conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_DOUBLE: {
+            switch (srcType) {
+                case PRIM_BYTE:
+                case PRIM_CHAR:
+                case PRIM_SHORT:
+                case PRIM_INT:    conv = ItoD; break;
+                case PRIM_LONG:   conv = JtoD; break;
+                case PRIM_FLOAT:  conv = FtoD; break;
+                case PRIM_DOUBLE: conv = OK8;  break;
+                default:          conv = bad;  break;
+            }
+            break;
+        }
+        case PRIM_VOID:
+        case PRIM_NOT:
+        default: {
+            conv = bad;
+            break;
+        }
+    }
+
+    switch (conv) {
+        case OK4:  *dstPtr = *srcPtr;                                   return 1;
+        case OK8:  *(s8*) dstPtr = *(s8*)srcPtr;                        return 2;
+        case ItoJ: *(s8*) dstPtr = (s8) (*(s4*) srcPtr);                return 2;
+#ifndef ARCH_HAVE_ALIGNED_DOUBLES
+        case ItoD: *(double*) dstPtr = (double) (*(s4*) srcPtr);        return 2;
+        case JtoD: *(double*) dstPtr = (double) (*(long long*) srcPtr); return 2;
+        case FtoD: *(double*) dstPtr = (double) (*(float*) srcPtr);     return 2;
+#else
+        case ItoD: ret = (double) (*(s4*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+        case JtoD: ret = (double) (*(long long*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+        case FtoD: ret = (double) (*(float*) srcPtr); memcpy(dstPtr, &ret, 8); return 2;
+#endif
+        case ItoF: *(float*) dstPtr = (float) (*(int*) srcPtr);         return 1;
+        case JtoF: *(float*) dstPtr = (float) (*(long long*) srcPtr);   return 1;
+        case bad: {
+            ALOGV("illegal primitive conversion: '%s' to '%s'",
+                    dexGetPrimitiveTypeDescriptor(srcType),
+                    dexGetPrimitiveTypeDescriptor(dstType));
+            return -1;
+        }
+        default: {
+            dvmAbort();
+            return -1; // Keep the compiler happy.
+        }
+    }
+}
+
+/*
+ * Convert types and widen primitives.  Puts the value of "arg" into
+ * "destPtr".
+ *
+ * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error.
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr)
+{
+    int retVal;
+
+    if (dvmIsPrimitiveClass(type)) {
+        /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */
+        PrimitiveType srcType;
+        s4* valuePtr;
+
+        srcType = getBoxedType(arg);
+        if (srcType == PRIM_NOT) {     // didn't pass a boxed primitive in
+            LOGVV("conv arg: type '%s' not boxed primitive",
+                arg->clazz->descriptor);
+            return -1;
+        }
+
+        /* assumes value is stored in first instance field */
+        valuePtr = (s4*) arg->instanceData;
+
+        retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType,
+                    valuePtr, destPtr);
+    } else {
+        /* verify object is compatible */
+        if ((arg == NULL) || dvmInstanceof(arg->clazz, type)) {
+            *destPtr = (s4) arg;
+            retVal = 1;
+        } else {
+            LOGVV("Arg %p (%s) not compatible with %s",
+                arg, arg->clazz->descriptor, type->descriptor);
+            retVal = -1;
+        }
+    }
+
+    return retVal;
+}
+
+/*
+ * Create a wrapper object for a primitive data type.  If "returnType" is
+ * not primitive, this just casts "value" to an object and returns it.
+ *
+ * We could invoke the "toValue" method on the box types to take
+ * advantage of pre-created values, but running that through the
+ * interpreter is probably less efficient than just allocating storage here.
+ *
+ * The caller must call dvmReleaseTrackedAlloc on the result.
+ */
+DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType)
+{
+    ClassObject* wrapperClass;
+    DataObject* wrapperObj;
+    s4* dataPtr;
+    PrimitiveType typeIndex = returnType->primitiveType;
+    const char* classDescriptor;
+
+    if (typeIndex == PRIM_NOT) {
+        /* add to tracking table so return value is always in table */
+        if (value.l != NULL)
+            dvmAddTrackedAlloc((Object*)value.l, NULL);
+        return (DataObject*) value.l;
+    }
+
+    classDescriptor = dexGetBoxedTypeDescriptor(typeIndex);
+    if (classDescriptor == NULL) {
+        return NULL;
+    }
+
+    wrapperClass = dvmFindSystemClass(classDescriptor);
+    if (wrapperClass == NULL) {
+        ALOGW("Unable to find '%s'", classDescriptor);
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+
+    wrapperObj = (DataObject*) dvmAllocObject(wrapperClass, ALLOC_DEFAULT);
+    if (wrapperObj == NULL)
+        return NULL;
+    dataPtr = (s4*) wrapperObj->instanceData;
+
+    /* assumes value is stored in first instance field */
+    /* (see dvmValidateBoxClasses) */
+    if (typeIndex == PRIM_LONG || typeIndex == PRIM_DOUBLE)
+        *(s8*)dataPtr = value.j;
+    else
+        *dataPtr = value.i;
+
+    return wrapperObj;
+}
+
+/*
+ * Unwrap a primitive data type, if necessary.
+ *
+ * If "returnType" is not primitive, we just tuck "value" into JValue and
+ * return it after verifying that it's the right type of object.
+ *
+ * Fails if the field is primitive and "value" is either not a boxed
+ * primitive or is of a type that cannot be converted.
+ *
+ * Returns "true" on success, "false" on failure.
+ */
+bool dvmUnboxPrimitive(Object* value, ClassObject* returnType,
+    JValue* pResult)
+{
+    PrimitiveType typeIndex = returnType->primitiveType;
+    PrimitiveType valueIndex;
+
+    if (typeIndex == PRIM_NOT) {
+        if (value != NULL && !dvmInstanceof(value->clazz, returnType)) {
+            ALOGD("wrong object type: %s %s",
+                value->clazz->descriptor, returnType->descriptor);
+            return false;
+        }
+        pResult->l = value;
+        return true;
+    } else if (typeIndex == PRIM_VOID) {
+        /* can't put anything into a void */
+        return false;
+    }
+
+    valueIndex = getBoxedType((DataObject*)value);
+    if (valueIndex == PRIM_NOT)
+        return false;
+
+    /* assumes value is stored in first instance field of "value" */
+    /* (see dvmValidateBoxClasses) */
+    if (dvmConvertPrimitiveValue(valueIndex, typeIndex,
+            (s4*) ((DataObject*)value)->instanceData, (s4*)pResult) < 0)
+    {
+        ALOGV("Prim conversion failed");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Find the return type in the signature, and convert it to a class
+ * object.  For primitive types we use a boxed class, for reference types
+ * we do a name lookup.
+ *
+ * On failure, we return NULL with an exception raised.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth)
+{
+    const char* sig = dexProtoGetReturnType(&meth->prototype);
+
+    switch (*sig) {
+    case 'Z':
+    case 'C':
+    case 'F':
+    case 'D':
+    case 'B':
+    case 'S':
+    case 'I':
+    case 'J':
+    case 'V':
+        return dvmFindPrimitiveClass(*sig);
+    case '[':
+    case 'L':
+        return dvmFindClass(sig, meth->clazz->classLoader);
+    default: {
+        /* should not have passed verification */
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        ALOGE("Bad return type in signature '%s'", desc);
+        free(desc);
+        dvmThrowInternalError(NULL);
+        return NULL;
+    }
+    }
+}
+
+
+/*
+ * JNI reflection support: convert reflection object to Field ptr.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj)
+{
+    ClassObject* clazz;
+    int slot;
+
+    assert(obj->clazz == gDvm.classJavaLangReflectField);
+    clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectField_declClass);
+    slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot);
+
+    /* must initialize the class before returning a field ID */
+    if (!dvmInitClass(clazz))
+        return NULL;
+
+    return dvmSlotToField(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert reflection object to Method ptr.
+ */
+Method* dvmGetMethodFromReflectObj(Object* obj)
+{
+    ClassObject* clazz;
+    int slot;
+
+    if (obj->clazz == gDvm.classJavaLangReflectConstructor) {
+        clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectConstructor_declClass);
+        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot);
+    } else if (obj->clazz == gDvm.classJavaLangReflectMethod) {
+        clazz = (ClassObject*)dvmGetFieldObject(obj,
+                                gDvm.offJavaLangReflectMethod_declClass);
+        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot);
+    } else {
+        assert(false);
+        return NULL;
+    }
+
+    /* must initialize the class before returning a method ID */
+    if (!dvmInitClass(clazz))
+        return NULL;
+
+    return dvmSlotToMethod(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert Field to reflection object.
+ *
+ * The return value is a java.lang.reflect.Field.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field)
+{
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    /* caller must dvmReleaseTrackedAlloc(result) */
+    return createFieldObject(field, clazz);
+}
+
+/*
+ * JNI reflection support: convert Method to reflection object.
+ *
+ * The returned object will be either a java.lang.reflect.Method or
+ * .Constructor, depending on whether "method" is a constructor.
+ *
+ * This is also used for certain "system" annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method)
+{
+    UNUSED_PARAMETER(clazz);
+
+    if (strcmp(method->name, "<init>") == 0) {
+        if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+            dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+        return createConstructorObject(method);
+    } else {
+        if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+            dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+        return dvmCreateReflectMethodObject(method);
+    }
+}
diff --git a/vm/reflect/Reflect.h b/vm/reflect/Reflect.h
new file mode 100644
index 0000000..21bb08d
--- /dev/null
+++ b/vm/reflect/Reflect.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 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 reflection calls and utility functions.
+ */
+#ifndef DALVIK_REFLECT_REFLECT_H_
+#define DALVIK_REFLECT_REFLECT_H_
+
+/*
+ * During startup, validate the "box" classes, e.g. java/lang/Integer.
+ */
+bool dvmValidateBoxClasses();
+
+/*
+ * Get all fields declared by a class.
+ *
+ * Includes both class and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get the named field.
+ */
+Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj);
+
+/*
+ * Get all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false.  It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get the named method.
+ */
+Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz,
+    StringObject* nameObj, ArrayObject* args);
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz);
+
+/*
+ * Convert slot numbers back to objects.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot);
+Method* dvmSlotToMethod(ClassObject* clazz, int slot);
+
+/*
+ * Convert a primitive value, performing a widening conversion if necessary.
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+    PrimitiveType dstType, const s4* srcPtr, s4* dstPtr);
+
+/*
+ * Convert the argument to the specified type.
+ *
+ * Returns the width of the argument (1 for most types, 2 for J/D, -1 on
+ * error).
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* ins);
+
+/*
+ * Box a primitive value into an object.  If "returnType" is
+ * not primitive, this just returns "value" cast to an object.
+ */
+DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType);
+
+/*
+ * Unwrap a boxed primitive.  If "returnType" is not primitive, this just
+ * returns "value" cast into a JValue.
+ */
+bool dvmUnboxPrimitive(Object* value, ClassObject* returnType,
+    JValue* pResult);
+
+/*
+ * Return the class object that matches the method's signature.  For
+ * primitive types, returns the box class.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth);
+
+/*
+ * JNI reflection support.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj);
+Method* dvmGetMethodFromReflectObj(Object* obj);
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field);
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method);
+
+/*
+ * Quick test to determine if the method in question is a reflection call.
+ * Used for some stack parsing.  Currently defined as "the method's declaring
+ * class is java.lang.reflect.Method".
+ */
+INLINE bool dvmIsReflectionMethod(const Method* method)
+{
+    return (method->clazz == gDvm.classJavaLangReflectMethod);
+}
+
+/*
+ * Proxy class generation.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+    Object* loader);
+
+/*
+ * Create a new java.lang.reflect.Method object based on "meth".
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth);
+
+/*
+ * Return an array of Annotation objects for the specified piece.  For method
+ * parameters this is an array of arrays of Annotation objects.
+ *
+ * Method also applies to Constructor.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz);
+ArrayObject* dvmGetMethodAnnotations(const Method* method);
+ArrayObject* dvmGetFieldAnnotations(const Field* field);
+ArrayObject* dvmGetParameterAnnotations(const Method* method);
+
+/*
+ * Return the annotation if it exists.
+ */
+Object* dvmGetClassAnnotation(const ClassObject* clazz, const ClassObject* annotationClazz);
+Object* dvmGetMethodAnnotation(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz);
+Object* dvmGetFieldAnnotation(const ClassObject* clazz, const Field* method,
+        const ClassObject* annotationClazz);
+
+/*
+ * Return true if the annotation exists.
+ */
+bool dvmIsClassAnnotationPresent(const ClassObject* clazz, const ClassObject* annotationClazz);
+bool dvmIsMethodAnnotationPresent(const ClassObject* clazz, const Method* method,
+        const ClassObject* annotationClazz);
+bool dvmIsFieldAnnotationPresent(const ClassObject* clazz, const Field* method,
+        const ClassObject* annotationClazz);
+
+/*
+ * Find the default value for an annotation member.
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method);
+
+/*
+ * Get the list of thrown exceptions for a method.  Returns NULL if there
+ * are no exceptions listed.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method);
+
+/*
+ * Get the Signature annotation.
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz);
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method);
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field);
+
+/*
+ * Get the EnclosingMethod attribute from an annotation.  Returns a Method
+ * object, or NULL.
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz);
+
+/*
+ * Return clazz's declaring class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz);
+
+/*
+ * Return clazz's enclosing class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz);
+
+/*
+ * Get the EnclosingClass attribute from an annotation.  If found, returns
+ * "true".  A String with the original name of the class and the original
+ * access flags are returned through the arguments.  (The name will be NULL
+ * for an anonymous inner class.)
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+    int* pAccessFlags);
+
+/*
+ * Get an array of class objects from the MemberClasses annotation.  Returns
+ * NULL if none found.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz);
+
+/*
+ * Used to pass values out of annotation (and encoded array) processing
+ * functions.
+ */
+struct AnnotationValue {
+    JValue  value;
+    u1      type;
+};
+
+
+/**
+ * Iterator structure for iterating over DexEncodedArray instances. The
+ * structure should be treated as opaque.
+ */
+struct EncodedArrayIterator {
+    const u1* cursor;                    /* current cursor */
+    u4 elementsLeft;                     /* number of elements left to read */
+    const DexEncodedArray* encodedArray; /* instance being iterated over */
+    u4 size;                             /* number of elements in instance */
+    const ClassObject* clazz;            /* class to resolve with respect to */
+};
+
+/**
+ * Initializes an encoded array iterator.
+ *
+ * @param iterator iterator to initialize
+ * @param encodedArray encoded array to iterate over
+ * @param clazz class to use when resolving strings and types
+ */
+void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
+        const DexEncodedArray* encodedArray, const ClassObject* clazz);
+
+/**
+ * Returns whether there are more elements to be read.
+ */
+bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator);
+
+/**
+ * Returns the next decoded value from the iterator, advancing its
+ * cursor. This returns primitive values in their corresponding union
+ * slots, and returns everything else (including nulls) as object
+ * references in the "l" union slot.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on any returned reference.
+ *
+ * @param value pointer to store decoded value into
+ * @returns true if a value was decoded and the cursor advanced; false if
+ * the last value had already been decoded or if there was a problem decoding
+ */
+bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
+        AnnotationValue* value);
+
+#endif  // DALVIK_REFLECT_REFLECT_H_
diff --git a/vm/test/Test.h b/vm/test/Test.h
new file mode 100644
index 0000000..e5acdfd
--- /dev/null
+++ b/vm/test/Test.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Internal unit tests.
+ */
+#ifndef DALVIK_TEST_TEST_H_
+#define DALVIK_TEST_TEST_H_
+
+bool dvmTestHash(void);
+bool dvmTestAtomicSpeed(void);
+bool dvmTestIndirectRefTable(void);
+
+#endif  // DALVIK_TEST_TEST_H_
diff --git a/vm/test/TestHash.cpp b/vm/test/TestHash.cpp
new file mode 100644
index 0000000..52e421b
--- /dev/null
+++ b/vm/test/TestHash.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Test the hash table functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#ifndef NDEBUG
+
+#define kNumTestEntries 14
+
+/*
+ * Test foreach.
+ */
+static int printFunc(void* data, void* arg)
+{
+    //printf("  '%s'\n", (const char*) data);
+    // (should verify strings)
+
+    int* count = (int*) arg;
+    (*count)++;
+    return 0;
+}
+static void dumpForeach(HashTable* pTab)
+{
+    int count = 0;
+
+    //printf("Print from foreach:\n");
+    dvmHashForeach(pTab, printFunc, &count);
+    if (count != kNumTestEntries) {
+        ALOGE("TestHash foreach test failed");
+        assert(false);
+    }
+}
+
+/*
+ * Test iterator.
+ */
+static void dumpIterator(HashTable* pTab)
+{
+    int count = 0;
+
+    //printf("Print from iterator:\n");
+    HashIter iter;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        //const char* str = (const char*) dvmHashIterData(&iter);
+        //printf("  '%s'\n", str);
+        // (should verify strings)
+        count++;
+    }
+    if (count != kNumTestEntries) {
+        ALOGE("TestHash iterator test failed");
+        assert(false);
+    }
+}
+
+/*
+ * Some quick hash table tests.
+ */
+bool dvmTestHash()
+{
+    HashTable* pTab;
+    char tmpStr[64];
+    const char* str;
+    u4 hash;
+    int i;
+
+    ALOGV("TestHash BEGIN");
+
+    pTab = dvmHashTableCreate(dvmHashSize(12), free);
+    if (pTab == NULL)
+        return false;
+
+    dvmHashTableLock(pTab);
+
+    /* add some entries */
+    for (i = 0; i < kNumTestEntries; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        hash = dvmComputeUtf8Hash(tmpStr);
+        dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+            (HashCompareFunc) strcmp, true);
+    }
+
+    dvmHashTableUnlock(pTab);
+
+    /* make sure we can find all entries */
+    for (i = 0; i < kNumTestEntries; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        hash = dvmComputeUtf8Hash(tmpStr);
+        str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+                (HashCompareFunc) strcmp, false);
+        if (str == NULL) {
+            ALOGE("TestHash: failure: could not find '%s'", tmpStr);
+            /* return false */
+        }
+    }
+
+    /* make sure it behaves correctly when entry not found and !doAdd */
+    sprintf(tmpStr, "entry %d", 17);
+    hash = dvmComputeUtf8Hash(tmpStr);
+    str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+            (HashCompareFunc) strcmp, false);
+    if (str == NULL) {
+        /* good */
+    } else {
+        ALOGE("TestHash found nonexistent string (improper add?)");
+    }
+
+    dumpForeach(pTab);
+    dumpIterator(pTab);
+
+    /* make sure they all get freed */
+    dvmHashTableFree(pTab);
+
+
+    /*
+     * Round 2: verify probing & tombstones.
+     */
+    pTab = dvmHashTableCreate(dvmHashSize(2), free);
+    if (pTab == NULL)
+        return false;
+
+    hash = 0;
+
+    /* two entries, same hash, different values */
+    const char* str1;
+    str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"),
+            (HashCompareFunc) strcmp, true);
+    assert(str1 != NULL);
+    str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"),
+            (HashCompareFunc) strcmp, true);
+
+    /* remove the first one */
+    if (!dvmHashTableRemove(pTab, hash, (void*)str1))
+        ALOGE("TestHash failed to delete item");
+    else
+        free((void*)str1);     // "Remove" doesn't call the free func
+
+    /* make sure iterator doesn't included deleted entries */
+    int count = 0;
+    HashIter iter;
+    for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+        dvmHashIterNext(&iter))
+    {
+        count++;
+    }
+    if (count != 1) {
+        ALOGE("TestHash wrong number of entries (%d)", count);
+    }
+
+    /* see if we can find them */
+    str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"one",
+            (HashCompareFunc) strcmp,false);
+    if (str != NULL)
+        ALOGE("TestHash deleted entry has returned!");
+    str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"two",
+            (HashCompareFunc) strcmp,false);
+    if (str == NULL)
+        ALOGE("TestHash entry vanished");
+
+    /* force a table realloc to exercise tombstone removal */
+    for (i = 0; i < 20; i++) {
+        sprintf(tmpStr, "entry %d", i);
+        str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+                (HashCompareFunc) strcmp, true);
+        assert(str != NULL);
+    }
+
+    dvmHashTableFree(pTab);
+    ALOGV("TestHash END");
+
+    return true;
+}
+
+#endif /*NDEBUG*/
diff --git a/vm/test/TestIndirectRefTable.cpp b/vm/test/TestIndirectRefTable.cpp
new file mode 100644
index 0000000..f144254
--- /dev/null
+++ b/vm/test/TestIndirectRefTable.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Test the indirect reference table implementation.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+#ifndef NDEBUG
+
+#define DBUG_MSG    ALOGI
+
+class Stopwatch {
+public:
+    Stopwatch() {
+        reset();
+    }
+
+    void reset() {
+        start_ = now();
+    }
+
+    float elapsedSeconds() {
+        return (now() - start_) * 0.000001f;
+    }
+
+private:
+    u8 start_;
+
+    static u8 now() {
+#ifdef HAVE_POSIX_CLOCKS
+        struct timespec tm;
+        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+        return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+#else
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return tv.tv_sec * 1000000LL + tv.tv_usec;
+#endif
+    }
+};
+
+/*
+ * Basic add/get/delete tests in an unsegmented table.
+ */
+static bool basicTest()
+{
+    static const int kTableMax = 20;
+    IndirectRefTable irt;
+    IndirectRef iref0, iref1, iref2, iref3;
+    IndirectRef manyRefs[kTableMax];
+    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    const u4 cookie = IRT_FIRST_SEGMENT;
+    bool result = false;
+
+    if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) {
+        return false;
+    }
+
+    iref0 = (IndirectRef) 0x11110;
+    if (irt.remove(cookie, iref0)) {
+        ALOGE("unexpectedly successful removal");
+        goto bail;
+    }
+
+    /*
+     * Add three, check, remove in the order in which they were added.
+     */
+    DBUG_MSG("+++ START fifo\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add1 failed");
+        goto bail;
+    }
+
+    if (irt.get(iref0) != obj0 ||
+            irt.get(iref1) != obj1 ||
+            irt.get(iref2) != obj2) {
+        ALOGE("objects don't match expected values %p %p %p vs. %p %p %p",
+                irt.get(iref0), irt.get(iref1), irt.get(iref2),
+                obj0, obj1, obj2);
+        goto bail;
+    } else {
+        DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
+    }
+
+    if (!irt.remove(cookie, iref0) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref2))
+    {
+        ALOGE("fifo deletion failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("fifo del not empty");
+        goto bail;
+    }
+
+    /* get invalid entry (off the end of the list) */
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
+        ALOGE("stale entry get succeeded unexpectedly");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove in the opposite order.
+     */
+    DBUG_MSG("+++ START lifo\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add2 failed");
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref2) ||
+            !irt.remove(cookie, iref1) ||
+            !irt.remove(cookie, iref0))
+    {
+        ALOGE("lifo deletion failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("lifo del not empty");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove middle / middle / bottom / top.  (Second attempt
+     * to remove middle should fail.)
+     */
+    DBUG_MSG("+++ START unorder\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        ALOGE("trivial add3 failed");
+        goto bail;
+    }
+
+    if (irt.capacity() != 3) {
+        ALOGE("expected 3 entries, found %d", irt.capacity());
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) {
+        ALOGE("unorder deletion1 failed");
+        goto bail;
+    }
+
+    /* get invalid entry (from hole) */
+    if (irt.get(iref1) != kInvalidIndirectRefObject) {
+        ALOGE("hole get succeeded unexpectedly");
+        goto bail;
+    }
+
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
+        ALOGE("unorder deletion2 failed");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (irt.capacity() != 0) {
+        ALOGE("unorder del not empty");
+        goto bail;
+    }
+
+    /*
+     * Add four entries.  Remove #1, add new entry, verify that table size
+     * is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
+     * that we delete one and don't hole-compact the other.
+     */
+    DBUG_MSG("+++ START hole fill\n");
+    iref0 = irt.add(cookie, obj0);
+    iref1 = irt.add(cookie, obj1);
+    iref2 = irt.add(cookie, obj2);
+    iref3 = irt.add(cookie, obj3);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
+        ALOGE("trivial add4 failed");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("remove 1 of 4 failed");
+        goto bail;
+    }
+    iref1 = irt.add(cookie, obj1);
+    if (irt.capacity() != 4) {
+        ALOGE("hole not filled");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) {
+        ALOGE("remove 1/3 failed");
+        goto bail;
+    }
+    if (irt.capacity() != 3) {
+        ALOGE("should be 3 after two deletions");
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
+        ALOGE("remove 2/0 failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("not empty after split remove");
+        goto bail;
+    }
+
+    /*
+     * Add an entry, remove it, add a new entry, and try to use the original
+     * iref.  They have the same slot number but are for different objects.
+     * With the extended checks in place, this should fail.
+     */
+    DBUG_MSG("+++ START switched\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj1);
+    if (irt.remove(cookie, iref0)) {
+        ALOGE("mismatched del succeeded (%p vs %p)", iref0, iref1);
+        goto bail;
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("switched del failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("switching del not empty");
+        goto bail;
+    }
+
+    /*
+     * Same as above, but with the same object.  A more rigorous checker
+     * (e.g. with slot serialization) will catch this.
+     */
+    DBUG_MSG("+++ START switched same object\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    iref1 = irt.add(cookie, obj0);
+    if (iref0 != iref1) {
+        /* try 0, should not work */
+        if (irt.remove(cookie, iref0)) {
+            ALOGE("temporal del succeeded (%p vs %p)", iref0, iref1);
+            goto bail;
+        }
+    }
+    if (!irt.remove(cookie, iref1)) {
+        ALOGE("temporal cleanup failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("temporal del not empty");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ START null lookup\n");
+    if (irt.get(NULL) != kInvalidIndirectRefObject) {
+        ALOGE("null lookup succeeded");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ START stale lookup\n");
+    iref0 = irt.add(cookie, obj0);
+    irt.remove(cookie, iref0);
+    if (irt.get(iref0) != kInvalidIndirectRefObject) {
+        ALOGE("stale lookup succeeded");
+        goto bail;
+    }
+
+    /*
+     * Test table overflow.
+     */
+    DBUG_MSG("+++ START overflow\n");
+    int i;
+    for (i = 0; i < kTableMax; i++) {
+        manyRefs[i] = irt.add(cookie, obj0);
+        if (manyRefs[i] == NULL) {
+            ALOGE("Failed adding %d of %d", i, kTableMax);
+            goto bail;
+        }
+    }
+    if (irt.add(cookie, obj0) != NULL) {
+        ALOGE("Table overflow succeeded");
+        goto bail;
+    }
+    if (irt.capacity() != (size_t)kTableMax) {
+        ALOGE("Expected %d entries, found %d", kTableMax, irt.capacity());
+        goto bail;
+    }
+    irt.dump("table with 20 entries, all filled");
+    for (i = 0; i < kTableMax-1; i++) {
+        if (!irt.remove(cookie, manyRefs[i])) {
+            ALOGE("multi-remove failed at %d", i);
+            goto bail;
+        }
+    }
+    irt.dump("table with 20 entries, 19 of them holes");
+    /* because of removal order, should have 20 entries, 19 of them holes */
+    if (irt.capacity() != (size_t)kTableMax) {
+        ALOGE("Expected %d entries (with holes), found %d",
+                kTableMax, irt.capacity());
+        goto bail;
+    }
+    if (!irt.remove(cookie, manyRefs[kTableMax-1])) {
+        ALOGE("multi-remove final failed");
+        goto bail;
+    }
+    if (irt.capacity() != 0) {
+        ALOGE("multi-del not empty");
+        goto bail;
+    }
+
+    /* Done */
+    DBUG_MSG("+++ basic test complete\n");
+    result = true;
+
+bail:
+    irt.destroy();
+    return result;
+}
+
+static bool performanceTest()
+{
+    static const int kTableMax = 100;
+    IndirectRefTable irt;
+    IndirectRef manyRefs[kTableMax];
+    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    const u4 cookie = IRT_FIRST_SEGMENT;
+    const int kLoops = 100000;
+    Stopwatch stopwatch;
+
+    DBUG_MSG("+++ START performance\n");
+
+    if (!irt.init(kTableMax, kTableMax, kIndirectKindGlobal)) {
+        return false;
+    }
+
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            manyRefs[i] = irt.add(cookie, obj0);
+        }
+        for (int i = 0; i < kTableMax; i++) {
+            irt.remove(cookie, manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Add/remove %d objects FIFO order, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops);
+
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            manyRefs[i] = irt.add(cookie, obj0);
+        }
+        for (int i = kTableMax; i-- > 0; ) {
+            irt.remove(cookie, manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Add/remove %d objects LIFO order, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
+
+    for (int i = 0; i < kTableMax; i++) {
+        manyRefs[i] = irt.add(cookie, obj0);
+    }
+    stopwatch.reset();
+    for (int loop = 0; loop < kLoops; loop++) {
+        for (int i = 0; i < kTableMax; i++) {
+            irt.get(manyRefs[i]);
+        }
+    }
+    DBUG_MSG("Get %d objects, %d iterations, %0.3fms / iteration",
+            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
+    for (int i = kTableMax; i-- > 0; ) {
+        irt.remove(cookie, manyRefs[i]);
+    }
+
+    irt.destroy();
+    return true;
+}
+
+/*
+ * Some quick tests.
+ */
+bool dvmTestIndirectRefTable()
+{
+    if (!basicTest()) {
+        ALOGE("IRT basic test failed");
+        return false;
+    }
+
+    if (!performanceTest()) {
+        ALOGE("IRT performance test failed");
+        return false;
+    }
+
+    return true;
+}
+
+#endif /*NDEBUG*/